Skip to content

Instantly share code, notes, and snippets.

@karol-majewski
Last active December 4, 2025 02:39
Show Gist options
  • Select an option

  • Save karol-majewski/08d8bab714837e21941d71ad9ef5ac18 to your computer and use it in GitHub Desktop.

Select an option

Save karol-majewski/08d8bab714837e21941d71ad9ef5ac18 to your computer and use it in GitHub Desktop.
Blossom Carousel layout using captions and images of different width preserving their intrinsic aspect ratio
{/* Fixed width gallery */}
<div className="content-width">
<p className="text-[17px]/[23px] text-pretty">
Images have fixed width. On smaller screens they get smaller too.
Scroll snapping is enabled. Scrolling horizontally is somewhat
forced by using `data-lenis-prevent` on the page container
</p>
</div>
<div className="content-width-full" data-lenis-prevent>
<BlossomCarousel className="carousel my-6 md:my-12">
<div className="slide max-w-[450px]">
<figure className="relative">
<Image
src={workstationVertical}
alt="Karol Majewski's workstation"
width={3447}
height={4434}
placeholder="blur"
quality={70}
className="w-full object-cover"
/>
<figcaption className="relative w-[80%] pt-3 pb-3">
<div className="relative flex w-full flex-col items-start text-[13px]/[16px] text-(--color-almost-black) before:absolute before:top-0 before:left-0 before:-ml-[0.5em] before:content-['\201C']">
<span>
Research work always involves cycles of chaos and order.
Before I commit to a rabbit hole, my workstation is neat
and empty. Beginner&apos;s mind and all that. As I
explore, things get increasingly messy and the place
starts to look somewhat schizo. Eventually, clarity
emerges and it&apos;s time to tidy up again. I&apos;m
not going to show you the messy part.&rdquo; ↗
</span>
</div>
</figcaption>
</figure>
</div>
<div className="slide max-w-[450px]">
<figure className="relative">
<Image
src={pi}
alt="Pi Max"
width={3447}
height={1080}
placeholder="blur"
quality={70}
className="h-full rounded-xs object-contain"
/>
<figcaption className="relative w-[80%] pt-3 pb-3">
<div className="relative flex w-full flex-col items-start text-[13px]/[16px] text-(--color-almost-black)">
<span>
In this frame from <em>Pi</em> (1998), protagonist Max
Cohen navigates the chaotic interior of his Chinatown
apartment—a claustrophobic nerve center packed with
obsolete computer equipment, tangled cables, and
monitors glowing with mathematical patterns. The
black-and-white cinematography amplifies the paranoid
intensity of a mind consumed by numerical obsession,
where the boundary between breakthrough and breakdown
grows increasingly unstable. Good research feels like
this.
</span>
</div>
</figcaption>
</figure>
</div>
<div className="slide max-w-[450px]">
<figure className="relative">
<Image
src={maxCohen}
alt="Pi Max"
width={600}
height={400}
placeholder="blur"
quality={70}
// Removed w-auto to make image fill to 450px width
className="h-full rounded-xs object-contain"
/>
<figcaption className="relative w-[80%] pt-3 pb-3">
<div className="relative flex w-full flex-col items-start text-[13px]/[16px] text-(--color-almost-black)">
<span>
Max Cohen hunches over his makeshift supercomputer
Euclid, bathed in the harsh glow of multiple monitors
displaying his quest to decode the numerical pattern
underlying the stock market—and perhaps reality itself.
The композиция places the human figure at the center of
a technological labyrinth, suggesting both mastery and
entrapment. Aronofsky’s camera finds poetry in the
detritus of obsolete computing: the exposed circuitry,
the tangles of coaxial cable, the anthropomorphic
quality of the machine that seems to breathe and think
alongside its creator.
</span>
</div>
</figcaption>
</figure>
</div>
</BlossomCarousel>
</div>
{/* Fixed height gallery */}
<div className="content-width">
<p className="text-[17px]/[23px] text-pretty">
Images have fixed height. On smaller screens they keep that height
unless you changed the desired height via the `--carousel-height`
CSS variable. Scroll snapping is disabled. Scrolling horizontally
is somewhat forced by using `data-lenis-prevent` on the page
container.
</p>
</div>
<div className="content-width-full" data-lenis-prevent>
<BlossomCarousel
className="my-12 grid! snap-none! auto-cols-max grid-flow-col gap-[1.3rem] px-[max(var(--width-side-gutter),calc((100vw-var(--width-content))/2))]"
style={
{
"--carousel-height": "477px",
"--carousel-offset":
"max(var(--width-side-gutter), calc((100vw - var(--width-content)) / 2))",
} as React.CSSProperties
}
>
<div className="snap-start scroll-mx-(--carousel-offset)">
<figure className="inline-grid w-fit grid-cols-[min-content] grid-rows-[auto_min-content]">
<Image
src="https://placehold.co/351x477/png"
alt="Placeholder"
width={351}
height={477}
className="col-start-1 row-start-1 h-(--carousel-height) w-max"
/>
<figcaption className="col-start-1 row-start-2 min-w-0 pt-4">
<div className="text-[13px]/[16px] text-pretty wrap-break-word whitespace-normal text-(--color-almost-black)">
This is a &lt;figcaption&gt; and it can have variable
height. Short captions will fit on a single line, but
multi-line captions should be allowed just as well.
</div>
</figcaption>
</figure>
</div>
<div className="snap-start scroll-mx-(--carousel-offset)">
<figure className="inline-grid w-fit grid-cols-[min-content] grid-rows-[auto_min-content]">
<Image
src="https://placehold.co/621x477/png"
alt="Placeholder"
width={621}
height={477}
className="col-start-1 row-start-1 h-(--carousel-height) w-max"
/>
<figcaption className="col-start-1 row-start-2 min-w-0 pt-4">
<div className="text-[13px]/[16px] text-pretty wrap-break-word whitespace-normal text-(--color-almost-black)">
This is a &lt;figcaption&gt; and it can have variable
height. Short captions will fit on a single line, but
multi-line captions should be allowed just as well.
</div>
</figcaption>
</figure>
</div>
<div className="snap-start scroll-mx-(--carousel-offset)">
<figure className="inline-grid w-fit grid-cols-[min-content] grid-rows-[auto_min-content]">
<Image
src="https://placehold.co/282x477/png"
alt="Placeholder"
width={282}
height={477}
className="col-start-1 row-start-1 h-(--carousel-height) w-max"
/>
<figcaption className="col-start-1 row-start-2 min-w-0 pt-4">
<div className="text-[13px]/[16px] text-pretty wrap-break-word whitespace-normal text-(--color-almost-black)">
This is a &lt;figcaption&gt;.
</div>
</figcaption>
</figure>
</div>
</BlossomCarousel>
</div>

