Case Study Carousel
Horizontal case-study carousel. Carousel Desktop (content + image card) is used on /, /work, and /about. Carousel Mobile (image-dominant card with title overlay and success-metric sticker) renders below the lg breakpoint on those surfaces and in the home hero's right column.
Carousel Desktop
Default layout (variant="full"). 960px-wide cards with content on the left and image on the right. Shown with mock data.
Carousel Mobile
Compact image-dominated cards (variant="hero"). Used as the mobile layout for the case-studies module and inside the home-page hero's right column. Keeps the same rotating border animation on the active card.
Loading skeleton — desktop
Shimmer state that mirrors the desktop carousel's active card. Used by WorkCarousel as a fade-in overlay during category filter transitions so the carousel never flash-swaps.
Loading skeleton — mobile
Mirrors the mobile hero card — aspect-square shimmer plus dots and arrow-button placeholders below.
Filter-aware wrapper (WorkCarousel)
WorkCarousel is the client wrapper used on /work. It reads the ?category= URL param (written by CategoryFilters), filters the case studies client-side, and fades the skeleton overlay in for 500ms during the transition so new cards never flash-replace old ones. If a filter produces zero matches, it renders a centered empty-state message instead.
View full version
Props
caseStudies
CaseStudy[] (required)Array of case study data from Sanity CMS.
variant
"full" | "hero" (default "full")"full" is the responsive Case Studies module: Carousel Mobile below lg, Carousel Desktop at lg+. "hero" forces Carousel Mobile at all widths — used inside the home-page hero, wrapped in a lg:block parent so it only shows on desktop.
title
string (default: "Case Studies", ignored when forced to hero)Section heading. Set to empty string to hide. Hero-forced renders never draw a title.
subtitle
string (optional)Description text below heading. Full variant only.
showViewAll
boolean (default false)Full variant only. Renders a "View all case studies" button below the carousel.
Card styling
.card-carousel-item
Gradient from accent-subtle to surface, subtle border. Shared by both variants..card-carousel-active
Rotating conic-gradient border animation applied only to the active (centered) card in both variants.Shimmer primitive
Every shimmer bar in the skeleton is a local `ShimmerBar` — a rounded div with `bg-foreground/10 animate-pulse`. Colors adapt to light / dark theme via CSS variables.
function ShimmerBar({ className }: { className?: string }) {
return (
<div
className={cn(
'rounded-sm bg-foreground/10 animate-pulse',
className
)}
/>
)
}Usage
import { CaseStudyCarousel } from '@/components/sections/CaseStudyCarousel'
import { CaseStudyGridSkeleton } from '@/components/sections/CaseStudyGridSkeleton'
// Full carousel on /work and /home
<CaseStudyCarousel caseStudies={caseStudies} showViewAll subtitle="…" />
// Compact hero variant — wrap in an lg:block container
<div className="hidden w-full lg:block lg:max-w-[540px]">
<CaseStudyCarousel caseStudies={caseStudies} variant="hero" />
</div>
// Loading skeleton (auto-picks variant by viewport)
<CaseStudyGridSkeleton />Import
import { CaseStudyCarousel } from '@/components/sections/CaseStudyCarousel'
import { CaseStudyGridSkeleton } from '@/components/sections/CaseStudyGridSkeleton'
import { WorkCarousel } from '@/components/sections/WorkCarousel'Related files
src/components/sections/CaseStudyCarousel.tsxsrc/components/sections/CaseStudyGridSkeleton.tsxsrc/components/sections/WorkCarousel.tsxsanity/schemas/caseStudy.tssrc/lib/queries.ts