Detent web components
By Shane Logsdon

Media & effects

Story Cards

Implemented

Responsive interactive story cards carousel

detent-story-card

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

7.0 KB gzip for the class module

Variants

A local documentation carousel: the four stories are sample copy assigned as the cards property, with no images and no network request. Use the Previous / Next controls, click a preview card, scroll the track with a wheel or trackpad (or drag it horizontally), or focus a control and press the arrow keys. The instance below has auto-play on: hovering it holds the story until you move away. Neither it nor the main example emits a custom event, and with prefers-reduced-motion: reduce the transitions and the autoplay are removed while every story stays reachable.

Recorded states

  • hover recorded state; no setter
  • paused recorded state; no setter
  • dragging recorded state; no setter
  • reduced motion recorded state; no setter
  • active recorded state; no setter

Usage

Import

import '/components/story-card/define.js';

// or register it yourself
import { DetentStoryCards } from '/components/story-card/story-card.js';

Attributes

  • auto-play
  • auto-play-interval
  • enable-wheel-scroll
  • initial-index
  • loop
  • show-navigation
  • 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/story-card.ts. The snippet below is generated from the catalogued attributes.

<detent-story-card
  auto-play
  auto-play-interval="5000"
  enable-wheel-scroll
  initial-index="0"
  loop
  show-navigation
  subtitle="Discover the latest stories."
  title="What\'s happening"
></detent-story-card>

Property-only values

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

Source

The pinned upstream source for this row:

  • src/components/ui/StoryCard.tsx sha256 0de1c5150e5a…

Native files

The native module and its artifacts:

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

API

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

Attributes

Attributes
AttributePropertyTypeDefaultNotes
auto-playautoPlaybooleanfalsePresence semantics: present means true, absent means false.
auto-play-intervalautoPlayIntervalnumber5000Kebab-case attribute; camelCase property.
enable-wheel-scrollenableWheelScrollbooleantruePresence semantics: present means true, absent means false.
initial-indexinitialIndexnumber0Kebab-case attribute; camelCase property.
looploopbooleantruePresence semantics: present means true, absent means false.
show-navigationshowNavigationbooleantruePresence semantics: present means true, absent means false.
subtitlesubtitlestring"Discover the latest stories."Kebab-case attribute; camelCase property.
titletitlestring"What\'s happening"Kebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
cardsStoryCard[]undefinedProperty-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.
  • The pinned touchstart/touchend swipe becomes a Pointer Events drag with setPointerCapture at the same 40 px threshold, which is what makes the recorded dragging state observable; a click after a drag is suppressed so one gesture is one step.
  • Arrow-key navigation is handled while focus is inside the component instead of on window, so several instances do not hijack arrow-key scrolling (PC-12.6).
  • Preview cards are native buttons with aria-label Story: <title> and the active card is a labelled group with aria-current, instead of div role=button with re-implemented keyboard handling (PC-12.1).
  • AnimatePresence per-child enter/exit becomes slot width and opacity transitions; the active content swap is immediate (motion fidelity stays an unresolved visual gate).
  • initial-index is validated to 0..cards.length-1 with one warning and re-applied when the attribute changes.
  • title/subtitle stay attributes; title is also the global HTMLElement.title attribute, so the heading and tooltip share it and no alias attribute is invented.
  • The default demo dataset keeps the pinned copy but drops the four remote Unsplash image URLs (no network).
  • Borders use #626b7a instead of the upstream hairline for 3:1 non-text contrast; the summary block renders the active description without a second CTA link.

What it does

A responsive story carousel: one expanded feature card, compact preview cards that navigate on click, infinite looping, wheel/trackpad and drag navigation, previous/next controls, arrow-key navigation and optional autoplay with hover pause.

Upstream behaviour
  • Infinite loop carousel with continuous smooth transitions
  • Responsive horizontal story card layout
  • Large featured card with compact preview cards
  • Mouse wheel and trackpad horizontal/vertical scroll support
  • Touch swipe and drag gesture navigation
  • Smooth spring-powered layout animations
  • Previous and next navigation controls
  • Clickable story previews for quick navigation
  • Optional autoplay with configurable interval and hover pause
  • Keyboard navigation with arrow keys
  • Hardware accelerated animations
  • Respects reduced-motion preferences
  • Customizable appearance with standard Tailwind utility classes
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.
  • The pinned touchstart/touchend swipe becomes a Pointer Events drag with setPointerCapture at the same 40 px threshold, which is what makes the recorded dragging state observable; a click after a drag is suppressed so one gesture is one step.
  • Arrow-key navigation is handled while focus is inside the component instead of on window, so several instances do not hijack arrow-key scrolling (PC-12.6).
  • Preview cards are native buttons with aria-label Story: <title> and the active card is a labelled group with aria-current, instead of div role=button with re-implemented keyboard handling (PC-12.1).
  • AnimatePresence per-child enter/exit becomes slot width and opacity transitions; the active content swap is immediate (motion fidelity stays an unresolved visual gate).
  • initial-index is validated to 0..cards.length-1 with one warning and re-applied when the attribute changes.
  • title/subtitle stay attributes; title is also the global HTMLElement.title attribute, so the heading and tooltip share it and no alias attribute is invented.
  • The default demo dataset keeps the pinned copy but drops the four remote Unsplash image URLs (no network).
  • Borders use #626b7a instead of the upstream hairline for 3:1 non-text contrast; the summary block renders the active description without a second CTA link.
Previous: Sticky Pages Next: StretchSwitch

Docs

Components

Project