Anchored Auto Layout
npm

Hover or focus a box to measure it. Click its tag, or press ] and [, to pin one. Esc unpins.

    Legend
    • Anchor: a box with data-⚓️-name
    • Placed by anchors: out of flow
    • Gap from the anchor's edge, in px
    • Edges of the box you're inspecting

    Every tag, line and number here is itself placed by data-⚓️-* attributes. JS only measures.

    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.

    anchor
    abovebelowbeforeaftertop + leadingbottom + trailing

    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-from grows 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-⚓️-tween lets below ↔ above animate instead of snap.
    • data-⚓️-fill covers 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.

    Copied