[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"playground-content-visibility-skip-offscreen-rendering":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},"content-visibility-skip-offscreen-rendering","content-visibility: auto — skip off-screen rendering",null,"\u003Cdiv class=\"pg\">\n    \u003Ch1 class=\"pg-title\">🎮 content-visibility: auto\u003C\u002Fh1>\n    \u003Cp class=\"pg-sub\">\n      500 article cards — rendered without \u003Ccode>content-visibility\u003C\u002Fcode>, then\n      with it. The timer runs from DOM insert to first committed paint (double-rAF).\n      \u003Cstrong>Run both buttons\u003C\u002Fstrong> to see the difference.\n    \u003C\u002Fp>\n\n    \u003Cdiv class=\"panel\">\n      \u003Cdiv class=\"panel-label\">\u003Cspan class=\"n\">1\u003C\u002Fspan> Run the comparison\u003C\u002Fdiv>\n      \u003Cdiv class=\"btn-row\">\n        \u003Cbutton class=\"btn bad\"     id=\"btnWithout\">▶ Without content-visibility\u003C\u002Fbutton>\n        \u003Cbutton class=\"btn primary\" id=\"btnWith\">▶ With content-visibility: auto\u003C\u002Fbutton>\n      \u003C\u002Fdiv>\n      \u003Cp class=\"hint\">\n        Run \u003Cem>Without\u003C\u002Fem> first for a baseline, then \u003Cem>With\u003C\u002Fem>. The gap is\n        larger on slower CPUs — the property pays off most where users feel lag most.\n      \u003C\u002Fp>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"panel\">\n      \u003Cdiv class=\"panel-label\">\u003Cspan class=\"n\">2\u003C\u002Fspan> Render time comparison\u003C\u002Fdiv>\n      \u003Cdiv class=\"timing-grid\">\n        \u003Cdiv class=\"timing-box\">\n          \u003Cdiv class=\"t-label\">Without\u003C\u002Fdiv>\n          \u003Cdiv class=\"t-val\" id=\"timeWithout\">—\u003C\u002Fdiv>\n          \u003Cdiv class=\"t-note\">Browser paints all 500 cards\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n        \u003Cdiv class=\"timing-box\">\n          \u003Cdiv class=\"t-label\">With content-visibility: auto\u003C\u002Fdiv>\n          \u003Cdiv class=\"t-val\" id=\"timeWith\">—\u003C\u002Fdiv>\n          \u003Cdiv class=\"t-note\">Off-screen cards skipped\u003C\u002Fdiv>\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n      \u003Cdiv class=\"speedup\" id=\"speedup\">\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"panel\">\n      \u003Cdiv class=\"panel-label\">\u003Cspan class=\"n\">3\u003C\u002Fspan> The list\u003C\u002Fdiv>\n      \u003Cdiv class=\"counter-row\">\n        \u003Cspan id=\"listStatus\">Click a button above to render.\u003C\u002Fspan>\n        \u003Cspan id=\"listMode\" class=\"pill\">\u003C\u002Fspan>\n      \u003C\u002Fdiv>\n      \u003Cdiv class=\"card-scroll\" id=\"cardList\">\n        \u003Cdiv style=\"display:flex;align-items:center;justify-content:center;\n                    height:100%;color:var(--muted);font-size:.85rem;\">\n          Click a button above to generate the list.\n        \u003C\u002Fdiv>\n      \u003C\u002Fdiv>\n      \u003Cp class=\"hint\">\n        \u003Cstrong>Scroll after each run.\u003C\u002Fstrong> With \u003Ccode>content-visibility: auto\u003C\u002Fcode>\n        the DOM is identical — only rendering is deferred per section.\n        \u003Ccode>contain-intrinsic-size: auto 68px\u003C\u002Fcode> gives off-screen cards a\n        placeholder height so the scrollbar stays accurate and never jumps.\n      \u003C\u002Fp>\n    \u003C\u002Fdiv>\n\n    \u003Cdiv class=\"panel\">\n      \u003Cdiv class=\"panel-label\">\u003Cspan class=\"n\">4\u003C\u002Fspan> The CSS\u003C\u002Fdiv>\n      \u003Cdiv class=\"code\">\u003Cspan class=\"tok-sel\">.card-item\u003C\u002Fspan> {\n  \u003Cspan class=\"tok-prop\">padding\u003C\u002Fspan>: \u003Cspan class=\"tok-num\">12px 16px\u003C\u002Fspan>;\n  \u003Cspan class=\"tok-prop\">border-bottom\u003C\u002Fspan>: \u003Cspan class=\"tok-str\">1px solid #e2e7f0\u003C\u002Fspan>;\n}\n\n\u003Cspan class=\"tok-cmt\">\u002F* Two lines — that's the whole change *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"tok-sel\">.card-item.optimized\u003C\u002Fspan> {\n  \u003Cspan class=\"tok-prop\">content-visibility\u003C\u002Fspan>: \u003Cspan class=\"tok-kw\">auto\u003C\u002Fspan>;\n  \u003Cspan class=\"tok-prop\">contain-intrinsic-size\u003C\u002Fspan>: \u003Cspan class=\"tok-kw\">auto\u003C\u002Fspan> \u003Cspan class=\"tok-num\">68px\u003C\u002Fspan>; \u003Cspan class=\"tok-cmt\">\u002F* height estimate *\u002F\u003C\u002Fspan>\n}\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n  \u003C\u002Fdiv>","\u002F*\n * Shared playground design system — daily-post\n * ============================================\n * Link it from any post's playground\u002Findex.html (no per-post boilerplate):\n *\n *   \u003Clink rel=\"stylesheet\" href=\"..\u002F..\u002F..\u002Fplayground\u002Fassets\u002Fbase.css\" \u002F>\n *\n * The relative path resolves the same locally (posts\u002F\u003Cbase>\u002Fplayground\u002F) and on\n * the deployed host (public\u002Fposts\u002F\u003Cbase>\u002Fplayground\u002F), because build-site.js\n * mirrors this file to public\u002Fplayground\u002Fassets\u002Fbase.css. For a CodeSandbox\n * --embed, playground.js inlines this file so the single-file sandbox still styles.\n *\n * Then write ONLY the demo-specific CSS in your inline \u003Cstyle>. Everything below\n * — tokens, layout, panels, buttons, toggles, sliders, code blocks, status\u002Flog,\n * banners, presets — comes for free and looks consistent across every post.\n *\n * Theme-aware: dark by default, light via prefers-color-scheme AND a\n * [data-theme] override on \u003Chtml> (so a toggle can force either). Accessible:\n * real focus rings, adequate contrast, reduced-motion honored.\n *\u002F\n\n\u002F* ---------- design tokens ---------- *\u002F\n:root {\n  --bg: #0d1117;\n  --bg-grid: #ffffff0a;\n  --panel: #161b22;\n  --panel-2: #0b0e14;\n  --card: #0b0e14;\n  --ink: #e8ebf2;\n  --muted: #9aa4b2;\n  --faint: #6b7688;\n  --line: #232a35;\n  --line-soft: #ffffff12;\n  --accent: #7c9cff;\n  --accent-ink: #0b0e14;\n  --good: #3fb950;\n  --warn: #f0883e;\n  --bad: #f85149;\n  --info: #58a6ff;\n  --radius: 14px;\n  --radius-sm: 9px;\n  --shadow: 0 8px 30px #0006;\n  --shadow-sm: 0 4px 14px #0005;\n  --mono: ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace;\n  --sans: ui-sans-serif, system-ui, -apple-system, \"Segoe UI\", Roboto, \"Helvetica Neue\", sans-serif;\n  color-scheme: dark;\n}\n\n\u002F* Light theme — system preference, overridable by [data-theme] on \u003Chtml>. *\u002F\n@media (prefers-color-scheme: light) {\n  :root:not([data-theme=\"dark\"]) {\n    --bg: #f4f6fb;\n    --bg-grid: #00000008;\n    --panel: #ffffff;\n    --panel-2: #f0f3f9;\n    --card: #f7f9fc;\n    --ink: #131722;\n    --muted: #5b6472;\n    --faint: #8a93a3;\n    --line: #e2e7f0;\n    --line-soft: #00000010;\n    --accent: #2f6df0;\n    --accent-ink: #ffffff;\n    --good: #1a7f37;\n    --warn: #bc4c00;\n    --bad: #cf222e;\n    --info: #0969da;\n    --shadow: 0 8px 30px #0000001f;\n    --shadow-sm: 0 4px 14px #00000014;\n    color-scheme: light;\n  }\n}\n:root[data-theme=\"light\"] {\n  --bg: #f4f6fb;\n  --bg-grid: #00000008;\n  --panel: #ffffff;\n  --panel-2: #f0f3f9;\n  --card: #f7f9fc;\n  --ink: #131722;\n  --muted: #5b6472;\n  --faint: #8a93a3;\n  --line: #e2e7f0;\n  --line-soft: #00000010;\n  --accent: #2f6df0;\n  --accent-ink: #ffffff;\n  --good: #1a7f37;\n  --warn: #bc4c00;\n  --bad: #cf222e;\n  --info: #0969da;\n  --shadow: 0 8px 30px #0000001f;\n  --shadow-sm: 0 4px 14px #00000014;\n  color-scheme: light;\n}\n\n\u002F* ---------- reset & base ---------- *\u002F\n*, *::before, *::after { box-sizing: border-box; }\n\nbody {\n  margin: 0;\n  min-height: 100vh;\n  font-family: var(--sans);\n  color: var(--ink);\n  background:\n    radial-gradient(circle at 1px 1px, var(--bg-grid) 1px, transparent 0) 0 0 \u002F 24px 24px,\n    var(--bg);\n  display: flex;\n  justify-content: center;\n  align-items: flex-start;\n  padding: clamp(14px, 3vw, 26px);\n  line-height: 1.55;\n  -webkit-font-smoothing: antialiased;\n  text-rendering: optimizeLegibility;\n}\n\n\u002F* The single content column. *\u002F\n.pg {\n  width: 100%;\n  max-width: 860px;\n}\n\n\u002F* ---------- header ---------- *\u002F\n.pg-title {\n  font-size: clamp(1.15rem, 1rem + 1vw, 1.4rem);\n  font-weight: 800;\n  letter-spacing: -0.02em;\n  margin: 0 0 4px;\n  display: flex;\n  align-items: center;\n  gap: 9px;\n}\n.pg-sub {\n  color: var(--muted);\n  font-size: 0.92rem;\n  line-height: 1.5;\n  margin: 0 0 16px;\n  max-width: 66ch;\n}\n.pg-sub strong, .pg-sub b { color: var(--ink); font-weight: 700; }\n.pg-sub code { font-family: var(--mono); background: var(--line-soft); color: var(--accent);\n  padding: 1px 6px; border-radius: 6px; font-size: 0.85em; }\n\n\u002F* ---------- panels ---------- *\u002F\n.panel {\n  background: var(--panel);\n  border: 1px solid var(--line);\n  border-radius: var(--radius);\n  padding: 16px 18px;\n  margin-bottom: 14px;\n  box-shadow: var(--shadow-sm);\n}\n.panel-label {\n  font-size: 0.7rem;\n  text-transform: uppercase;\n  letter-spacing: 0.08em;\n  color: var(--muted);\n  font-weight: 800;\n  margin: 0 0 13px;\n  display: flex;\n  align-items: center;\n  gap: 8px;\n}\n.panel-label .n {\n  display: inline-grid; place-items: center;\n  width: 18px; height: 18px; border-radius: 50%;\n  background: var(--accent); color: var(--accent-ink);\n  font-size: 0.66rem; font-weight: 800;\n}\n\n\u002F* ---------- layout helpers ---------- *\u002F\n.row { display: flex; flex-wrap: wrap; gap: 12px; align-items: center; }\n.row.between { justify-content: space-between; }\n.col { display: flex; flex-direction: column; gap: 10px; }\n.grow { flex: 1 1 0; min-width: 0; }\n.wrap-controls { display: flex; flex-wrap: wrap; gap: 16px 24px; align-items: flex-end; }\n.field { display: flex; flex-direction: column; gap: 7px; min-width: 0; }\n.field > .label, .ctl-label {\n  font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.04em;\n  color: var(--muted); font-weight: 800;\n}\n\n\u002F* ---------- buttons ---------- *\u002F\n.btn {\n  font: inherit; font-weight: 650; font-size: 0.88rem;\n  cursor: pointer; user-select: none;\n  border: 1px solid var(--line);\n  background: var(--card); color: var(--ink);\n  padding: 9px 15px; border-radius: var(--radius-sm);\n  display: inline-flex; align-items: center; gap: 8px;\n  transition: background .15s ease, border-color .15s ease, transform .05s ease, color .15s ease;\n}\n.btn:hover { background: var(--line-soft); border-color: var(--muted); }\n.btn:active { transform: translateY(1px); }\n.btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }\n.btn[disabled] { opacity: 0.45; cursor: not-allowed; }\n.btn.primary { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); }\n.btn.primary:hover { filter: brightness(1.08); background: var(--accent); }\n.btn.good { background: var(--good); border-color: var(--good); color: #04140a; }\n.btn.bad  { background: var(--bad);  border-color: var(--bad);  color: #fff; }\n.btn.ghost { background: transparent; }\n.btn.sm { padding: 6px 11px; font-size: 0.82rem; }\n\n.btn-row { display: flex; flex-wrap: wrap; gap: 9px; }\n\n\u002F* Segmented control — a row of buttons where one is active (aria-pressed). *\u002F\n.seg { display: inline-flex; background: var(--card); border: 1px solid var(--line);\n  border-radius: var(--radius-sm); padding: 3px; gap: 3px; }\n.seg button {\n  font: inherit; font-weight: 650; font-size: 0.84rem; cursor: pointer;\n  border: none; background: transparent; color: var(--muted);\n  padding: 7px 13px; border-radius: 7px;\n  transition: background .15s ease, color .15s ease;\n}\n.seg button:hover { color: var(--ink); }\n.seg button[aria-pressed=\"true\"] { background: var(--accent); color: var(--accent-ink); }\n.seg button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }\n\n\u002F* Preset chips — worked-example shortcuts. *\u002F\n.presets { display: flex; flex-wrap: wrap; gap: 8px; }\n.presets .btn { background: var(--panel-2); }\n\n\u002F* ---------- toggle switch ---------- *\u002F\n.toggle { display: inline-flex; align-items: center; gap: 10px; cursor: pointer;\n  font-size: 0.88rem; font-weight: 600; user-select: none; }\n.toggle input { position: absolute; opacity: 0; width: 0; height: 0; }\n.switch { position: relative; width: 44px; height: 25px; border-radius: 999px; flex: none;\n  background: var(--line); border: 1px solid var(--line); transition: background .18s ease; }\n.switch::after { content: \"\"; position: absolute; top: 2px; left: 2px; width: 19px; height: 19px;\n  border-radius: 50%; background: #cfd6e4; transition: transform .18s ease; box-shadow: 0 1px 3px #0006; }\n.toggle input:checked + .switch { background: var(--good); border-color: var(--good); }\n.toggle input:checked + .switch::after { transform: translateX(19px); background: #fff; }\n.toggle input:focus-visible + .switch { outline: 2px solid var(--accent); outline-offset: 2px; }\n\n\u002F* ---------- range slider ---------- *\u002F\ninput[type=\"range\"] {\n  -webkit-appearance: none; appearance: none; width: 100%; height: 6px;\n  background: var(--line); border-radius: 999px; outline: none; cursor: pointer; margin: 8px 0;\n}\ninput[type=\"range\"]::-webkit-slider-thumb {\n  -webkit-appearance: none; appearance: none; width: 19px; height: 19px; border-radius: 50%;\n  background: var(--accent); border: 2px solid var(--panel); box-shadow: var(--shadow-sm); cursor: pointer;\n}\ninput[type=\"range\"]::-moz-range-thumb {\n  width: 19px; height: 19px; border-radius: 50%; background: var(--accent);\n  border: 2px solid var(--panel); box-shadow: var(--shadow-sm); cursor: pointer;\n}\ninput[type=\"range\"]:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }\n\n\u002F* ---------- text inputs \u002F selects ---------- *\u002F\ninput[type=\"text\"], input[type=\"number\"], input[type=\"search\"], select, textarea {\n  font: inherit; color: var(--ink); background: var(--card);\n  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 8px 11px; width: 100%;\n}\ntextarea { font-family: var(--mono); font-size: 0.85rem; line-height: 1.6; resize: vertical; }\ninput:focus-visible, select:focus-visible, textarea:focus-visible {\n  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);\n}\n\n\u002F* ---------- status \u002F output \u002F log ---------- *\u002F\n.status {\n  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;\n  font-size: 0.9rem; font-weight: 600;\n  padding: 11px 14px; border-radius: var(--radius-sm);\n  background: var(--panel-2); border: 1px solid var(--line); color: var(--ink);\n}\n.status.good { border-color: color-mix(in srgb, var(--good) 55%, var(--line)); }\n.status.warn { border-color: color-mix(in srgb, var(--warn) 55%, var(--line)); }\n.status.bad  { border-color: color-mix(in srgb, var(--bad) 55%, var(--line)); }\n.dot { width: 9px; height: 9px; border-radius: 50%; background: var(--muted); flex: none; }\n.dot.good { background: var(--good); } .dot.warn { background: var(--warn); }\n.dot.bad { background: var(--bad); } .dot.info { background: var(--info); }\n\n.output { background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-sm);\n  padding: 13px 15px; }\n.log { font-family: var(--mono); font-size: 0.8rem; line-height: 1.7; background: var(--card);\n  border: 1px solid var(--line); border-radius: var(--radius-sm); padding: 11px 14px;\n  max-height: 220px; overflow-y: auto; white-space: pre-wrap; }\n\n\u002F* ---------- code block + syntax tokens ---------- *\u002F\n.code {\n  font-family: var(--mono); font-size: 0.82rem; line-height: 1.7;\n  background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-sm);\n  padding: 13px 15px; overflow-x: auto; white-space: pre; tab-size: 2;\n}\n.code .tok-sel  { color: var(--accent); }\n.code .tok-prop { color: #86e29b; }\n.code .tok-kw   { color: #ff9bcb; }\n.code .tok-str  { color: #f0c674; }\n.code .tok-num  { color: #f0883e; }\n.code .tok-cmt  { color: var(--faint); font-style: italic; }\n.code .hl { background: color-mix(in srgb, var(--good) 22%, transparent); border-radius: 4px; padding: 0 3px; }\n\n\u002F* ---------- hint \u002F caption ---------- *\u002F\n.hint { font-size: 0.83rem; color: var(--muted); line-height: 1.55; margin: 10px 0 0; }\n.hint code { font-family: var(--mono); color: var(--ink); background: var(--line-soft);\n  padding: 1px 5px; border-radius: 5px; font-size: 0.86em; }\n.hint strong { color: var(--ink); }\n\n\u002F* ---------- banner (feature-support \u002F notices) ---------- *\u002F\n.banner {\n  display: none; margin-bottom: 14px; padding: 11px 14px; border-radius: 11px;\n  font-size: 0.85rem; line-height: 1.5;\n  color: var(--ink); background: color-mix(in srgb, var(--warn) 12%, var(--panel));\n  border: 1px solid color-mix(in srgb, var(--warn) 45%, var(--line));\n}\n.banner.show { display: block; }\n.banner.info { background: color-mix(in srgb, var(--info) 12%, var(--panel));\n  border-color: color-mix(in srgb, var(--info) 45%, var(--line)); }\n.banner code { font-family: var(--mono); background: var(--line-soft); padding: 1px 5px; border-radius: 5px; }\n\n\u002F* ---------- misc atoms ---------- *\u002F\n.pill { display: inline-flex; align-items: center; gap: 6px; font-size: 0.75rem; font-weight: 700;\n  padding: 3px 9px; border-radius: 999px; background: var(--line-soft); color: var(--muted); }\n.pill.good { color: var(--good); background: color-mix(in srgb, var(--good) 16%, transparent); }\n.pill.bad  { color: var(--bad);  background: color-mix(in srgb, var(--bad) 16%, transparent); }\n.mono { font-family: var(--mono); }\n.muted { color: var(--muted); }\n.big-num { font-family: var(--mono); font-size: 1.6rem; font-weight: 800; letter-spacing: -0.02em; }\n\n\u002F* Optional theme toggle button (top-right). Wire it in JS if you want it. *\u002F\n.theme-btn { position: fixed; top: 12px; right: 12px; z-index: 20; }\n\n\u002F* ---------- responsive ---------- *\u002F\n@media (max-width: 560px) {\n  .panel { padding: 14px; }\n  .wrap-controls { gap: 14px; }\n  .btn { padding: 9px 13px; }\n}\n\n\u002F* ---------- reduced motion ---------- *\u002F\n@media (prefers-reduced-motion: reduce) {\n  *, *::before, *::after { transition-duration: 0.01ms !important; animation-duration: 0.01ms !important; }\n}\n\n\n.timing-grid {\n    display: grid;\n    grid-template-columns: 1fr 1fr;\n    gap: 10px;\n  }\n  @media (max-width: 480px) { .timing-grid { grid-template-columns: 1fr; } }\n\n  .timing-box {\n    background: var(--panel-2);\n    border: 1px solid var(--line);\n    border-radius: var(--radius-sm);\n    padding: 14px;\n    text-align: center;\n  }\n  .timing-box .t-label {\n    font-size: 0.7rem; text-transform: uppercase; letter-spacing: 0.06em;\n    font-weight: 700; color: var(--muted); margin-bottom: 8px;\n  }\n  .timing-box .t-val {\n    font-family: var(--mono); font-size: 1.6rem; font-weight: 800;\n    line-height: 1; color: var(--ink);\n  }\n  .timing-box .t-val.fast { color: var(--good); }\n  .timing-box .t-val.slow { color: var(--bad); }\n  .timing-box .t-note { font-size: 0.72rem; color: var(--muted); margin-top: 6px; }\n\n  .speedup {\n    margin-top: 10px; padding: 10px 14px; border-radius: var(--radius-sm);\n    background: color-mix(in srgb, var(--good) 10%, var(--panel-2));\n    border: 1px solid color-mix(in srgb, var(--good) 30%, var(--line));\n    font-size: 0.83rem; font-weight: 700; color: var(--good);\n    text-align: center; display: none;\n  }\n  .speedup.show { display: block; }\n\n  .card-scroll {\n    height: 340px; overflow-y: auto;\n    border: 1px solid var(--line); border-radius: var(--radius-sm);\n    background: var(--card);\n  }\n  .card-item {\n    padding: 12px 16px;\n    border-bottom: 1px solid var(--line-soft);\n  }\n  .card-item.cv {\n    content-visibility: auto;\n    contain-intrinsic-size: auto 68px;\n  }\n  .card-title {\n    font-weight: 700; font-size: 0.88rem; margin-bottom: 4px;\n    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;\n  }\n  .card-meta {\n    font-size: 0.74rem; color: var(--muted);\n    display: flex; gap: 14px; margin-bottom: 6px;\n  }\n  .card-tag {\n    display: inline-block; font-size: 0.68rem; font-weight: 700;\n    padding: 2px 7px; border-radius: 999px;\n    background: color-mix(in srgb, var(--accent) 15%, transparent);\n    color: var(--accent); margin-right: 4px;\n  }\n  .counter-row {\n    display: flex; justify-content: space-between; align-items: center;\n    font-size: 0.78rem; color: var(--muted); margin-bottom: 6px; flex-wrap: wrap; gap: 6px;\n  }","const CARD_COUNT = 500;\n    const titles = [\n      \"The curious case of CSS containment\",\n      \"Why your async code is lying to you\",\n      \"TypeScript discriminated unions in practice\",\n      \"Debugging memory leaks at 2am\",\n      \"Grid layout: the properties nobody teaches\",\n      \"WebAssembly for the rest of us\",\n      \"How the browser event loop actually works\",\n      \"Signals vs observables vs state machines\",\n      \"The underrated power of CSS custom properties\",\n      \"Accessibility is a performance feature\",\n      \"Micro-frontends: good, bad, and ugly\",\n      \"From callback hell to async heaven\",\n      \"Container queries will replace media queries\",\n      \"The real cost of JavaScript bundles\",\n      \"Progressive enhancement is not dead\",\n      \"Composable architecture without frameworks\",\n      \"When not to use useState\",\n      \"Browser caching strategies that actually work\",\n      \"HTTP\u002F3 in five minutes\",\n      \"The dark side of tree shaking\",\n    ];\n    const tagSets = [\n      [\"css\", \"frontend\"], [\"javascript\", \"webdev\"], [\"typescript\", \"devtools\"],\n      [\"performance\", \"javascript\"], [\"css\", \"layout\"], [\"webdev\", \"wasm\"],\n      [\"javascript\", \"async\"], [\"frontend\", \"state\"], [\"css\", \"webdev\"],\n      [\"a11y\", \"performance\"],\n    ];\n    const authors = [\"Parsa J.\", \"Alex K.\", \"Mia Chen\", \"Ravi P.\", \"Sora T.\"];\n    const readTimes = [\"4 min\", \"7 min\", \"3 min\", \"9 min\", \"5 min\", \"11 min\"];\n\n    function seeded(n, max) {\n      return ((n * 1664525 + 1013904223) >>> 0) % max;\n    }\n\n    function buildCard(i, useCv) {\n      const title    = titles[seeded(i * 7 + 3, titles.length)];\n      const tagSet   = tagSets[seeded(i * 13 + 1, tagSets.length)];\n      const author   = authors[seeded(i * 3 + 2, authors.length)];\n      const rt       = readTimes[seeded(i * 11 + 5, readTimes.length)];\n\n      const div = document.createElement('div');\n      div.className = 'card-item' + (useCv ? ' cv' : '');\n\n      const titleEl = document.createElement('div');\n      titleEl.className = 'card-title';\n      titleEl.textContent = `${i + 1}. ${title}`;\n\n      const meta = document.createElement('div');\n      meta.className = 'card-meta';\n      const a = document.createElement('span'); a.textContent = '✍️ ' + author;\n      const r = document.createElement('span'); r.textContent = '⏱ ' + rt;\n      meta.appendChild(a); meta.appendChild(r);\n\n      const tagsRow = document.createElement('div');\n      tagSet.forEach(t => {\n        const s = document.createElement('span');\n        s.className = 'card-tag'; s.textContent = '#' + t;\n        tagsRow.appendChild(s);\n      });\n\n      div.appendChild(titleEl);\n      div.appendChild(meta);\n      div.appendChild(tagsRow);\n      return div;\n    }\n\n    function measureRender(useCv) {\n      const list = document.getElementById('cardList');\n      list.innerHTML = '';\n\n      const t0 = performance.now();\n      const frag = document.createDocumentFragment();\n      for (let i = 0; i \u003C CARD_COUNT; i++) frag.appendChild(buildCard(i, useCv));\n      list.appendChild(frag);\n\n      \u002F\u002F Double-rAF: first fires before paint, second fires after the\n      \u002F\u002F browser has committed the frame.\n      requestAnimationFrame(() => {\n        requestAnimationFrame(() => {\n          const elapsed = performance.now() - t0;\n          showResult(useCv, elapsed);\n        });\n      });\n    }\n\n    function showResult(useCv, ms) {\n      const el = document.getElementById(useCv ? 'timeWith' : 'timeWithout');\n      el.textContent = ms.toFixed(1) + ' ms';\n      el.className = 't-val ' + (useCv ? 'fast' : 'slow');\n\n      const modeEl   = document.getElementById('listMode');\n      const statusEl = document.getElementById('listStatus');\n      if (useCv) {\n        modeEl.textContent = 'content-visibility: auto';\n        modeEl.className = 'pill good';\n        statusEl.textContent = '500 cards in DOM — only viewport rendered';\n      } else {\n        modeEl.textContent = 'no content-visibility';\n        modeEl.className = 'pill bad';\n        statusEl.textContent = '500 cards — all painted immediately';\n      }\n\n      const withoutMs = parseFloat(document.getElementById('timeWithout').textContent);\n      const withMs    = parseFloat(document.getElementById('timeWith').textContent);\n      const speedup   = document.getElementById('speedup');\n      if (!isNaN(withoutMs) && !isNaN(withMs) && withMs > 0) {\n        const ratio = withoutMs \u002F withMs;\n        speedup.className = 'speedup show';\n        speedup.textContent =\n          `content-visibility: auto was ${ratio.toFixed(1)}× faster ` +\n          `(${withoutMs.toFixed(1)} ms → ${withMs.toFixed(1)} ms)`;\n      }\n    }\n\n    document.getElementById('btnWithout').addEventListener('click', () => measureRender(false));\n    document.getElementById('btnWith').addEventListener('click',    () => measureRender(true));",{"height":40,"autorun":17},520,{"slug":33,"title":42,"excerpt":43,"coverUrl":44},"Your browser renders everything, even what you can't see — `content-visibility: auto` fixes that","Browsers lay out and paint the entire page on load, including the 80% below the fold. `content-visibility: auto` tells the browser to skip that work for off-screen elements — and it's Baseline 2024.","\u002Fmedia\u002Fcovers\u002Fcontent-visibility-skip-offscreen-rendering.png"]