{label} line {i + 1} — scroll to pin the header.
+ {/each} +
+ A position: sticky wrapper that KNOWS when it is pinned. An IntersectionObserver
+ sentinel (never a scroll listener + getBoundingClientRect — that forces a reflow)
+ flips data-stuck the moment the box sticks, so a header can change elevation.
+ data-stuck
+ + data-edge mirror the platform
+ @container scroll-state(stuck: top|bottom) — this is its cross-browser polyfill.
+ The recipe positions; you decorate [data-sticky][data-stuck].
+
+ Scroll the panel above to pin the header. offset is the inset (px) from the pin
+ edge — it feeds BOTH the CSS inset and the observer geometry, so the sentinel's trip line
+ lands exactly on the pin line. root here is the scroll panel (the scroll-host); for
+ a page header it defaults to the viewport.
+
{eidosSnippet}
+
+ Foundation knobs applied to the stage. RTL flips the accent-agnostic geometry (the recipe
+ uses logical inset-block-start / inset-block-end, so top/bottom
+ pinning is writing-mode correct).
+
+ Sticky declares no semantic events: pinning is a fact of layout/scroll, not a perceptual
+ occurrence the user commits. data-stuck is a styling signal (a frame late — IO
+ is async), never a commit. AntD's onChange(affixed) maps to a reserved
+ change-stuck event for a future version (README Gaps); declaring it now would flip
+ the component to interactive for an event that fires nothing in v1.
+
+ Sticky consumes adom (the ActiveDom): all observation goes through
+ uix.dom.observeIntersection(...) — the sanctioned, iframe/popup-correct IO
+ wrapper that returns a disconnect cleanup — never a raw IntersectionObserver
+ or a scroll listener. langs supplies only the catalog name («{uix.langs.ts(
+ '#?components.sticky.label|Sticky'
+ )}»). No format / announce / clipboard.
+
| Prop | Type | Notes |
|---|---|---|
| offset | +number (px) | +
+ Inset from the pin edge. Feeds both the CSS inset and the observer
+ rootMargin — must be a resolvable number, not a CSS string. Default
+ 0.
+ |
+
| edge | +top | bottom | +Which viewport edge the box pins to. Default top. |
+
| root | +Element | Document | null | +
+ The scroll-host (nearest scrolling ancestor). null = viewport. Pass the scroll
+ container when the box lives inside one, or detection silently never fires.
+ |
+
| disabled | +boolean | +Suspend observation (stays unstuck). Default false. |
+
| ref | +HTMLElement | null | +Bindable ref to the sticky box. | +
+ Emits data-stuck (present while pinned) and
+ data-edge (top | bottom) on the box; an internal
+ data-sticky-sentinel (aria-hidden) does the observing. Style the pinned state
+ via [data-sticky][data-stuck].
+
| Field | Value |
|---|---|
| name | {stickyMorfo.name} |
| kebab | {stickyMorfo.kebab} |
| scope | {stickyMorfo.scope.join(', ')} |
| parts | {partsList.length} |
| events | {events.length} |
| kebab | marker | element | archetype | optional | +
|---|---|---|---|---|
| {part.kebab} | +[{part.marker}] |
+ <{part.defaultElement}> | +{part.archetype ?? '—'} | +{part.optional ? 'yes' : 'no'} | +
+ The provider box carries data-edge (prop-driven) + data-stuck
+ (state-driven present/absent). The sentinel carries data-edge (to pick which
+ margin side the recipe collapses) + aria-hidden. The offset rides an eidos-only
+ --_sticky-offset custom-property (A8 data), never visual CSS from the provider.
+
+ Recipe lives in src/uix/eidos/components/sticky/sticky.css. It POSITIONS only:
+ position: sticky + the logical inset (inset-block-start /
+ inset-block-end from --_sticky-offset) + the
+ --sticky-z-index token + the layout-neutral sentinel. The pinned TREATMENT is
+ yours: [data-sticky][data-stuck].
+
| Selector | Owner | Purpose |
|---|---|---|
[data-sticky] |
+ morfo | +position: sticky + z-index token. |
+
[data-sticky][data-edge='top'|'bottom'] |
+ morfo | +The logical inset from --_sticky-offset. |
+
[data-sticky-sentinel] |
+ morfo | +Real-height, layout-neutral (negative margin), invisible, inert probe. | +
| Concern | Contract |
|---|---|
| Role | +
+ None — a layout affix, not a widget. Membership is met by complex behavior (apg: none). The consumer's own content (a <header>, a
+ <nav>) carries whatever landmark it needs.
+ |
+
| Sentinel | +
+ aria-hidden="true" + pointer-events: none +
+ visibility: hidden — an inert probe, invisible to AT and to the pointer,
+ occupying zero net layout.
+ |
+
| data-stuck timing | ++ Flips a frame late (IO is async) — a styling signal only. Never gate focus or layout + math on it. + | +
| Reduced motion | +
+ No motion of its own. Any pin transition is the consumer's and must honor
+ prefers-reduced-motion.
+ |
+