/// Utilities
Affix
Pins its children to the viewport with fixed positioning in a portal, with an optional scroll threshold to reveal it.
import { Affix } from "@protocore/pds";Basics
Affix renders its children position: fixed relative to the viewport, in a portal at the document root. Set position with viewport offsets — left/right resolve to logical insets, so it mirrors correctly under RTL.
Toggle a viewport-pinned notice. It renders fixed to the bottom-end corner of the window, in a portal, above page content.
Back to top
Pass scrollThreshold to reveal the affix only after the page has scrolled a given distance — the classic back-to-top button. Below the threshold it renders nothing.
Scroll the page down: once you pass 300px the back-to-top control fades in, pinned to the bottom-end corner of the viewport. It portals out of this demo frame.
When to use it
Use Affix for viewport-anchored UI that must float above the page and survive scrolling — a back-to-top button, a floating action button, a persistent cookie or status notice. It portals to the document root so it escapes any overflow or transform ancestor that would otherwise clip a fixed element, and sits at the sticky z-index. The optional scrollThreshold reveals it only once the reader has scrolled past a point. For content that should scroll *with* a column but stick within it, use native position: sticky instead — Affix is specifically for viewport-fixed elements.
Usage
Do
- Use Affix for viewport-fixed floating UI (back-to-top, FAB, persistent notice).
- Set a scrollThreshold for reveal-on-scroll affordances so they don't clutter the top of the page.
- Give the affixed control a clear aria-label — it's detached from surrounding context.
Don't
- Use Affix for content that should stick within a scrolling column — that's position: sticky.
- Stack many affixes at the same corner; they'll overlap and trap focus.
- Assume left/right are physical — they are logical insets and flip under RTL.
Accessibility
- Affix is a portaled container; the interactive control inside it must be a real button/link with its own accessible name.
- Because it is visually detached, give affixed controls an explicit label (e.g. aria-label="Back to top").
- Its appearance fades in with a short animation that honors prefers-reduced-motion.
Affix props
| Prop | Type | Default | Description |
|---|---|---|---|
className | string | — | |
position | AffixPosition | { bottom: 20, right: 20 } | Viewport offsets. |
scrollThreshold | number | — | Only show the affix once the window has scrolled at least this many pixels vertically. Omit to always show it. |
style | CSSProperties | — | |
target | HTMLElement | null | document.body | Portal target. |
zIndex | string | number | var(--pds-z-sticky) | Override the stacking order. |