[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"search-suggestions":32,"article-css-trigonometric-functions-radial-layout":78,"related-css-trigonometric-functions-radial-layout":239,"code:js:true:1mapv4e":367,"code:css:true:jkpegp":368,"code:js:true:km7o00":369},[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","Read it. Run it. Prove it.","A post a day on modern web development — most with an editable playground and a quiz that explains every answer. Free, no account needed.","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.","Grammar explained the way good documentation explains an API — one idea at a time, each with a quiz.","amber-400","⌘",[13,15,29],"es",{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":17,"packages":18,"community":17,"certificates":17,"teams":18,"commerce":18},2,[33,37,41,44,48,52,56,60,64,68,72,75],{"slug":34,"name":35,"articles":36},"webdev","Webdev",56,{"slug":38,"name":39,"articles":40},"frontend","Frontend",50,{"slug":42,"name":43,"articles":40},"javascript","Javascript",{"slug":45,"name":46,"articles":47},"css","Css",21,{"slug":49,"name":50,"articles":51},"typescript","Typescript",10,{"slug":53,"name":54,"articles":55},"performance","Performance",8,{"slug":57,"name":58,"articles":59},"grammar","Grammar",6,{"slug":61,"name":62,"articles":63},"react","React",5,{"slug":65,"name":66,"articles":67},"node","Node",4,{"slug":69,"name":70,"articles":71},"ai","Ai",3,{"slug":73,"name":74,"articles":71},"html","Html",{"slug":76,"name":77,"articles":71},"tutorial","Tutorial",{"id":79,"slug":80,"title":81,"subtitle":82,"excerpt":83,"coverUrl":84,"locale":13,"readingMinutes":67,"publishedAt":85,"viewCount":86,"likeCount":19,"commentCount":19,"author":87,"vertical":92,"topic":93,"tags":95,"_count":100,"playground":102,"body":104,"bodyMd":225,"seo":226,"translationGroupId":228,"thread":229,"assessments":231,"translations":235,"quiz":237},"019ffa10-eeed-707d-9b50-7d83664c7a9a","css-trigonometric-functions-radial-layout","Stop Writing Math.cos() in a Resize Listener",null,"CSS learned trigonometry. Here's the radial menu that used to need a resize listener — and doesn't anymore.","\u002Fmedia\u002Fcovers\u002Fcss-trigonometric-functions-radial-layout.png","2026-08-17T07:03:57.776Z",28,{"id":88,"name":89,"username":90,"avatarUrl":82,"headline":91},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":45,"name":94},"CSS",[96,97,98,99],{"slug":45,"name":46,"color":82},{"slug":34,"name":35,"color":82},{"slug":42,"name":43,"color":82},{"slug":38,"name":39,"color":82},{"assessments":101},1,{"slug":80,"title":103},"CSS trig radial layout — interactive playground",{"blocks":105,"version":101},[106,110,116,119,122,127,130,133,136,139,142,146,149,152,156,159,162,165,168,171,174,177,180,183,186,189,192,195,198,201,204,207,210,213,216],{"id":107,"html":108,"type":109},"b1","\u003Cp>You&#39;ve written this function before, maybe without thinking twice about it:\u003C\u002Fp>","paragraph",{"id":111,"code":112,"type":113,"language":114,"highlight":115},"b2","function layoutRadial(container, radius) {\n  const items = [...container.children];\n  const step = (2 * Math.PI) \u002F items.length;\n\n  items.forEach((item, i) => {\n    const angle = i * step;\n    const x = Math.cos(angle) * radius;\n    const y = Math.sin(angle) * radius;\n    item.style.transform = `translate(${x}px, ${y}px)`;\n  });\n}\n\nlayoutRadial(menu, 120);\nwindow.addEventListener(\"resize\", () => layoutRadial(menu, getRadius()));","code","js",[],{"id":117,"html":118,"type":109},"b3","\u003Cp>A radial nav, a rating dial, a pie-chart label ring, a &quot;share&quot; burst menu — anything arranged in a circle gets this same loop. It works. It also means your \u003Cem>layout\u003C\u002Fem> now lives in a \u003Ccode>resize\u003C\u002Fcode> handler, re-running trig on the main thread every time the viewport twitches.\u003C\u002Fp>",{"id":120,"html":121,"type":109},"b4","\u003Cp>Here&#39;s the thing: you don&#39;t need the loop anymore. CSS does the trig now.\u003C\u002Fp>",{"id":123,"html":124,"text":125,"type":126,"level":31},"b5","The belief that made sense until it didn&#39;t","The belief that made sense until it didn't","heading",{"id":128,"html":129,"type":109},"b6","\u003Cp>&quot;CSS can&#39;t do math beyond \u003Ccode>calc()\u003C\u002Fcode> plus and minus&quot; was true for a long time, and a decade of Stack Overflow answers, boilerplate, and muscle memory got built on top of that fact. If you wanted a circle of evenly spaced elements, JavaScript was the only tool with \u003Ccode>sin\u003C\u002Fcode> and \u003Ccode>cos\u003C\u002Fcode> in it, so JavaScript computed the positions — and JavaScript re-computed them on every resize, because inline pixel values don&#39;t respond to anything on their own.\u003C\u002Fp>",{"id":131,"html":132,"type":109},"b7","\u003Cp>That belief is why the \u003Ccode>layoutRadial\u003C\u002Fcode> function above still gets written today, copy-pasted into new components, in 2026.\u003C\u002Fp>",{"id":134,"html":135,"text":135,"type":126,"level":31},"b8","What actually shipped",{"id":137,"html":138,"type":109},"b9","\u003Cp>The CSS Values and Units spec added trigonometric functions — \u003Ccode>sin()\u003C\u002Fcode>, \u003Ccode>cos()\u003C\u002Fcode>, \u003Ccode>tan()\u003C\u002Fcode>, \u003Ccode>asin()\u003C\u002Fcode>, \u003Ccode>acos()\u003C\u002Fcode>, \u003Ccode>atan()\u003C\u002Fcode>, \u003Ccode>atan2()\u003C\u002Fcode> — and they&#39;ve been in every major engine (Chrome, Firefox, Safari) since early 2023. They work inside \u003Ccode>calc()\u003C\u002Fcode>-style math anywhere a number or length is expected, and they take custom properties as input just like anything else in CSS.\u003C\u002Fp>",{"id":140,"html":141,"type":109},"b10","\u003Cp>That means the radial math itself — turn an index into an angle, turn an angle into an x\u002Fy offset — can live in a stylesheet:\u003C\u002Fp>",{"id":143,"code":144,"type":113,"language":45,"highlight":145},"b11",".radial-item {\n  --angle: calc(360deg \u002F var(--count) * var(--i));\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  transform:\n    translate(-50%, -50%)\n    translate(\n      calc(cos(var(--angle)) * var(--radius)),\n      calc(sin(var(--angle)) * var(--radius))\n    );\n}",[],{"id":147,"html":148,"type":109},"b12","\u003Cp>\u003Ccode>cos()\u003C\u002Fcode> and \u003Ccode>sin()\u003C\u002Fcode> return a plain number, and CSS happily multiplies that number by a length (\u003Ccode>var(--radius)\u003C\u002Fcode>) to get another length. No JavaScript touches a pixel value here — the browser&#39;s own math engine does the trig every time it lays out the page, including on resize, for free.\u003C\u002Fp>",{"id":150,"html":151,"type":109},"b13","\u003Cp>JavaScript still shows up, but only once, to give each item its index and the group its count:\u003C\u002Fp>",{"id":153,"code":154,"type":113,"language":114,"highlight":155},"b14","const items = [...menu.children];\nitems.forEach((item, i) => item.style.setProperty(\"--i\", i));\nmenu.style.setProperty(\"--count\", items.length);",[],{"id":157,"html":158,"type":109},"b15","\u003Cp>That&#39;s it. No loop over \u003Ccode>Math.cos\u003C\u002Fcode>. No resize listener. Set \u003Ccode>--radius\u003C\u002Fcode> to a \u003Ccode>clamp()\u003C\u002Fcode> with container query units — \u003Ccode>clamp(60px, 22cqi, 140px)\u003C\u002Fcode> — and the circle resizes itself when its container does, with zero JavaScript involved in the resize at all.\u003C\u002Fp>",{"id":160,"html":161,"type":109},"b16","\u003C!-- playground:start -->",{"id":163,"html":164,"text":164,"type":126,"level":31},"b17","🎮 Try it yourself",{"id":166,"html":167,"type":109},"b18","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-trigonometric-functions-radial-layout\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":169,"html":170,"type":109},"b19","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":172,"html":173,"type":109},"b20","\u003C!-- playground:end -->",{"id":175,"html":176,"type":109},"b21","\u003Cp>Try it yourself before you take my word for the &quot;no resize listener&quot; part — drag the radius and item-count sliders in the playground above and watch the circle stay perfectly spaced with nothing but CSS custom properties changing.\u003C\u002Fp>",{"id":178,"html":179,"text":179,"type":126,"level":31},"b22","Where JS still earns its keep",{"id":181,"html":182,"type":109},"b23","\u003Cp>This isn&#39;t &quot;CSS replaced JavaScript for graphics.&quot; \u003Ccode>atan2()\u003C\u002Fcode> in CSS is genuinely useful for static angle math — pointing an arrow at a fixed target, say — but CSS still has no way to read where the user&#39;s mouse is. A draggable dial that needs to convert \u003Cem>pointer position\u003C\u002Fem> into an angle still needs \u003Ccode>Math.atan2(dy, dx)\u003C\u002Fcode> in a \u003Ccode>pointermove\u003C\u002Fcode> handler, because that math depends on live input CSS can&#39;t see.\u003C\u002Fp>",{"id":184,"html":185,"type":109},"b24","\u003Cp>The honest split: \u003Cstrong>static or state-driven circular layout\u003C\u002Fstrong> — anything you can express as &quot;N items, spaced evenly, at some radius&quot; — moves to CSS entirely. \u003Cstrong>Reading a live pointer angle to drive that state\u003C\u002Fstrong> stays JavaScript&#39;s job. You still write \u003Ccode>Math.atan2\u003C\u002Fcode>, you just write it to compute \u003Cem>one number\u003C\u002Fem> (the value the user dragged to), not to reposition every element in the layout by hand.\u003C\u002Fp>",{"id":187,"html":188,"text":188,"type":126,"level":31},"b25","The one-line version",{"id":190,"html":191,"type":109},"b26","\u003Cp>If you&#39;re placing things in a circle, JavaScript&#39;s job is handing CSS an index and a count — not doing the trigonometry. The browser has had \u003Ccode>sin()\u003C\u002Fcode>, \u003Ccode>cos()\u003C\u002Fcode>, and \u003Ccode>atan2()\u003C\u002Fcode> in its stylesheet engine since 2023; if your radial layout still has a \u003Ccode>resize\u003C\u002Fcode> listener recomputing \u003Ccode>Math.cos\u003C\u002Fcode>, that&#39;s not necessary anymore, it&#39;s just what the code looked like before the platform caught up.\u003C\u002Fp>",{"id":193,"html":194,"type":109},"b27","\u003Cp>What&#39;s the oldest &quot;JS does the math, CSS just applies it&quot; pattern still sitting in your codebase? I&#39;d bet trig isn&#39;t the only one.\u003C\u002Fp>",{"id":196,"html":197,"type":109},"b28","\u003C!-- quiz:start -->",{"id":199,"html":200,"text":200,"type":126,"level":31},"b29","🧠 Test yourself",{"id":202,"html":203,"type":109},"b30","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-trigonometric-functions-radial-layout\u002Fquiz\">Take the 8-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":205,"html":206,"type":109},"b31","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":208,"html":209,"type":109},"b32","\u003C!-- quiz:end -->",{"id":211,"type":212},"b33","divider",{"id":214,"html":215,"type":109},"b34","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":217,"type":218,"items":219,"ordered":18},"b35","list",[220,221,222,223,224],"⭐ \u003Cstrong>GitHub\u003C\u002Fstrong> — follow me and star the projects: \u003Ca href=\"https:\u002F\u002Fgithub.com\u002Fparsajiravand\">github.com\u002Fparsajiravand\u003C\u002Fa>","💬 \u003Cstrong>Discord\u003C\u002Fstrong> — join the frontend best-practices community: \u003Ca href=\"https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ\">discord.gg\u002Fd9KRhuAwQ\u003C\u002Fa>","📸 \u003Cstrong>Instagram\u003C\u002Fstrong> — frontend best practices, daily: \u003Ca href=\"https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F\">@bestpractice___\u003C\u002Fa>","💼 \u003Cstrong>LinkedIn\u003C\u002Fstrong> — \u003Ca href=\"https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F\">linkedin.com\u002Fin\u002Fparsa-jiravand\u003C\u002Fa>","✉️ \u003Cstrong>Email\u003C\u002Fstrong> (work &amp; contract inquiries): \u003Ca href=\"mailto:bestpractice2026@gmail.com\">bestpractice2026@gmail.com\u003C\u002Fa>","You've written this function before, maybe without thinking twice about it:\n\n```js\nfunction layoutRadial(container, radius) {\n  const items = [...container.children];\n  const step = (2 * Math.PI) \u002F items.length;\n\n  items.forEach((item, i) => {\n    const angle = i * step;\n    const x = Math.cos(angle) * radius;\n    const y = Math.sin(angle) * radius;\n    item.style.transform = `translate(${x}px, ${y}px)`;\n  });\n}\n\nlayoutRadial(menu, 120);\nwindow.addEventListener(\"resize\", () => layoutRadial(menu, getRadius()));\n```\n\nA radial nav, a rating dial, a pie-chart label ring, a \"share\" burst menu — anything arranged in a circle gets this same loop. It works. It also means your *layout* now lives in a `resize` handler, re-running trig on the main thread every time the viewport twitches.\n\nHere's the thing: you don't need the loop anymore. CSS does the trig now.\n\n## The belief that made sense until it didn't\n\n\"CSS can't do math beyond `calc()` plus and minus\" was true for a long time, and a decade of Stack Overflow answers, boilerplate, and muscle memory got built on top of that fact. If you wanted a circle of evenly spaced elements, JavaScript was the only tool with `sin` and `cos` in it, so JavaScript computed the positions — and JavaScript re-computed them on every resize, because inline pixel values don't respond to anything on their own.\n\nThat belief is why the `layoutRadial` function above still gets written today, copy-pasted into new components, in 2026.\n\n## What actually shipped\n\nThe CSS Values and Units spec added trigonometric functions — `sin()`, `cos()`, `tan()`, `asin()`, `acos()`, `atan()`, `atan2()` — and they've been in every major engine (Chrome, Firefox, Safari) since early 2023. They work inside `calc()`-style math anywhere a number or length is expected, and they take custom properties as input just like anything else in CSS.\n\nThat means the radial math itself — turn an index into an angle, turn an angle into an x\u002Fy offset — can live in a stylesheet:\n\n```css\n.radial-item {\n  --angle: calc(360deg \u002F var(--count) * var(--i));\n  position: absolute;\n  top: 50%;\n  left: 50%;\n  transform:\n    translate(-50%, -50%)\n    translate(\n      calc(cos(var(--angle)) * var(--radius)),\n      calc(sin(var(--angle)) * var(--radius))\n    );\n}\n```\n\n`cos()` and `sin()` return a plain number, and CSS happily multiplies that number by a length (`var(--radius)`) to get another length. No JavaScript touches a pixel value here — the browser's own math engine does the trig every time it lays out the page, including on resize, for free.\n\nJavaScript still shows up, but only once, to give each item its index and the group its count:\n\n```js\nconst items = [...menu.children];\nitems.forEach((item, i) => item.style.setProperty(\"--i\", i));\nmenu.style.setProperty(\"--count\", items.length);\n```\n\nThat's it. No loop over `Math.cos`. No resize listener. Set `--radius` to a `clamp()` with container query units — `clamp(60px, 22cqi, 140px)` — and the circle resizes itself when its container does, with zero JavaScript involved in the resize at all.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-trigonometric-functions-radial-layout\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\nTry it yourself before you take my word for the \"no resize listener\" part — drag the radius and item-count sliders in the playground above and watch the circle stay perfectly spaced with nothing but CSS custom properties changing.\n\n## Where JS still earns its keep\n\nThis isn't \"CSS replaced JavaScript for graphics.\" `atan2()` in CSS is genuinely useful for static angle math — pointing an arrow at a fixed target, say — but CSS still has no way to read where the user's mouse is. A draggable dial that needs to convert *pointer position* into an angle still needs `Math.atan2(dy, dx)` in a `pointermove` handler, because that math depends on live input CSS can't see.\n\nThe honest split: **static or state-driven circular layout** — anything you can express as \"N items, spaced evenly, at some radius\" — moves to CSS entirely. **Reading a live pointer angle to drive that state** stays JavaScript's job. You still write `Math.atan2`, you just write it to compute *one number* (the value the user dragged to), not to reposition every element in the layout by hand.\n\n## The one-line version\n\nIf you're placing things in a circle, JavaScript's job is handing CSS an index and a count — not doing the trigonometry. The browser has had `sin()`, `cos()`, and `atan2()` in its stylesheet engine since 2023; if your radial layout still has a `resize` listener recomputing `Math.cos`, that's not necessary anymore, it's just what the code looked like before the platform caught up.\n\nWhat's the oldest \"JS does the math, CSS just applies it\" pattern still sitting in your codebase? I'd bet trig isn't the only one.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 8-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-trigonometric-functions-radial-layout\u002Fquiz)**\n\n_Instant feedback, a hint on every question, and an explanation for each answer — right or wrong._\n\n\u003C!-- quiz:end -->\n\n---\n\n*Thanks for reading! Let's stay connected:*\n\n- ⭐ **GitHub** — follow me and star the projects: [github.com\u002Fparsajiravand](https:\u002F\u002Fgithub.com\u002Fparsajiravand)\n- 💬 **Discord** — join the frontend best-practices community: [discord.gg\u002Fd9KRhuAwQ](https:\u002F\u002Fdiscord.gg\u002Fd9KRhuAwQ)\n- 📸 **Instagram** — frontend best practices, daily: [@bestpractice___](https:\u002F\u002Fwww.instagram.com\u002Fbestpractice___\u002F)\n- 💼 **LinkedIn** — [linkedin.com\u002Fin\u002Fparsa-jiravand](https:\u002F\u002Fwww.linkedin.com\u002Fin\u002Fparsa-jiravand\u002F)\n- ✉️ **Email** (work & contract inquiries): [bestpractice2026@gmail.com](mailto:bestpractice2026@gmail.com)",{"title":81,"canonical":227,"description":83},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-trigonometric-functions-radial-layout","019ffa10-eeed-707d-9b50-835ead5c62ea",{"id":230,"locked":18},"019ffa10-ef15-735e-9eca-479c80f2e1d0",[232],{"id":233,"slug":80,"title":81,"_count":234},"019ffa10-ef31-75e8-9806-335d2a940c89",{"questions":55},[236],{"locale":13,"slug":80},{"id":233,"slug":80,"title":81,"_count":238,"questionCount":55},{"questions":55},{"items":240,"meta":365},[241,253,273,291,311,329,347],{"id":79,"slug":80,"title":81,"subtitle":82,"excerpt":83,"coverUrl":84,"locale":13,"readingMinutes":67,"publishedAt":85,"viewCount":242,"likeCount":19,"commentCount":19,"author":243,"vertical":244,"topic":245,"tags":246,"_count":251,"playground":252,"hasQuiz":17,"hasPlayground":17},29,{"id":88,"name":89,"username":90,"avatarUrl":82,"headline":91},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":45,"name":94},[247,248,249,250],{"slug":45,"name":46,"color":82},{"slug":34,"name":35,"color":82},{"slug":42,"name":43,"color":82},{"slug":38,"name":39,"color":82},{"assessments":101},{"slug":80},{"id":254,"slug":255,"title":256,"subtitle":82,"excerpt":257,"coverUrl":258,"locale":13,"readingMinutes":59,"publishedAt":259,"viewCount":260,"likeCount":19,"commentCount":19,"author":261,"vertical":262,"topic":263,"tags":264,"_count":271,"playground":272,"hasQuiz":17,"hasPlayground":17},"019ff73a-ed77-732e-a13f-8be6d6939332","calc-size-animate-to-auto","I Can Finally Delete My max-height: 9999px Animation Hack","CSS transitions have never been able to animate to height: auto, because auto isn't a number the engine can interpolate. calc-size() and interpolate-size finally give it real numbers — no JavaScript, no magic-number ceiling.","\u002Fmedia\u002Fcovers\u002Fcalc-size-animate-to-auto.png","2026-08-16T06:46:49.217Z",146,{"id":88,"name":89,"username":90,"avatarUrl":82,"headline":91},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":45,"name":94},[265,266,267,268],{"slug":45,"name":46,"color":82},{"slug":34,"name":35,"color":82},{"slug":38,"name":39,"color":82},{"slug":269,"name":270,"color":82},"animation","Animation",{"assessments":101},{"slug":255},{"id":274,"slug":275,"title":276,"subtitle":82,"excerpt":277,"coverUrl":278,"locale":13,"readingMinutes":67,"publishedAt":279,"viewCount":280,"likeCount":19,"commentCount":19,"author":281,"vertical":282,"topic":283,"tags":284,"_count":289,"playground":290,"hasQuiz":17,"hasPlayground":17},"019ff19b-f763-70e0-abdf-eded59c8e69a","overscroll-behavior-scroll-containment","You're blocking touchmove events to contain scroll. `overscroll-behavior` does it natively.","When a scrollable container hits its edge, the page behind it starts scrolling too. Developers block this with JavaScript event handlers or body overflow tricks. overscroll-behavior is the CSS property that turns off scroll chaining in one line.","\u002Fmedia\u002Fcovers\u002Foverscroll-behavior-scroll-containment.png","2026-08-12T07:38:53.786Z",290,{"id":88,"name":89,"username":90,"avatarUrl":82,"headline":91},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":45,"name":94},[285,286,287,288],{"slug":45,"name":46,"color":82},{"slug":34,"name":35,"color":82},{"slug":38,"name":39,"color":82},{"slug":42,"name":43,"color":82},{"assessments":101},{"slug":275},{"id":292,"slug":293,"title":294,"subtitle":82,"excerpt":295,"coverUrl":296,"locale":13,"readingMinutes":67,"publishedAt":297,"viewCount":298,"likeCount":19,"commentCount":19,"author":299,"vertical":300,"topic":301,"tags":302,"_count":309,"playground":310,"hasQuiz":17,"hasPlayground":17},"019fe660-e5d0-756a-9ae2-20c6ff4c2f48","css-style-queries-react-to-custom-properties","Container queries can check more than size — style queries let components read custom property values","Size queries react to container width. Style queries react to CSS custom property values on a container ancestor — enabling component-level theming and variant switching in pure CSS, no JavaScript involved.","\u002Fmedia\u002Fcovers\u002Fcss-style-queries-react-to-custom-properties.png","2026-08-06T08:44:20.136Z",130,{"id":88,"name":89,"username":90,"avatarUrl":82,"headline":91},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":45,"name":94},[303,304,305,306],{"slug":45,"name":46,"color":82},{"slug":34,"name":35,"color":82},{"slug":38,"name":39,"color":82},{"slug":307,"name":308,"color":82},"components","Components",{"assessments":101},{"slug":293},{"id":312,"slug":313,"title":314,"subtitle":82,"excerpt":315,"coverUrl":316,"locale":13,"readingMinutes":63,"publishedAt":317,"viewCount":318,"likeCount":19,"commentCount":19,"author":319,"vertical":320,"topic":321,"tags":322,"_count":327,"playground":328,"hasQuiz":17,"hasPlayground":17},"019fe660-d6d5-7099-89bb-af60e48fcb28","content-visibility-skip-offscreen-rendering","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","2026-08-04T08:44:59.227Z",342,{"id":88,"name":89,"username":90,"avatarUrl":82,"headline":91},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":45,"name":94},[323,324,325,326],{"slug":45,"name":46,"color":82},{"slug":34,"name":35,"color":82},{"slug":53,"name":54,"color":82},{"slug":38,"name":39,"color":82},{"assessments":101},{"slug":313},{"id":330,"slug":331,"title":332,"subtitle":82,"excerpt":333,"coverUrl":334,"locale":13,"readingMinutes":63,"publishedAt":335,"viewCount":336,"likeCount":19,"commentCount":19,"author":337,"vertical":338,"topic":339,"tags":340,"_count":345,"playground":346,"hasQuiz":17,"hasPlayground":17},"019fe660-c020-760c-b4c7-46c1465e25bd","css-light-dark-function","You're writing dark-mode colors twice. `light-dark()` fixes that.","Every `@media (prefers-color-scheme: dark)` block duplicates your color definitions. `light-dark()` lets you write both values on the same line, in the same rule, with no separate block and no build step. Baseline 2024.","\u002Fmedia\u002Fcovers\u002Fcss-light-dark-function.png","2026-08-02T08:29:56.698Z",285,{"id":88,"name":89,"username":90,"avatarUrl":82,"headline":91},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":45,"name":94},[341,342,343,344],{"slug":45,"name":46,"color":82},{"slug":34,"name":35,"color":82},{"slug":38,"name":39,"color":82},{"slug":42,"name":43,"color":82},{"assessments":101},{"slug":331},{"id":348,"slug":349,"title":350,"subtitle":82,"excerpt":351,"coverUrl":352,"locale":13,"readingMinutes":63,"publishedAt":353,"viewCount":354,"likeCount":19,"commentCount":19,"author":355,"vertical":356,"topic":357,"tags":358,"_count":363,"playground":364,"hasQuiz":17,"hasPlayground":17},"019fe660-b09d-731e-b160-9e45270a5652","css-scope-native-style-scoping","CSS has no native scoping. `@scope` changes that.","BEM, CSS Modules, and scoped component styles all exist because CSS leaks — a rule written for one component can silently affect another. `@scope` gives the cascade a lower boundary so styles stay where you put them, natively, with no build step.","\u002Fmedia\u002Fcovers\u002Fcss-scope-native-style-scoping.png","2026-07-31T08:56:20.507Z",207,{"id":88,"name":89,"username":90,"avatarUrl":82,"headline":91},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":45,"name":94},[359,360,361,362],{"slug":45,"name":46,"color":82},{"slug":34,"name":35,"color":82},{"slug":38,"name":39,"color":82},{"slug":42,"name":43,"color":82},{"assessments":101},{"slug":349},{"page":101,"perPage":366,"total":47,"totalPages":71},7,"\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003Cdiv>13\u003C\u002Fdiv>\u003Cdiv>14\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">layoutRadial\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>container\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> radius\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> items \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>container\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>children\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> step \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">2\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">*\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Math\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-num\">PI\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">\u002F\u003C\u002Fspan> items\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length;\n\n  items\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">forEach\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>item\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> i\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n    \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> angle \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> i \u003Cspan class=\"shj-oper\">*\u003C\u002Fspan> step;\n    \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> x \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Math\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">cos\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>angle\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">*\u003C\u002Fspan> radius;\n    \u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> y \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-class\">Math\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">sin\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>angle\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-oper\">*\u003C\u002Fspan> radius;\n    item\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>style\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>transform \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-str\">`translate(\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>x\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">px, \u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>y\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">px)`\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\n\u003Cspan class=\"shj-func\">layoutRadial\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>menu\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-num\">120\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\nwindow\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"resize\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> \u003Cspan class=\"shj-func\">layoutRadial\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>menu\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> \u003Cspan class=\"shj-func\">getRadius\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-css shj-multiline\" data-lang=\"css\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003Cdiv>9\u003C\u002Fdiv>\u003Cdiv>10\u003C\u002Fdiv>\u003Cdiv>11\u003C\u002Fdiv>\u003Cdiv>12\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-var\">.radial-item\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  --\u003Cspan class=\"shj-var\">angle\u003C\u002Fspan>: \u003Cspan class=\"shj-func\">calc\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-num\">360\u003C\u002Fspan>deg \u002F \u003Cspan class=\"shj-func\">var\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>--\u003Cspan class=\"shj-num\">count\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> * \u003Cspan class=\"shj-func\">var\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>--\u003Cspan class=\"shj-num\">i\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-var\">position\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">absolute\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-var\">top\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">50\u003C\u002Fspan>\u003Cspan class=\"shj-var\">%\u003C\u002Fspan>\u003Cspan class=\"shj-num\">\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-var\">left\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">50\u003C\u002Fspan>\u003Cspan class=\"shj-var\">%\u003C\u002Fspan>\u003Cspan class=\"shj-num\">\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-var\">transform\u003C\u002Fspan>:\n    \u003Cspan class=\"shj-func\">translate\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>-\u003Cspan class=\"shj-num\">50\u003C\u002Fspan>\u003Cspan class=\"shj-var\">%\u003C\u002Fspan>\u003Cspan class=\"shj-num\">\u003C\u002Fspan>, -\u003Cspan class=\"shj-num\">50\u003C\u002Fspan>\u003Cspan class=\"shj-var\">%\u003C\u002Fspan>\u003Cspan class=\"shj-num\">\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n    \u003Cspan class=\"shj-func\">translate\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\n      \u003Cspan class=\"shj-func\">calc\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-func\">cos\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-func\">var\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>--\u003Cspan class=\"shj-num\">angle\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> * \u003Cspan class=\"shj-func\">var\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>--\u003Cspan class=\"shj-num\">radius\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>,\n      \u003Cspan class=\"shj-func\">calc\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-func\">sin\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-func\">var\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>--\u003Cspan class=\"shj-num\">angle\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> * \u003Cspan class=\"shj-func\">var\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>--\u003Cspan class=\"shj-num\">radius\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\n    \u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>","\u003Cdiv class=\"shj shj-lang-js shj-multiline\" data-lang=\"js\">\u003Cdiv class=\"shj-scroll\">\u003Cdiv class=\"shj-numbers\">\u003Cdiv>1\u003C\u002Fdiv>\u003Cdiv>2\u003C\u002Fdiv>\u003Cdiv>3\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">const\u003C\u002Fspan> items \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">...\u003C\u002Fspan>menu\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>children\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>;\nitems\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">forEach\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>item\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> i\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-kwd\">=&gt;\u003C\u002Fspan> item\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>style\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">setProperty\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"--i\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> i\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\nmenu\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>style\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">setProperty\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"--count\"\u003C\u002Fspan>\u003Cspan class=\"shj-oper\">,\u003C\u002Fspan> items\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>length\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>;\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>"]