Implement a Blossom Carousel containing a row of slides arranged using CSS Grid. The gallery contains 3 images and each has different dimensions. However, they must all have the same height and each should have their width be resulting from height and intrinsic aspect ratio.

Carousel height is pre-given (477px) and should be defined using a CSS Custom Property called --carousel-height

All images render using their intrinsic aspect ratio, there is no clipping, no cropping

Height is defined by the CSS Custom property therefore image width is automatic

All images are rendered using the same height (--carousel height)

Figure caption element’ width is equal to the width of the image it’s describing

Image gallery lives inside a Blossom Carousel and is always exactly one row; items never wrap

On smaller screens, images don’t get compressed horizontally but maintain their width as dictated by --carousel height and image’s intrinsic aspect ratio.

Use CSS Grid

Using CSS Subgrid is allowed

Consult Blossom Carousel documentation page https://blossom-carousel.com/docs/examples#css-grid

Implement images using placeholders, e.g. “Placeholder”

Ignore older carousel implementations found elsewhere in the codebase

Import carousel from “src/components/carousel.tsx”

Prefer using Tailwind CSS over creating separate CSS files


@karol-majewski
Copy link
Author

Considerations:

  • When used in conjunction with Lenis, add data-lenis-prevent to the container holding the carousel. This will prevent nested containers and using trackpads to scroll carousels horizontally will work just as expected — that is, the carousel detects horizontal scrolling and prevents vertical movement unless the gesture is clearly vertical. That’s how it works on the Collins website.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment