Skip to content

Components / Sections

Case Stack

Case studies as a deck: on a wide screen each card holds at the top of the screen while the next slides up over it, and the one underneath shrinks and darkens a little on a scroll-driven timeline; on a phone they follow one another. Each card paints a colour block: its number and field, its name (the card's link), a line, the work it took as chips and a "View case" cue, beside a picture or a drawing in a second block (Motif). Zero JavaScript.

Live demo

scroll inside

Sample work

Selected work

All work
  1. 03 / 04Beach club

    Sunset, every evening

    A month of stories and one reel a week for a beach club.

    • Stories
    • Reels
    Reel
  2. 04 / 04Spa

    A quiet feed for a spa

    Slow pictures, short captions, and bookings by message.

    • Photo
    • Captions
    Story

Live, from the library itself, at full size: scroll, hover, navigate away and back. The demo is never gated.

Variants · each row writes the attribute the prop writes; the section's own CSS answers

density

Measured

JavaScript of its own

0 B

CSS 18.0 kB raw including the base tokens. Measured from a production build, and again in CI for every change that can move it.

Browser support

Limited availability

Chrome 115 · not Firefox · Safari 26; needs scroll-timeline, has. Elsewhere: the cards still stack (sticky) without shrinking or darkening.

Install Pro

The demo above is never gated; the source is. With a key in components.json (see account), the registry serves it like any other item:

terminal
npx shadcn@latest add @moonarc-pro/case-stack
src/pages/index.astro
---
import CaseStack from '@/components/moonarc/pro/CaseStack.astro';
---
<CaseStack
  eyebrow="Work"
  more={{ href: '/work/', label: 'All work' }}
  cases={[
    { href: '/work/one/', title: 'A menu that sold out', text: 'A new menu shot in a day and posted for a month.', meta: 'Restaurant', tags: ['Photo', 'Social'], motif: 'plate' },
    { href: '/work/two/', title: 'Rooms booked by June', text: 'A villa\'s rooms, filmed for the season.', meta: 'Villa', tags: ['Video', 'Ads'], motif: 'gate' },
  ]}
>
  <span slot="headline">Selected <em>work</em></span>
</CaseStack>

Without a key the registry answers 401 with an upgrade message. Everything this section composes is free: motif.

Props

PropTypeDefaultDescription
cases{ href, title, text?, meta?, tags?, block?, art?, motif?, caption?, image? }[]noneThe cases, in order. block is the card's colour (the five in turn when unset), art the drawing's (the next block when unset); image ({ src, alt, srcset?, sizes? }) replaces the drawing.
eyebrowstringnoneA short line above the heading.
headlinestringnoneThe heading. Slot "headline" wins and may hold an <em>.
more{ href: string; label: string }noneA link beside the heading, e.g. to all the work.
cuestring'View case'The cue at the foot of each card, beside the round arrow (hidden from a screen reader: the name is the link).
tagsLabelstring'Services'Accessible name of each card's chips.
level2 | 32Heading level of the section's heading; the cases' names are one below.
density'compact' | 'cozy' | 'roomy''cozy'Block padding ×0.8 / ×1 / ×1.25 through --ma-density.

Theming and overrides

The section reads the theme contract (shadcn's variable names with the base tokens as fallback), so a pasted theme or one --ma-brand recolours it. Its class names are the override surface and stay stable; a named slot replaces the matching prop with your own markup.

Classes

.ma-casestack.ma-casestack__arrow.ma-casestack__body.ma-casestack__card.ma-casestack__cue.ma-casestack__eyebrow.ma-casestack__head.ma-casestack__headline.ma-casestack__item.ma-casestack__link.ma-casestack__list.ma-casestack__media.ma-casestack__meta.ma-casestack__more.ma-casestack__summary.ma-casestack__tags.ma-casestack__text.ma-casestack__title

Slots

slot="eyebrow"slot="headline"

Reduced motion

The cards still hold and stack; none shrinks or darkens, the arrow and the picture stay put.

With ClientRouter

CSS only (a view timeline); nothing to rebind.

Why it is built this way

Replaces: Sticky card stacks (GSAP ScrollTrigger) · Stacking cards (Framer templates). See the migration table.

Cards stick to the top as you scroll and each one shrinks and dims as the next slides over it, as on Apple product pages.

Chrome 116 · not Firefox · Safari 26scroll

What is on offer, as colour blocks: each card paints one of the palette's blocks and carries a drawing in its corner (Motif), an index, a name, a line, chips for what it includes and a foot with a price and a round arrow.

newly · Chrome 105 · Firefox 121 · Safari 15.4hover

Also: view transitions · how costs are measured · browser support · accessibility policy · five-minute setup · MCP for agents · this page as Markdown