Démos

Positionnement par ancre CSS interactif

01

HTML

index.html
<form class="controls"> <fieldset> <legend>Inline</legend> <label> <input type="radio" name="position-inline" value="left" checked /> Left </label> <label> <input type="radio" name="position-inline" value="center" /> Center </label> <label> <input type="radio" name="position-inline" value="right" /> Right </label> </fieldset> <fieldset> <legend>Block</legend> <label> <input type="radio" name="position-block" value="top" checked /> Top </label> <label> <input type="radio" name="position-block" value="center" /> Center </label> <label> <input type="radio" name="position-block" value="bottom" /> Bottom </label> </fieldset> </form> <div> <button class="open anchor" type="button">Anchor</button> <article class="anchor-positioned top-left" style="--position-inline: right; --position-block: bottom;"> top-left* </article> <article class="anchor-positioned top-right" style="--position-inline: left; --position-block: bottom;"> top-right* </article> <article class="anchor-positioned bottom-right" style="--position-inline: left; --position-block: top;"> bottom-right* </article> <article class="anchor-positioned bottom-left" style="--position-inline: right; --position-block: top;"> bottom-left* </article> </div> <p class="note">* Position du point d’ancrage</p>
02

CSS

style.css
@layer demo { .anchor { anchor-name: --myAnchor; } .anchor-positioned { position: absolute; position-anchor: --myAnchor; &.top-left { top: anchor(var(--position-block)); left: anchor(var(--position-inline)); } &.top-right { top: anchor(var(--position-block)); right: anchor(var(--position-inline)); } &.bottom-right { bottom: anchor(var(--position-block)); right: anchor(var(--position-inline)); } &.bottom-left { bottom: anchor(var(--position-block)); left: anchor(var(--position-inline)); } } } @layer demoStyles { body { flex-direction: column; } .open { width: 300px; height: 300px; padding: 1rem; background: orange; border: 1px dashed white; } .anchor-positioned { position: relative; width: fit-content; padding: 1rem; background-color: rgb(0 255 255 / 80%); border: 1px dashed white; &::after { content: ''; position: absolute; width: 6px; height: 6px; background-color: red; border-radius: 100vmax; } &.top-left::after { top: 0; left: 0; transform: translate(-50%, -50%); } &.top-right::after { top: 0; right: 0; transform: translate(50%, -50%); } &.bottom-right::after { right: 0; bottom: 0; transform: translate(50%, 50%); } &.bottom-left::after { bottom: 0; left: 0; transform: translate(-50%, 50%); } } .controls { position: absolute; top: 0; right: 0; display: grid; gap: 1rem; padding: 1rem; background-color: white; fieldset { display: grid; gap: 0.25rem; border: 0; } legend { font-weight: 700; } } .note { color: red; } }
03

JavaScript

script.js
const controls = document.querySelector('.controls'); const anchorsPositioned = document.querySelectorAll('.anchor-positioned'); controls.addEventListener('change', () => { const inline = controls.querySelector('[name="position-inline"]:checked').value; const block = controls.querySelector('[name="position-block"]:checked').value; anchorsPositioned.forEach(anchorPositioned => { anchorPositioned.style.setProperty('--position-inline', inline); anchorPositioned.style.setProperty('--position-block', block); }); });