Detent web components
By Shane Logsdon

Media & effects

Stacked Cards

Implemented

Scroll-driven sticky stacking cards

detent-stacked-cards

Port status
Catalog status
implemented
Origin
upstream
Phase
4
Wave
phase-4-cinematic
Group
Media & effects

4.8 KB gzip for the class module

Variants

A local documentation deck: the three cards above are sample copy assigned as the cards property, with no images and no network request. Scroll the page so the section passes the viewport: each card pins 16px lower than the previous one and scales down by 0.075 per covering card, on native position: sticky and native window scrolling. With prefers-reduced-motion: reduce the scale is removed entirely and the cards simply stack.

Recorded states

  • light theme recorded state; no setter
  • reduced motion recorded state; no setter

Usage

Import

import '/components/stacked-cards/define.js';

// or register it yourself
import { DetentStackedCards } from '/components/stacked-cards/stacked-cards.js';

Attributes

  • subtitle
  • title

Properties

  • cards

Slots

No slots are recorded for this component.

Events

No events are recorded for this component.

Example

Runnable example: site/examples/stacked-cards.ts. The snippet below is generated from the catalogued attributes.

<detent-stacked-cards
  subtitle
  title
></detent-stacked-cards>

Property-only values

const element = document.querySelector('detent-stacked-cards');
if (element !== null) {
  // cards: StackedCard[]
  element.cards = [];
}

Source

The pinned upstream source for this row:

  • src/components/ui/StackedCards.tsx sha256 ec863b52fba4…

Native files

The native module and its artifacts:

  • src/components/stacked-cards/stacked-cards.ts
  • src/components/stacked-cards/styles.css
  • src/components/stacked-cards/define.ts
  • docs/components/stacked-cards.md
  • site/examples/stacked-cards.ts
  • tests/browser/stacked-cards.spec.ts
  • evidence/components/stacked-cards.md

API

Boolean attributes follow HTML presence semantics: present means true, absent means false, and disabled="false" still means disabled.

Attributes

Attributes
AttributePropertyTypeDefaultNotes
subtitlesubtitlestringundefinedKebab-case attribute; camelCase property.
titletitlestringundefinedKebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
cardsStackedCard[]defaultCardsProperty-only: assign from script; not accepted as an attribute.

Events

No custom events are recorded for this component.

Slots

No slots are recorded for this component.

CSS customisation

  • `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • index, total, card and progress are private CardItemProps in the pinned source; the host derives them from the cards array and the section scroll, so the row lists only title/subtitle attributes and cards as a property.
  • The default demo dataset keeps the pinned copy but drops the four remote Unsplash image URLs; a component must not perform third-party network requests, and image/imageAlt stay consumer-supplied under the R-3 scheme allow-list.
  • A card without an allowed image URL renders the documented media gradient placeholder because there is no local upstream asset to ship.
  • Card borders use #626b7a/#6b7280 instead of the upstream hairline for the 3:1 non-text ratio (PC-12.6).

What it does

A column of story cards that stays aligned in the initial view and stacks on scroll: every card is a sticky wrapper around an article that scales down as the next card covers it.

Upstream behaviour
  • Naturally aligned column layout in the initial view
  • Scroll-driven sticky stacking where cards stack one above another on scroll
  • Non-blocking native window scrolling — continues to subsequent sections seamlessly
  • Responsive two-column grid on desktop, single-column on mobile
  • Dark and light mode compatible with high-contrast typography and borders
  • Hardware accelerated motion transitions with subtle depth shadow
  • Respects prefers-reduced-motion preferences
Differences from upstream
  • `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
  • Framer Motion springs/AnimatePresence ported to CSS transitions, WAAPI or the shared rAF spring helper.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • index, total, card and progress are private CardItemProps in the pinned source; the host derives them from the cards array and the section scroll, so the row lists only title/subtitle attributes and cards as a property.
  • The default demo dataset keeps the pinned copy but drops the four remote Unsplash image URLs; a component must not perform third-party network requests, and image/imageAlt stay consumer-supplied under the R-3 scheme allow-list.
  • A card without an allowed image URL renders the documented media gradient placeholder because there is no local upstream asset to ship.
  • Card borders use #626b7a/#6b7280 instead of the upstream hairline for the 3:1 non-text ratio (PC-12.6).
Previous: Unfold Panel Next: Stat Card

Docs

Components

Project