Detent web components
By Shane Logsdon

Media & effects

Sticky Pages

Implemented

Static sticky full-page stacking scroll sections with clean physical shadows and natural release

detent-sticky-pages

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

01. Architected for speed

Slotted page — consumer markup and consumer styles.

02. Modern aesthetic system

Each page pins under the previous one.

03. Production grade components

The last page releases back into natural scrolling.

4.5 KB gzip for the class module

Variants

Slot composition — the pages in the main example are real light-DOM children of detent-sticky-pages. They are never moved, cloned, clipped or trapped: scroll the page and each one pins under the previous one with its own surface, then the last page releases into normal flow. Reduced motion keeps the exact same layout with the shadow transition removed.

Data composition — the same stacking from a pages property: badge, counter, title and description are laid out by the component (documentation demo copy only). The third page overrides the surface with its own themed bg token.

Recorded states

  • reduced motion recorded state; no setter

Usage

Import

import '/components/sticky-pages/define.js';

// or register it yourself
import { DetentStickyPages } from '/components/sticky-pages/sticky-pages.js';

Attributes

  • bg
  • border
  • page-height
  • rounded
  • shadow
  • stack-offset
  • top-offset

Properties

  • pages

Slots

  • default

Events

No events are recorded for this component.

Example

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

<detent-sticky-pages
  bg
  border
  page-height="100vh"
  rounded
  shadow
  stack-offset="0"
  top-offset="0"
></detent-sticky-pages>

Property-only values

const element = document.querySelector('detent-sticky-pages');
if (element !== null) {
  // pages: StickyPageItem[]
  element.pages = [];
}

Source

The pinned upstream source for this row:

  • src/components/ui/StickyPages.tsx sha256 8e547c91eda5…

Native files

The native module and its artifacts:

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

API

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

Attributes

Attributes
AttributePropertyTypeDefaultNotes
bg———Recorded in the catalog as an attribute.
borderborderbooleantruePresence semantics: present means true, absent means false.
page-heightpageHeightstring'100vh'Kebab-case attribute; camelCase property.
roundedroundedbooleantruePresence semantics: present means true, absent means false.
shadowshadowbooleantruePresence semantics: present means true, absent means false.
stack-offsetstackOffsetnumber0Kebab-case attribute; camelCase property.
top-offsettopOffsetnumber0Kebab-case attribute; camelCase property.

Properties

Properties
PropertyTypeDefaultNotes
pagesStickyPageItem[]—Property-only: assign from script; not accepted as an attribute.

Events

No custom events are recorded for this component.

Slots

Slots
SlotNotes
defaultConsumer content; rendered through a slot, never re-created.

CSS customisation

  • React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
  • `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • index and total are StickyPageCardProps internals derived from composition order and length, not host API; the row lists only the geometry attributes and pages.
  • pages[].content has no native data equivalent; rich pages are supplied through the default slot, which keeps the consumer DOM, listeners and focus.
  • rounded, shadow and border are declared but never read in the pinned source; they are implemented here because the row records them as API.
  • data-active, data-active-index and the covering page elevated shadow are native additions derived from the pinned index.
  • Slotted pages receive --detent-sticky-pages-index and --detent-sticky-pages-active so any number of light-DOM children get per-index sticky geometry without being moved or cloned.
  • StickyPage is a thin wrapper with no native need and StickyStack/StackingPages are pure aliases; one tag and one class per PC-2.2/PC-13.1.
  • Page borders use #626b7a for the 3:1 non-text contrast.

What it does

Full-page sections that pin under native position: sticky and stack over one another as the document scrolls, then release back into normal flow. Zero tilting, zero rotation, zero transform distortion, zero scroll hijacking.

Upstream behaviour
  • Pure static stacking: zero tilting, zero rotation, zero transform distortion
  • Native browser scrolling: zero scroll hijacking or wheel locking, built on native CSS position: sticky
  • Stacking depth: each page slides up directly over the previous one with elevated top shadows and rounded borders
  • Natural release: once the final page finishes stacking, the container seamlessly transitions back into normal document flow
  • Dual composition support: pass a structured pages data array or arbitrary <StickyPage> child components
  • Configurable top offset, stack offset, page height, rounded corners, and drop shadows
  • Full prefers-reduced-motion compliance maintaining standard clean sticky reading
Differences from upstream
  • React `children`/`ReactNode` props become DOM slots; caller content stays light DOM.
  • `className` prop is replaced by documented CSS parts/custom properties; no Tailwind in consumers.
  • React state/hooks replaced by element-owned state with pre-upgrade property preservation.
  • index and total are StickyPageCardProps internals derived from composition order and length, not host API; the row lists only the geometry attributes and pages.
  • pages[].content has no native data equivalent; rich pages are supplied through the default slot, which keeps the consumer DOM, listeners and focus.
  • rounded, shadow and border are declared but never read in the pinned source; they are implemented here because the row records them as API.
  • data-active, data-active-index and the covering page elevated shadow are native additions derived from the pinned index.
  • Slotted pages receive --detent-sticky-pages-index and --detent-sticky-pages-active so any number of light-DOM children get per-index sticky geometry without being moved or cloned.
  • StickyPage is a thin wrapper with no native need and StickyStack/StackingPages are pure aliases; one tag and one class per PC-2.2/PC-13.1.
  • Page borders use #626b7a for the 3:1 non-text contrast.
Previous: Stepper Next: Story Cards

Docs

Components

Project