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.

    RTL mirror: logical anchor sides

    A chat UI whose tooltips, timestamps, reaction badge, presence dot, hint and menu are all placed with logical data-⚓️-* sides (before/after/align-trailing).

    Full page
    <main class="demo">
      <header class="intro">
        <h1>Mirror it</h1>
        <p>
          Every tooltip, timestamp, badge, hint and menu here is placed with logical
          <code>data-⚓️-*</code> sides: <code>before</code>, <code>after</code>,
          <code>align-trailing</code>. Flip <code>dir</code> on <code>&lt;html&gt;</code> and the whole UI mirrors
          with zero RTL CSS; the button only changes that one attribute.
        </p>
      </header>
    
      <div class="bar">
        <button class="flip" type="button" aria-pressed="false">
          <span class="flip-icon" aria-hidden="true">⇄</span>
          <code class="flip-label" dir="ltr">dir="<span class="flip-value">ltr</span>"</code>
        </button>
        <label class="xray-toggle"><input type="checkbox" id="xray"> X-ray</label>
      </div>
    
      <div class="app" data-⚓️-root>
        <nav class="rail" aria-label="Sections">
          <ul>
            <li data-⚓️-scope-name="--nav">
              <a href="#" data-⚓️-name="--nav" aria-label="Inbox">
                <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 13h4l2 3h4l2-3h4M4 13l2-7h12l2 7v5H4z"/></svg>
              </a>
              <span class="tip" data-⚓️-to="--nav" data-⚓️-side="after" data-⚓️-align-y="center" aria-hidden="true">Inbox</span>
            </li>
            <li data-⚓️-scope-name="--nav">
              <a href="#" data-⚓️-name="--nav" aria-label="Messages" aria-current="page">
                <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 5h16v11H9l-5 4z"/></svg>
              </a>
              <span class="tip" data-⚓️-to="--nav" data-⚓️-side="after" data-⚓️-align-y="center" aria-hidden="true">Messages</span>
            </li>
            <li data-⚓️-scope-name="--nav">
              <a href="#" data-⚓️-name="--nav" aria-label="Calendar">
                <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 6h16v14H4zM4 10h16M8 3v5M16 3v5"/></svg>
              </a>
              <span class="tip" data-⚓️-to="--nav" data-⚓️-side="after" data-⚓️-align-y="center" aria-hidden="true">Calendar</span>
            </li>
            <li data-⚓️-scope-name="--nav">
              <a href="#" data-⚓️-name="--nav" aria-label="Settings">
                <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="3"/><path d="M12 2v3M12 19v3M2 12h3M19 12h3M5 5l2 2M17 17l2 2M5 19l2-2M17 7l2-2"/></svg>
              </a>
              <span class="tip" data-⚓️-to="--nav" data-⚓️-side="after" data-⚓️-align-y="center" aria-hidden="true">Settings</span>
            </li>
          </ul>
        </nav>
    
        <section class="chat" aria-label="Conversation with Noor">
          <header class="chat-head">
            <span class="avatar" data-⚓️-name="--avatar" aria-hidden="true">N</span>
            <span class="online" data-⚓️-to="--avatar" data-⚓️-align-trailing data-⚓️-align-bottom aria-hidden="true"></span>
            <span class="who"><b>Noor</b><small>online</small></span>
            <button class="more" type="button" popovertarget="menu" data-⚓️-name="--more" aria-label="Conversation options">⋯</button>
          </header>
    
          <ol class="thread">
            <li class="in" data-⚓️-scope-name="--msg">
              <p class="bubble" data-⚓️-name="--msg">Did the anchors survive the RTL build?</p>
              <time class="stamp" data-⚓️-to="--msg" data-⚓️-side="after" data-⚓️-align-bottom>9:41</time>
            </li>
            <li class="out" data-⚓️-scope-name="--msg">
              <p class="bubble" data-⚓️-name="--msg">Every one. They're all logical sides.</p>
              <time class="stamp" data-⚓️-to="--msg" data-⚓️-side="before" data-⚓️-align-bottom>9:42</time>
              <span class="react" data-⚓️-to="--msg" data-⚓️-side="above" data-⚓️-align-trailing aria-label="Noor reacted">🔥</span>
            </li>
            <li class="in" data-⚓️-scope-name="--msg">
              <p class="bubble" data-⚓️-name="--msg">No translate hacks?</p>
              <time class="stamp" data-⚓️-to="--msg" data-⚓️-side="after" data-⚓️-align-bottom>9:42</time>
            </li>
            <li class="out" data-⚓️-scope-name="--msg">
              <p class="bubble" data-⚓️-name="--msg">Not one. Hit the button.</p>
              <time class="stamp" data-⚓️-to="--msg" data-⚓️-side="before" data-⚓️-align-bottom>9:43</time>
            </li>
          </ol>
    
          <form class="composer">
            <input type="text" aria-label="Message" placeholder="Write a message">
            <button type="submit" data-⚓️-name="--send" aria-label="Send">
              <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h14M13 6l6 6-6 6"/></svg>
            </button>
            <span class="hint" data-⚓️-to="--send" data-⚓️-side="above" data-⚓️-align-trailing aria-hidden="true">Enter ↵ to send</span>
          </form>
        </section>
    
        <div id="menu" class="menu" popover="manual" data-⚓️-to="--more" data-⚓️-side="below" data-⚓️-align-trailing>
          <button type="button">Mute</button>
          <button type="button">Pin chat</button>
          <button type="button">Search</button>
        </div>
      </div>
    </main>

    data-⚓️-align-bottomdata-⚓️-align-trailingdata-⚓️-align-ydata-⚓️-namedata-⚓️-rootdata-⚓️-scope-namedata-⚓️-sidedata-⚓️-to

    Copied