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.

    Chat sides

    Chat bubbles placed with data-⚓️-side="before|after" around dots on a centre spine.

    Full page
    <main class="app">
      <header class="bar">
        <div class="intro">
          <h1>Chat sides</h1>
          <p>
            Each bubble is anchored to its own dot on the centre spine with
            <code>data-⚓️-side="before"</code> (them) or <code>"after"</code> (you).
            <em>Swap seats</em> flips one value per bubble and <code>data-⚓️-tween</code>
            swings it across. New messages grow out of the spine (<code>data-⚓️-morph-from</code>).
          </p>
        </div>
        <div class="controls">
          <button id="swap" type="button" aria-pressed="false">Swap seats</button>
          <label class="xray"><input type="checkbox" id="xray"> X-ray</label>
        </div>
      </header>
    
      <section class="chat" id="chat" aria-label="Conversation between Riley and Sam">
        <div class="names" aria-hidden="true">
          <span class="name them" id="name-before">Riley</span>
          <span class="name me" id="name-after">Sam (you)</span>
        </div>
    
        <ol class="log" id="log" data-⚓️-root aria-live="polite">
          <li class="row" data-from="riley" data-msg="did you see the anchor positioning thing" data-⚓️-scope-name="--node">
            <span class="node" data-⚓️-name="--node"></span>
            <p class="bubble" data-⚓️-to="--node" data-⚓️-side="before" data-⚓️-align-top data-⚓️-tween data-⚓️-transition="morph"><span class="who">Riley: </span>did you see the anchor positioning thing</p>
          </li>
          <li class="row" data-from="sam" data-msg="the one where a bubble just says which side it lives on?" data-⚓️-scope-name="--node">
            <span class="node" data-⚓️-name="--node"></span>
            <p class="bubble" data-⚓️-to="--node" data-⚓️-side="after" data-⚓️-align-top data-⚓️-tween data-⚓️-transition="morph"><span class="who">Sam: </span>the one where a bubble just says which side it lives on?</p>
          </li>
          <li class="row" data-from="riley" data-msg="yes. no flex-direction, no margins, no JS maths" data-⚓️-scope-name="--node">
            <span class="node" data-⚓️-name="--node"></span>
            <p class="bubble" data-⚓️-to="--node" data-⚓️-side="before" data-⚓️-align-top data-⚓️-tween data-⚓️-transition="morph"><span class="who">Riley: </span>yes. no flex-direction, no margins, no JS maths</p>
          </li>
    
          <!-- The slot after the last message. The typing indicator is anchored here,
               so it glides down as messages arrive. -->
          <li class="row slot" aria-hidden="true">
            <span class="node" data-⚓️-name="--next"></span>
          </li>
    
          <li class="typing" id="typing" data-state="idle" aria-hidden="true"
              data-⚓️-to="--next" data-⚓️-side="after" data-⚓️-align-top data-⚓️-tween data-⚓️-transition="morph">
            <span></span><span></span><span></span>
          </li>
        </ol>
      </section>
    
      <form class="composer" id="composer">
        <label class="visually-hidden" for="msg">Message</label>
        <input id="msg" autocomplete="off" placeholder="Say something as Sam…" maxlength="120">
        <button type="submit">Send</button>
      </form>
    </main>

    data-⚓️-align-topdata-⚓️-morph-fromdata-⚓️-namedata-⚓️-rootdata-⚓️-scope-namedata-⚓️-sidedata-⚓️-todata-⚓️-transitiondata-⚓️-tween

    Copied