[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"playground-css-anchor-positioning":32},[4,20],{"id":5,"slug":6,"name":7,"tagline":8,"description":9,"accentFrom":10,"accentTo":11,"icon":12,"defaultLocale":13,"locales":14,"features":16,"position":19},"019fe637-3d33-714b-b57f-23e163ffca0c","dev","Web Development","Build. Learn. Ship.","Practical courses, engineering-grade articles and open-source tools for people who ship.","violet-500","cyan-400","◇","en",[13,15],"fa",{"courses":17,"paths":17,"articles":17,"exams":18,"flashcards":18,"packages":17,"community":17,"certificates":17,"teams":17,"commerce":17},true,false,0,{"id":21,"slug":22,"name":23,"tagline":24,"description":25,"accentFrom":26,"accentTo":10,"icon":27,"defaultLocale":13,"locales":28,"features":30,"position":31},"019fe637-3dc2-754c-8657-0f175bfee7c6","lang","Languages","Learn a language the way you learn a codebase.","Structured paths, listening drills and spaced repetition that actually sticks.","amber-400","⌘",[13,15,29],"es",{"courses":17,"paths":17,"articles":18,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":17},2,{"slug":33,"title":34,"summary":35,"html":36,"css":37,"js":38,"config":39,"doc":41},"css-anchor-positioning","CSS Anchor Positioning — position a tooltip with zero JS",null,"\u003Cdiv class=\"wrap\">\n    \u003Ch1>🎮 Anchor Positioning: a tooltip with zero JS\u003C\u002Fh1>\n    \u003Cp class=\"sub\">\n      Pick where the tooltip sits with \u003Ccode>position-area\u003C\u002Fcode>, then \u003Cstrong>drag the anchor\u003C\u002Fstrong> around the stage.\n      The tooltip tracks it automatically — no \u003Ccode>computePosition()\u003C\u002Fcode>, no \u003Ccode>ResizeObserver\u003C\u002Fcode>, no scroll\n      listener. Turn on \u003Cstrong>smart fallbacks\u003C\u002Fstrong> and drag toward an edge: \u003Ccode>position-try-fallbacks\u003C\u002Fcode>\n      flips the tooltip to keep it on-screen.\n    \u003C\u002Fp>\n\n    \u003Cdiv class=\"banner\" id=\"banner\">\n      Heads up: this browser doesn't support CSS Anchor Positioning (\u003Ccode>anchor-name\u003C\u002Fcode>), so the tooltip\n      won't reposition here. It's \u003Cstrong>Baseline 2026\u003C\u002Fstrong> — try the latest Chrome, Safari, or Firefox to see it live.\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"panel\">\n      \u003Ch2>1 · Controls\u003C\u002Fh2>\n      \u003Cdiv class=\"controls\">\n        \u003Cdiv class=\"ctl\">\n          \u003Cspan>position-area\u003C\u002Fspan>\n          \u003Cdiv class=\"grid3\" id=\"picker\">\n            \u003Cbutton data-area=\"top left\"      aria-label=\"top left\">↖\u003C\u002Fbutton>\n            \u003Cbutton data-area=\"top center\"    aria-label=\"top center\" aria-pressed=\"true\">↑\u003C\u002Fbutton>\n            \u003Cbutton data-area=\"top right\"     aria-label=\"top right\">↗\u003C\u002Fbutton>\n            \u003Cbutton data-area=\"center left\"   aria-label=\"center left\">←\u003C\u002Fbutton>\n            \u003Cbutton class=\"center\" tabindex=\"-1\" aria-hidden=\"true\">•\u003C\u002Fbutton>\n            \u003Cbutton data-area=\"center right\"  aria-label=\"center right\">→\u003C\u002Fbutton>\n            \u003Cbutton data-area=\"bottom left\"   aria-label=\"bottom left\">↙\u003C\u002Fbutton>\n            \u003Cbutton data-area=\"bottom center\" aria-label=\"bottom center\">↓\u003C\u002Fbutton>\n            \u003Cbutton data-area=\"bottom right\"  aria-label=\"bottom right\">↘\u003C\u002Fbutton>\n          \u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"ctl\">\n          \u003Cspan>Overflow handling\u003C\u002Fspan>\n          \u003Clabel class=\"toggle\">\n            \u003Cinput type=\"checkbox\" id=\"smart\" \u002F>\n            \u003Cspan class=\"switch\">\u003C\u002Fspan>\n            \u003Cspan>Smart fallbacks (\u003Ccode style=\"font-family:ui-monospace\">position-try-fallbacks\u003C\u002Fcode>)\u003C\u002Fspan>\n          \u003C\u002Flabel>\n          \u003Cp class=\"hint\" style=\"margin-top:12px; max-width:280px;\">Drag the anchor to the edge the tooltip points at. With fallbacks \u003Cspan class=\"flip\">on\u003C\u002Fspan>, it flips instead of clipping.\u003C\u002Fp>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"panel\">\n      \u003Ch2>2 · Drag the anchor\u003C\u002Fh2>\n      \u003Cdiv class=\"stage\" id=\"stage\">\n        \u003Cbutton class=\"anchor\" id=\"anchor\">Drag me ⠿\u003C\u002Fbutton>\n        \u003Cdiv class=\"tooltip\" id=\"tooltip\" role=\"status\">\n          \u003Cb>Positioned by CSS.\u003C\u002Fb> I'm glued to the anchor — move it and I follow, no JavaScript doing the math.\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n      \u003Cp class=\"hint\" id=\"hint\">\u003C\u002Fp>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"panel\">\n      \u003Ch2>3 · The CSS doing the work\u003C\u002Fh2>\n      \u003Cdiv class=\"code\" id=\"code\">\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>",":root {\n    --bg: #0d1117; --panel: #161b22; --ink: #e8ebf2; --muted: #9aa4b2;\n    --accent: #7c9cff; --good: #3fb950; --bad: #f0883e; --line: #232a35;\n    --card: #0b0e14;\n    font-family: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, sans-serif;\n  }\n  * { box-sizing: border-box; }\n  body { margin: 0; background: var(--bg); color: var(--ink); min-height: 100vh;\n    display: flex; align-items: flex-start; justify-content: center; padding: 18px; }\n  .wrap { width: 100%; max-width: 820px; }\n\n  h1 { font-size: 1.2rem; margin: 0 0 3px; letter-spacing: -0.01em; }\n  .sub { color: var(--muted); font-size: .9rem; margin: 0 0 14px; line-height: 1.45; }\n  .sub code, .sub strong { color: var(--accent); }\n  .sub code { background: #ffffff10; padding: 1px 5px; border-radius: 5px; font-size: .85em;\n    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }\n  .sub strong { font-weight: 700; }\n\n  .panel { background: var(--panel); border: 1px solid var(--line);\n    border-radius: 14px; padding: 16px; margin-bottom: 14px; }\n  .panel h2 { font-size: .72rem; text-transform: uppercase; letter-spacing: .07em;\n    color: var(--muted); margin: 0 0 12px; font-weight: 700; }\n\n  .banner { display: none; margin-bottom: 14px; padding: 11px 13px; border-radius: 11px;\n    font-size: .85rem; line-height: 1.5; color: #ffcda6; background: #f0883e14; border: 1px solid #f0883e55; }\n  .banner.show { display: block; }\n  .banner code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n    background: #ffffff12; padding: 1px 5px; border-radius: 5px; }\n\n  \u002F* ---- Controls ---- *\u002F\n  .controls { display: flex; flex-wrap: wrap; gap: 18px 26px; align-items: center; }\n  .ctl > span { display: block; font-size: .72rem; color: var(--muted); font-weight: 700;\n    letter-spacing: .03em; text-transform: uppercase; margin-bottom: 8px; }\n\n  \u002F* 3×3 placement picker *\u002F\n  .grid3 { display: grid; grid-template-columns: repeat(3, 34px); grid-template-rows: repeat(3, 34px);\n    gap: 5px; }\n  .grid3 button { border: 1px solid var(--line); background: var(--card); border-radius: 8px;\n    cursor: pointer; color: var(--muted); font-size: .9rem; display: grid; place-items: center; padding: 0; }\n  .grid3 button:hover { background: #ffffff0d; color: var(--ink); }\n  .grid3 button[aria-pressed=\"true\"] { background: var(--accent); border-color: var(--accent); color: #0b0e14; }\n  .grid3 button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }\n  .grid3 .center { color: #4b5568; cursor: default; }\n  .grid3 .center:hover { background: var(--card); color: #4b5568; }\n\n  .toggle { display: inline-flex; align-items: center; gap: 10px; cursor: pointer;\n    font-size: .88rem; font-weight: 600; user-select: none; }\n  .switch { position: relative; width: 46px; height: 26px; border-radius: 999px;\n    background: #2a3140; border: 1px solid var(--line); transition: background .18s ease; flex: none; }\n  .switch::after { content: \"\"; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px;\n    border-radius: 50%; background: #cfd6e4; transition: transform .18s ease; }\n  input[type=checkbox] { position: absolute; opacity: 0; pointer-events: none; }\n  .toggle input:checked + .switch { background: var(--good); }\n  .toggle input:checked + .switch::after { transform: translateX(20px); background: #fff; }\n  .toggle input:focus-visible + .switch { outline: 2px solid var(--accent); outline-offset: 2px; }\n\n  \u002F* ---- The stage ---- *\u002F\n  .stage { position: relative; height: 260px; border: 1px dashed var(--line); border-radius: 12px;\n    background:\n      radial-gradient(circle at 1px 1px, #ffffff0a 1px, transparent 0) 0 0 \u002F 22px 22px,\n      #0a0c12;\n    overflow: hidden; touch-action: none; }\n\n  .anchor {\n    anchor-name: --tip-anchor;\n    position: absolute; top: 118px; left: 340px;\n    font: inherit; font-weight: 700; font-size: .84rem; cursor: grab;\n    background: var(--accent); color: #0b0e14; border: none; border-radius: 10px;\n    padding: 9px 14px; box-shadow: 0 6px 18px #0008; white-space: nowrap;\n  }\n  .anchor:active { cursor: grabbing; }\n  .anchor:focus-visible { outline: 2px solid #fff; outline-offset: 2px; }\n\n  .tooltip {\n    position: absolute;\n    position-anchor: --tip-anchor;\n    position-area: top center;\n    margin: 8px;\n    max-width: 190px;\n    background: #eef1f8; color: #12151c; font-size: .8rem; line-height: 1.45; font-weight: 500;\n    border-radius: 10px; padding: 8px 11px; box-shadow: 0 8px 24px #000a;\n    position-try-fallbacks: none;\n  }\n  .tooltip.smart { position-try-fallbacks: flip-block, flip-inline, flip-block flip-inline; }\n  .tooltip b { color: #000; }\n\n  \u002F* Fallback when the browser can't do anchor positioning: keep it visible & sane *\u002F\n  @supports not (anchor-name: --x) {\n    .anchor { top: 20px; left: 20px; }\n    .tooltip { position: static; margin-top: 12px; max-width: none; }\n  }\n\n  \u002F* ---- CSS readout ---- *\u002F\n  .code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: .82rem;\n    background: var(--card); border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px;\n    line-height: 1.65; overflow-x: auto; margin-top: 4px; }\n  .code .sel { color: var(--accent); }\n  .code .prop { color: #86e29b; }\n  .code .cmt { color: #6b7688; }\n  .code .hl { background: #3fb95022; border-radius: 4px; padding: 0 3px; }\n\n  .hint { font-size: .82rem; color: var(--muted); margin: 10px 0 0; line-height: 1.5; }\n  .hint code { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;\n    color: var(--ink); background: #ffffff10; padding: 1px 5px; border-radius: 5px; font-size: .86em; }\n  .hint .flip { color: var(--good); font-weight: 700; }","\"use strict\";\n  const $ = (id) => document.getElementById(id);\n  const supported = CSS.supports(\"anchor-name: --x\") && CSS.supports(\"position-area: top center\");\n  if (!supported) $(\"banner\").classList.add(\"show\");\n\n  let area = \"top center\";\n  const anchor = $(\"anchor\");\n  const tooltip = $(\"tooltip\");\n  const stage = $(\"stage\");\n\n  function renderCode() {\n    const fb = $(\"smart\").checked\n      ? `\\n  \u003Cspan class=\"prop hl\">position-try-fallbacks:\u003C\u002Fspan>\\n    \u003Cspan class=\"prop hl\">flip-block, flip-inline;\u003C\u002Fspan>`\n      : `\\n  \u003Cspan class=\"cmt\">\u002F* no fallbacks — clips at the edge *\u002F\u003C\u002Fspan>`;\n    $(\"code\").innerHTML =\n`\u003Cspan class=\"sel\">.anchor\u003C\u002Fspan> {\n  \u003Cspan class=\"prop\">anchor-name: --tip-anchor;\u003C\u002Fspan>\n}\n\u003Cspan class=\"sel\">.tooltip\u003C\u002Fspan> {\n  position: absolute;\n  \u003Cspan class=\"prop\">position-anchor: --tip-anchor;\u003C\u002Fspan>\n  \u003Cspan class=\"prop hl\">position-area: ${area};\u003C\u002Fspan>${fb}\n}`;\n  }\n\n  function renderHint() {\n    const on = $(\"smart\").checked;\n    $(\"hint\").innerHTML = on\n      ? \"Fallbacks \u003Cspan class='flip'>on\u003C\u002Fspan>: if \u003Ccode>\" + area + \"\u003C\u002Fcode> would push the tooltip off the stage, the browser tries the opposite edge automatically.\"\n      : \"Fallbacks off: the tooltip stays at \u003Ccode>\" + area + \"\u003C\u002Fcode> even when it clips. Flip the toggle, then drag to an edge.\";\n  }\n\n  function setArea(a) {\n    area = a;\n    tooltip.style.setProperty(\"position-area\", a);\n    \u002F\u002F some engines read the property camel-free; also set via cssText fallback\n    tooltip.style.positionArea = a;\n    document.querySelectorAll(\"#picker button[data-area]\").forEach((b) =>\n      b.setAttribute(\"aria-pressed\", String(b.dataset.area === a)));\n    renderCode();\n    renderHint();\n  }\n\n  $(\"picker\").addEventListener(\"click\", (e) => {\n    const b = e.target.closest(\"button[data-area]\"); if (!b) return;\n    setArea(b.dataset.area);\n  });\n\n  $(\"smart\").addEventListener(\"change\", () => {\n    tooltip.classList.toggle(\"smart\", $(\"smart\").checked);\n    renderCode();\n    renderHint();\n  });\n\n  \u002F\u002F ---- Drag the anchor (pointer events; works mouse + touch) ----\n  let dragging = false, offX = 0, offY = 0;\n  anchor.addEventListener(\"pointerdown\", (e) => {\n    dragging = true;\n    anchor.setPointerCapture(e.pointerId);\n    const a = anchor.getBoundingClientRect();\n    offX = e.clientX - a.left;\n    offY = e.clientY - a.top;\n  });\n  anchor.addEventListener(\"pointermove\", (e) => {\n    if (!dragging) return;\n    const s = stage.getBoundingClientRect();\n    let x = e.clientX - s.left - offX;\n    let y = e.clientY - s.top - offY;\n    x = Math.max(0, Math.min(x, stage.clientWidth - anchor.offsetWidth));\n    y = Math.max(0, Math.min(y, stage.clientHeight - anchor.offsetHeight));\n    anchor.style.left = x + \"px\";\n    anchor.style.top = y + \"px\";\n  });\n  const endDrag = (e) => {\n    if (!dragging) return;\n    dragging = false;\n    try { anchor.releasePointerCapture(e.pointerId); } catch (_) {}\n  };\n  anchor.addEventListener(\"pointerup\", endDrag);\n  anchor.addEventListener(\"pointercancel\", endDrag);\n\n  \u002F\u002F Keyboard nudge for accessibility\n  anchor.addEventListener(\"keydown\", (e) => {\n    const step = e.shiftKey ? 24 : 8;\n    const cur = { l: anchor.offsetLeft, t: anchor.offsetTop };\n    if (e.key === \"ArrowLeft\")  cur.l -= step;\n    else if (e.key === \"ArrowRight\") cur.l += step;\n    else if (e.key === \"ArrowUp\")    cur.t -= step;\n    else if (e.key === \"ArrowDown\")  cur.t += step;\n    else return;\n    e.preventDefault();\n    anchor.style.left = Math.max(0, Math.min(cur.l, stage.clientWidth - anchor.offsetWidth)) + \"px\";\n    anchor.style.top  = Math.max(0, Math.min(cur.t, stage.clientHeight - anchor.offsetHeight)) + \"px\";\n  });\n\n  setArea(area);\n  renderHint();",{"height":40,"autorun":17},520,{"slug":33,"title":42,"excerpt":43,"coverUrl":44},"You're using Floating UI to position your tooltip. The browser does it natively now.","CSS Anchor Positioning is Baseline 2026 — it lets any element declare itself an anchor and any floating element position relative to it in pure CSS. No JavaScript, no layout math, no ResizeObserver wiring.","\u002Fmedia\u002Fcovers\u002Fcss-anchor-positioning.png"]