Concepts
Ten ideas. After them, the attribute list reads like a sentence.
Anchors and targets
An AnchorAny element with data-⚓️-name. has a name. A TargetAn element with data-⚓️-to, or an edge attribute with a --name value. points at one. The vocabulary comes from iOS Auto Layout,11 One-way: a target follows its anchor, never the reverse. written as attributes.
<button data-⚓️-name="--menu-btn">Menu</button>
<nav popover data-⚓️-to="--menu-btn" data-⚓️-below data-⚓️-align-trailing>…</nav>Outside, inside, across
Four outside edges, four inside alignments, two stretches. All logical: flip the direction.
data-⚓️-side is the four outside edges as one value, for when the side is state.
The containing block
Targets are absolutely positioned. data-⚓️-root gives them a positioned ancestor, and the pins (data-⚓️-pin-top, -pin-x…) attach to its edges with no anchor at all. Out of flow means a target doesn't push its siblings, and its parent doesn't grow to fit it.
Size from the anchor
(anchor × multiplier) + offset, with both as custom properties.
<aside data-⚓️-to="--panel" data-⚓️-after data-⚓️-sync-width
style="--⚓️-mult-x: .5; --⚓️-offset-x: -16px">…</aside>The top layer
Add popover to a target: it renders above every z-index and stays on its anchor. The library clears the browser's popover centring with a zero-specificity reset, so your classes still win.
When it doesn't fit
data-⚓️-try="flip-y" uses the other side when the preferred one would overflow; flip-x and flip-all too. data-⚓️-visibility hides a target whose anchor scrolled away.
Motion
data-⚓️-morph-fromgrows an element out of its anchor on entry. The phone menu on this site does.data-⚓️-transition="morph"or"glide"animates geometry. The header underline glides.data-⚓️-tweenlets below ↔ above animate instead of snap.data-⚓️-fillcovers the anchor with an explicit size, so tiles ride a reflow.
Under prefers-reduced-motion, geometry snaps. Nothing to configure.
Repeating components
Names are global, so five cards make five --card anchors and every badge finds the last. data-⚓️-scope-name="--card" fences a name to its subtree; data-⚓️-scope="all" fences all of them. See it.
Anchored container queries
Every data-⚓️-to target is an anchored container, so its insides can react to the fallback the browser chose.
@container anchored(fallback: flip-block) {
.tooltip-arrow { rotate: 180deg; }
}Side effect: a target is a containing block for its position: fixed descendants.
Whole pages
Page shells work too. On this site, <main> is the only region in flow: the header pins to the top, the sidebar hangs before main and stretches to its height, the footer hangs below. Press Constraints in the header to see it.