[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"article-css-field-sizing-auto-grow-textarea":44,"search-suggestions":231,"related-css-field-sizing-auto-grow-textarea":267,"code:js:true:a1nhzm":394,"code:js:true:jujb5r":395,"code:css:true:kyink":396,"code:css:true:1oudd7c":397,"code:css:true:15it0zx":398,"comments-01a02319-4485-7310-b494-dc94bdbfb49c":399},[4,20,32],{"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,{"id":33,"slug":34,"name":35,"tagline":36,"description":37,"accentFrom":38,"accentTo":39,"icon":40,"defaultLocale":13,"locales":41,"features":42,"position":43},"7b3c16f2-931d-410e-802e-e1fa4edab7de","soft","Soft Skills","The half of the job nobody wrote documentation for.","Weekly, on the parts of working life that decide more than your code does — first weeks, meetings, interviews, promotions, and the people around you. Written from what actually happens, and recorded as a podcast you can listen to on the walk.","emerald-400","teal-300","◉",[13],{"courses":18,"paths":18,"articles":17,"exams":18,"flashcards":18,"packages":18,"community":17,"certificates":18,"teams":18,"commerce":18},3,{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":53,"likeCount":19,"commentCount":19,"author":54,"vertical":59,"topic":60,"tags":63,"_count":75,"playground":77,"body":79,"bodyMd":215,"seo":216,"translationGroupId":218,"series":48,"verticalId":5,"thread":219,"assessments":221,"translations":227,"quiz":229},"01a02319-4485-7310-b494-dc94bdbfb49c","css-field-sizing-auto-grow-textarea","Delete Your Auto-Resize Textarea JS. CSS Does It Now.",null,"The scrollHeight hack everyone copy-pastes for auto-growing textareas has a one-line CSS replacement — plus the one gotcha that bites if you skip max-height.","\u002Fmedia\u002Fcovers\u002Fcss-field-sizing-auto-grow-textarea.png",5,"2026-08-26T06:56:38.974Z",58,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},"css","CSS",[64,66,69,72],{"slug":61,"name":65,"color":48},"Css",{"slug":67,"name":68,"color":48},"webdev","Webdev",{"slug":70,"name":71,"color":48},"frontend","Frontend",{"slug":73,"name":74,"color":48},"javascript","Javascript",{"assessments":76},1,{"slug":46,"title":78},"field-sizing: content — auto-grow textarea playground",{"blocks":80,"version":76},[81,85,91,94,98,101,105,108,116,119,122,126,129,132,136,139,142,145,148,152,155,158,161,164,167,170,173,176,179,182,185,188,191,194,197,200,203,206,209],{"id":82,"html":83,"type":84},"b1","\u003Cp>Open any comment box you&#39;ve built in the last five years and there&#39;s a decent chance this is sitting in a \u003Ccode>useEffect\u003C\u002Fcode> or an \u003Ccode>input\u003C\u002Fcode> listener:\u003C\u002Fp>","paragraph",{"id":86,"code":87,"type":88,"language":89,"highlight":90},"b2","textarea.style.height = \"auto\";\ntextarea.style.height = `${textarea.scrollHeight}px`;","code","js",[],{"id":92,"html":93,"type":84},"b3","\u003Cp>Two lines. Copy-pasted from a Stack Overflow answer, a gist, or your own last project. It&#39;s the unofficial industry-standard way to make a \u003Ccode>&lt;textarea&gt;\u003C\u002Fcode> grow as someone types, because \u003Ccode>&lt;textarea&gt;\u003C\u002Fcode> has never — not once, in thirty years of HTML — known how to size itself to its own content. Until now it does, and most people haven&#39;t heard.\u003C\u002Fp>",{"id":95,"html":96,"text":96,"type":97,"level":31},"b4","Why the two-liner is worse than it looks","heading",{"id":99,"html":100,"type":84},"b5","\u003Cp>Here&#39;s the fuller version most people actually ship, because the two-liner alone doesn&#39;t hold up:\u003C\u002Fp>",{"id":102,"code":103,"type":88,"language":89,"highlight":104},"b6","function autoGrow(el) {\n  el.style.height = \"auto\";\n  el.style.height = `${el.scrollHeight}px`;\n}\n\ntextarea.addEventListener(\"input\", () => autoGrow(textarea));\nwindow.addEventListener(\"resize\", () => autoGrow(textarea)); \u002F\u002F width changes reflow the text\nautoGrow(textarea); \u002F\u002F run once for pre-filled content",[],{"id":106,"html":107,"type":84},"b7","\u003Cp>Reset the height to \u003Ccode>auto\u003C\u002Fcode>, read \u003Ccode>scrollHeight\u003C\u002Fcode>, write it back. Do that on every keystroke. It works, but notice what &quot;works&quot; is quietly costing you:\u003C\u002Fp>",{"id":109,"type":110,"items":111,"ordered":18},"b8","list",[112,113,114,115],"\u003Cstrong>Two forced reflows per keystroke.\u003C\u002Fstrong> Setting \u003Ccode>height: auto\u003C\u002Fcode> invalidates layout; reading \u003Ccode>scrollHeight\u003C\u002Fcode> forces the browser to recompute it \u003Cem>synchronously\u003C\u002Fem> before it&#39;ll give you the number. On a slow device, in a form with several of these, that&#39;s measurable jank while someone&#39;s just trying to type.","\u003Cstrong>A visible flicker.\u003C\u002Fstrong> Reset-then-remeasure means the box visibly shrinks for one frame before snapping back, especially noticeable on a fast paste of several paragraphs.","\u003Cstrong>You have to remember the resize listener.\u003C\u002Fstrong> Miss it, and resizing the browser window leaves stale heights until the next keystroke — a bug that never shows up in your testing because you never resize your own window mid-demo.","\u003Cstrong>It still doesn&#39;t handle a \u003Ccode>&lt;input type=&quot;text&quot;&gt;\u003C\u002Fcode> growing with its content\u003C\u002Fstrong>, which is the same underlying problem in a different tag. You&#39;d write a second version of this hack.",{"id":117,"html":118,"type":84},"b9","\u003Cp>None of this is a bug in your code. It&#39;s the ceiling of what&#39;s possible when the browser gives you no way to say &quot;size to content&quot; and JavaScript has to fake it by measuring pixels after the fact.\u003C\u002Fp>",{"id":120,"html":121,"text":121,"type":97,"level":31},"b10","The one-line fix",{"id":123,"code":124,"type":88,"language":61,"highlight":125},"b11","textarea {\n  field-sizing: content;\n}",[],{"id":127,"html":128,"type":84},"b12","\u003Cp>That&#39;s the whole thing. No listener, no \u003Ccode>scrollHeight\u003C\u002Fcode>, no reflow-and-flicker dance. \u003Ccode>field-sizing\u003C\u002Fcode> is a CSS property that changes how form controls compute their intrinsic size: instead of the browser guessing a fixed box from \u003Ccode>rows\u003C\u002Fcode>\u002F\u003Ccode>cols\u003C\u002Fcode> (or a fixed width for \u003Ccode>&lt;input&gt;\u003C\u002Fcode>), \u003Ccode>content\u003C\u002Fcode> tells it to size the box to fit what&#39;s actually typed, and grow or shrink live as the user edits.\u003C\u002Fp>",{"id":130,"html":131,"type":84},"b13","\u003Cp>It isn&#39;t limited to \u003Ccode>&lt;textarea&gt;\u003C\u002Fcode>. Point it at a text \u003Ccode>&lt;input&gt;\u003C\u002Fcode> and the field widens as someone types, the same trick people currently fake with a hidden \u003Ccode>&lt;span&gt;\u003C\u002Fcode> measuring text width behind the scenes:\u003C\u002Fp>",{"id":133,"code":134,"type":88,"language":61,"highlight":135},"b14","input[type=\"text\"].auto-width {\n  field-sizing: content;\n}",[],{"id":137,"html":138,"type":84},"b15","\u003Cp>As of mid-2026 this is \u003Ca href=\"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FCSS\u002FReference\u002FProperties\u002Ffield-sizing\">Baseline &quot;Newly available&quot;\u003C\u002Fa> — it started in Chromium browsers in 2024 and has since landed in the current stable release of every core engine, Firefox and Safari included. &quot;Newly available&quot; is the first rung on Baseline&#39;s ladder, though, not the last: it means the latest stable version of each browser understands it, not that every visitor is already on that version. If you need to support someone on an older browser, that&#39;s exactly what a plain, unstyled textarea already does — it just won&#39;t auto-grow, and nothing breaks. Ship it behind nothing and it degrades to the old fixed-size box on its own.\u003C\u002Fp>",{"id":140,"html":141,"text":141,"type":97,"level":31},"b16","The gotcha nobody mentions in the one-liner",{"id":143,"html":144,"type":84},"b17","\u003Cp>Here&#39;s the part that bites people who copy just the CSS and stop reading: \u003Ccode>field-sizing: content\u003C\u002Fcode> on its own means the box has \u003Cstrong>no upper bound\u003C\u002Fstrong>. Let someone paste in a full paragraph, or ten, and your neat little comment box grows into a page-length column, pushing your submit button somewhere past the fold.\u003C\u002Fp>",{"id":146,"html":147,"type":84},"b18","\u003Cp>You still need a ceiling — you just set it in CSS instead of computing it in JS:\u003C\u002Fp>",{"id":149,"code":150,"type":88,"language":61,"highlight":151},"b19","textarea {\n  field-sizing: content;\n  max-height: 12lh; \u002F* stop growing after ~12 lines *\u002F\n  overflow-y: auto; \u002F* scroll inside the box past that *\u002F\n}",[],{"id":153,"html":154,"type":84},"b20","\u003Cp>\u003Ccode>max-height\u003C\u002Fcode> caps the growth, \u003Ccode>overflow-y: auto\u003C\u002Fcode> lets the box scroll internally once it hits that cap instead of just clipping the text. \u003Ccode>12lh\u003C\u002Fcode> — twelve line-heights — is a nice unit to reach for here because it scales with the font size instead of hardcoding a pixel guess.\u003C\u002Fp>",{"id":156,"html":157,"type":84},"b21","\u003Cp>Try both settings live below: type a short reply, then paste in a wall of text, and watch where the ceiling kicks in. Then flip the toggle to see the exact JS hack this replaces, side by side.\u003C\u002Fp>",{"id":159,"html":160,"type":84},"b22","\u003C!-- playground:start -->",{"id":162,"html":163,"text":163,"type":97,"level":31},"b23","🎮 Try it yourself",{"id":165,"html":166,"type":84},"b24","\u003Cp>\u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-field-sizing-auto-grow-textarea\u002Fplayground\">▶️ Open the interactive playground →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":168,"html":169,"type":84},"b25","\u003Cp>\u003Cem>Runs right in your browser — poke at it and watch the concept react live.\u003C\u002Fem>\u003C\u002Fp>",{"id":171,"html":172,"type":84},"b26","\u003C!-- playground:end -->",{"id":174,"html":175,"text":175,"type":97,"level":31},"b27","One property, one caveat",{"id":177,"html":178,"type":84},"b28","\u003Cp>\u003Ccode>field-sizing: content\u003C\u002Fcode> replaces a hack that&#39;s been copy-pasted into countless codebases since \u003Ccode>&lt;textarea&gt;\u003C\u002Fcode> shipped without an opinion on its own size. It removes a resize listener, two forced reflows per keystroke, and a visible flicker on paste — for the cost of one CSS declaration and remembering to pair it with \u003Ccode>max-height\u003C\u002Fcode>.\u003C\u002Fp>",{"id":180,"html":181,"type":84},"b29","\u003Cp>If you&#39;re still shipping the \u003Ccode>scrollHeight\u003C\u002Fcode> version because &quot;it works,&quot; it does — but so did jQuery \u003Ccode>.animate()\u003C\u002Fcode> before \u003Ccode>transition\u003C\u002Fcode> existed. Go check whether the browsers you actually support have caught up, and see how much JS you get to delete.\u003C\u002Fp>",{"id":183,"html":184,"type":84},"b30","\u003Cp>What&#39;s the oldest DOM-measuring hack you&#39;re still maintaining that you suspect CSS has quietly solved by now? Drop it below — I&#39;ll tell you if there&#39;s already a property for it.\u003C\u002Fp>",{"id":186,"html":187,"type":84},"b31","\u003C!-- quiz:start -->",{"id":189,"html":190,"text":190,"type":97,"level":31},"b32","🧠 Test yourself",{"id":192,"html":193,"type":84},"b33","\u003Cp>Think it clicked? \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-field-sizing-auto-grow-textarea\u002Fquiz\">Take the 7-question quiz →\u003C\u002Fa>\u003C\u002Fstrong>\u003C\u002Fp>",{"id":195,"html":196,"type":84},"b34","\u003Cp>\u003Cem>Instant feedback, a hint on every question, and an explanation for each answer — right or wrong.\u003C\u002Fem>\u003C\u002Fp>",{"id":198,"html":199,"type":84},"b35","\u003C!-- quiz:end -->",{"id":201,"type":202},"b36","divider",{"id":204,"html":205,"type":84},"b37","\u003Cp>🚀 \u003Cstrong>Want more like this?\u003C\u002Fstrong> Every guide, playground, and quiz lives on \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">bestpractic.org\u003C\u002Fa>\u003C\u002Fstrong> — open it and \u003Cstrong>\u003Ca href=\"https:\u002F\u002Fbestpractic.org\u002F\">sign up free\u003C\u002Fa>\u003C\u002Fstrong> so the next one finds you.\u003C\u002Fp>",{"id":207,"html":208,"type":84},"b38","\u003Cp>\u003Cem>Thanks for reading! Let&#39;s stay connected:\u003C\u002Fem>\u003C\u002Fp>",{"id":210,"type":110,"items":211,"ordered":18},"b39",[212,213,214],"⭐ \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>","Open any comment box you've built in the last five years and there's a decent chance this is sitting in a `useEffect` or an `input` listener:\n\n```js\ntextarea.style.height = \"auto\";\ntextarea.style.height = `${textarea.scrollHeight}px`;\n```\n\nTwo lines. Copy-pasted from a Stack Overflow answer, a gist, or your own last project. It's the unofficial industry-standard way to make a `\u003Ctextarea>` grow as someone types, because `\u003Ctextarea>` has never — not once, in thirty years of HTML — known how to size itself to its own content. Until now it does, and most people haven't heard.\n\n## Why the two-liner is worse than it looks\n\nHere's the fuller version most people actually ship, because the two-liner alone doesn't hold up:\n\n```js\nfunction autoGrow(el) {\n  el.style.height = \"auto\";\n  el.style.height = `${el.scrollHeight}px`;\n}\n\ntextarea.addEventListener(\"input\", () => autoGrow(textarea));\nwindow.addEventListener(\"resize\", () => autoGrow(textarea)); \u002F\u002F width changes reflow the text\nautoGrow(textarea); \u002F\u002F run once for pre-filled content\n```\n\nReset the height to `auto`, read `scrollHeight`, write it back. Do that on every keystroke. It works, but notice what \"works\" is quietly costing you:\n\n- **Two forced reflows per keystroke.** Setting `height: auto` invalidates layout; reading `scrollHeight` forces the browser to recompute it *synchronously* before it'll give you the number. On a slow device, in a form with several of these, that's measurable jank while someone's just trying to type.\n- **A visible flicker.** Reset-then-remeasure means the box visibly shrinks for one frame before snapping back, especially noticeable on a fast paste of several paragraphs.\n- **You have to remember the resize listener.** Miss it, and resizing the browser window leaves stale heights until the next keystroke — a bug that never shows up in your testing because you never resize your own window mid-demo.\n- **It still doesn't handle a `\u003Cinput type=\"text\">` growing with its content**, which is the same underlying problem in a different tag. You'd write a second version of this hack.\n\nNone of this is a bug in your code. It's the ceiling of what's possible when the browser gives you no way to say \"size to content\" and JavaScript has to fake it by measuring pixels after the fact.\n\n## The one-line fix\n\n```css\ntextarea {\n  field-sizing: content;\n}\n```\n\nThat's the whole thing. No listener, no `scrollHeight`, no reflow-and-flicker dance. `field-sizing` is a CSS property that changes how form controls compute their intrinsic size: instead of the browser guessing a fixed box from `rows`\u002F`cols` (or a fixed width for `\u003Cinput>`), `content` tells it to size the box to fit what's actually typed, and grow or shrink live as the user edits.\n\nIt isn't limited to `\u003Ctextarea>`. Point it at a text `\u003Cinput>` and the field widens as someone types, the same trick people currently fake with a hidden `\u003Cspan>` measuring text width behind the scenes:\n\n```css\ninput[type=\"text\"].auto-width {\n  field-sizing: content;\n}\n```\n\nAs of mid-2026 this is [Baseline \"Newly available\"](https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FCSS\u002FReference\u002FProperties\u002Ffield-sizing) — it started in Chromium browsers in 2024 and has since landed in the current stable release of every core engine, Firefox and Safari included. \"Newly available\" is the first rung on Baseline's ladder, though, not the last: it means the latest stable version of each browser understands it, not that every visitor is already on that version. If you need to support someone on an older browser, that's exactly what a plain, unstyled textarea already does — it just won't auto-grow, and nothing breaks. Ship it behind nothing and it degrades to the old fixed-size box on its own.\n\n## The gotcha nobody mentions in the one-liner\n\nHere's the part that bites people who copy just the CSS and stop reading: `field-sizing: content` on its own means the box has **no upper bound**. Let someone paste in a full paragraph, or ten, and your neat little comment box grows into a page-length column, pushing your submit button somewhere past the fold.\n\nYou still need a ceiling — you just set it in CSS instead of computing it in JS:\n\n```css\ntextarea {\n  field-sizing: content;\n  max-height: 12lh; \u002F* stop growing after ~12 lines *\u002F\n  overflow-y: auto; \u002F* scroll inside the box past that *\u002F\n}\n```\n\n`max-height` caps the growth, `overflow-y: auto` lets the box scroll internally once it hits that cap instead of just clipping the text. `12lh` — twelve line-heights — is a nice unit to reach for here because it scales with the font size instead of hardcoding a pixel guess.\n\nTry both settings live below: type a short reply, then paste in a wall of text, and watch where the ceiling kicks in. Then flip the toggle to see the exact JS hack this replaces, side by side.\n\n\u003C!-- playground:start -->\n\n## 🎮 Try it yourself\n\n**[▶️ Open the interactive playground →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-field-sizing-auto-grow-textarea\u002Fplayground)**\n\n_Runs right in your browser — poke at it and watch the concept react live._\n\n\u003C!-- playground:end -->\n\n## One property, one caveat\n\n`field-sizing: content` replaces a hack that's been copy-pasted into countless codebases since `\u003Ctextarea>` shipped without an opinion on its own size. It removes a resize listener, two forced reflows per keystroke, and a visible flicker on paste — for the cost of one CSS declaration and remembering to pair it with `max-height`.\n\nIf you're still shipping the `scrollHeight` version because \"it works,\" it does — but so did jQuery `.animate()` before `transition` existed. Go check whether the browsers you actually support have caught up, and see how much JS you get to delete.\n\nWhat's the oldest DOM-measuring hack you're still maintaining that you suspect CSS has quietly solved by now? Drop it below — I'll tell you if there's already a property for it.\n\n\u003C!-- quiz:start -->\n\n## 🧠 Test yourself\n\nThink it clicked? **[Take the 7-question quiz →](https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-field-sizing-auto-grow-textarea\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🚀 **Want more like this?** Every guide, playground, and quiz lives on **[bestpractic.org](https:\u002F\u002Fbestpractic.org\u002F)** — open it and **[sign up free](https:\u002F\u002Fbestpractic.org\u002F)** so the next one finds you.\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)",{"title":47,"canonical":217,"description":49},"https:\u002F\u002Fbestpractic.org\u002Fblog\u002Fcss-field-sizing-auto-grow-textarea","01a02319-4485-7310-b494-e3a1bcd79e46",{"id":220,"locked":18},"01a02319-44a2-718e-b30d-d5e958eed432",[222],{"id":223,"slug":46,"title":224,"_count":225},"01a02319-44bb-700b-b96b-63603a7d7ab8","field-sizing: content — auto-growing form fields",{"questions":226},7,[228],{"locale":13,"slug":46},{"id":223,"slug":46,"title":224,"_count":230,"questionCount":226},{"questions":226},[232,234,236,237,239,243,247,250,254,257,261,264],{"slug":67,"name":68,"articles":233},68,{"slug":70,"name":71,"articles":235},59,{"slug":73,"name":74,"articles":53},{"slug":61,"name":65,"articles":238},26,{"slug":240,"name":241,"articles":242},"typescript","Typescript",10,{"slug":244,"name":245,"articles":246},"performance","Performance",9,{"slug":248,"name":249,"articles":226},"tutorial","Tutorial",{"slug":251,"name":252,"articles":253},"grammar","Grammar",6,{"slug":255,"name":256,"articles":51},"react","React",{"slug":258,"name":259,"articles":260},"html","Html",4,{"slug":262,"name":263,"articles":260},"node","Node",{"slug":265,"name":266,"articles":260},"programming","Programming",{"items":268,"meta":393},[269,280,299,317,337,355,373],{"id":45,"slug":46,"title":47,"subtitle":48,"excerpt":49,"coverUrl":50,"locale":13,"readingMinutes":51,"publishedAt":52,"viewCount":235,"likeCount":19,"commentCount":19,"author":270,"vertical":271,"topic":272,"tags":273,"_count":278,"playground":279,"hasQuiz":17,"hasPlayground":17},{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[274,275,276,277],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":73,"name":74,"color":48},{"assessments":76},{"slug":46},{"id":281,"slug":282,"title":283,"subtitle":48,"excerpt":284,"coverUrl":285,"locale":13,"readingMinutes":51,"publishedAt":286,"viewCount":287,"likeCount":19,"commentCount":19,"author":288,"vertical":289,"topic":290,"tags":292,"_count":297,"playground":298,"hasQuiz":17,"hasPlayground":17},"01a01df1-d6d5-71e8-bba6-1242e581730b","web-animations-api-imperative-control","CSS Animations Can't Pause. The Web Animations API Can.","Toggling a class to restart a CSS animation works — until you need to pause it, reverse it mid-flight, or ask how far through it is. element.animate() returns an object with those methods built in, natively, in every browser you already support.","\u002Fmedia\u002Fcovers\u002Fweb-animations-api-imperative-control.png","2026-08-25T06:55:40.108Z",114,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":73,"name":291},"JavaScript",[293,294,295,296],{"slug":73,"name":74,"color":48},{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"assessments":76},{"slug":282},{"id":300,"slug":301,"title":302,"subtitle":48,"excerpt":303,"coverUrl":304,"locale":13,"readingMinutes":51,"publishedAt":305,"viewCount":306,"likeCount":19,"commentCount":19,"author":307,"vertical":308,"topic":309,"tags":310,"_count":315,"playground":316,"hasQuiz":17,"hasPlayground":17},"01a018ca-59cf-75a0-b0c2-ba8ca95ade59","css-where-is-specificity-control","The CSS Selector Built to Lose Every Fight","You keep reaching for !important to override a component's styles. There's a selector that fixes this by contributing zero specificity, on purpose.","\u002Fmedia\u002Fcovers\u002Fcss-where-is-specificity-control.png","2026-08-24T07:06:37.051Z",214,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[311,312,313,314],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":265,"name":266,"color":48},{"assessments":76},{"slug":301},{"id":318,"slug":319,"title":320,"subtitle":48,"excerpt":321,"coverUrl":322,"locale":13,"readingMinutes":260,"publishedAt":323,"viewCount":324,"likeCount":19,"commentCount":19,"author":325,"vertical":326,"topic":327,"tags":328,"_count":335,"playground":336,"hasQuiz":17,"hasPlayground":17},"01a0042a-852e-7338-83ed-a1f581d6c501","css-clamp-fluid-typography","Stop Writing Media Queries for Font Size","A design system audit found five breakpoints for one heading. clamp() replaces all of them with one line that never jumps.","\u002Fmedia\u002Fcovers\u002Fcss-clamp-fluid-typography.png","2026-08-19T06:51:54.290Z",237,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[329,330,331,332],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":333,"name":334,"color":48},"accessibility","Accessibility",{"assessments":76},{"slug":319},{"id":338,"slug":339,"title":340,"subtitle":48,"excerpt":341,"coverUrl":342,"locale":13,"readingMinutes":260,"publishedAt":343,"viewCount":344,"likeCount":19,"commentCount":19,"author":345,"vertical":346,"topic":347,"tags":348,"_count":353,"playground":354,"hasQuiz":17,"hasPlayground":17},"019fff35-4908-727e-b7a3-dafd1ada4784","css-scroll-snap-native-carousel","I Ripped Out a Carousel Library. CSS Replaced It.","A scroll listener, a debounce, and 200 lines of carousel-library glue code — replaced by three CSS properties that snap smoother than any of it did.","\u002Fmedia\u002Fcovers\u002Fcss-scroll-snap-native-carousel.png","2026-08-18T06:50:52.267Z",383,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[349,350,351,352],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":248,"name":249,"color":48},{"assessments":76},{"slug":339},{"id":356,"slug":357,"title":358,"subtitle":48,"excerpt":359,"coverUrl":360,"locale":13,"readingMinutes":260,"publishedAt":361,"viewCount":362,"likeCount":19,"commentCount":19,"author":363,"vertical":364,"topic":365,"tags":366,"_count":371,"playground":372,"hasQuiz":17,"hasPlayground":17},"019ffa10-eeed-707d-9b50-7d83664c7a9a","css-trigonometric-functions-radial-layout","Stop Writing Math.cos() in a Resize Listener","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",441,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[367,368,369,370],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":73,"name":74,"color":48},{"slug":70,"name":71,"color":48},{"assessments":76},{"slug":357},{"id":374,"slug":375,"title":376,"subtitle":48,"excerpt":377,"coverUrl":378,"locale":13,"readingMinutes":253,"publishedAt":379,"viewCount":380,"likeCount":19,"commentCount":19,"author":381,"vertical":382,"topic":383,"tags":384,"_count":391,"playground":392,"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",410,{"id":55,"name":56,"username":57,"avatarUrl":48,"headline":58},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":61,"name":62},[385,386,387,388],{"slug":61,"name":65,"color":48},{"slug":67,"name":68,"color":48},{"slug":70,"name":71,"color":48},{"slug":389,"name":390,"color":48},"animation","Animation",{"assessments":76},{"slug":375},{"page":76,"perPage":226,"total":238,"totalPages":260},"\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>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">textarea\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>style\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>height \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"auto\"\u003C\u002Fspan>;\ntextarea\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>style\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>height \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>textarea\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>scrollHeight\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">px`\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>\u003Cdiv>4\u003C\u002Fdiv>\u003Cdiv>5\u003C\u002Fdiv>\u003Cdiv>6\u003C\u002Fdiv>\u003Cdiv>7\u003C\u002Fdiv>\u003Cdiv>8\u003C\u002Fdiv>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">function\u003C\u002Fspan> \u003Cspan class=\"shj-func\">autoGrow\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>el\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  el\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>style\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>height \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-str\">\"auto\"\u003C\u002Fspan>;\n  el\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>style\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>height \u003Cspan class=\"shj-oper\">=\u003C\u002Fspan> \u003Cspan class=\"shj-str\">`\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">${\u003C\u002Fspan>el\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>scrollHeight\u003Cspan class=\"shj-kwd\">}\u003C\u002Fspan>\u003Cspan class=\"shj-str\">px`\u003C\u002Fspan>;\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\n\ntextarea\u003Cspan class=\"shj-oper\">.\u003C\u002Fspan>\u003Cspan class=\"shj-func\">addEventListener\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>\u003Cspan class=\"shj-str\">\"input\"\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\">autoGrow\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>textarea\u003Cspan class=\"shj-bracket\">)\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\">autoGrow\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>textarea\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F width changes reflow the text\n\u003C\u002Fspan>\u003Cspan class=\"shj-func\">autoGrow\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">(\u003C\u002Fspan>textarea\u003Cspan class=\"shj-bracket\">)\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F\u002F run once for pre-filled content\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>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">textarea\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">field-sizing\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">content\u003C\u002Fspan>;\n\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>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">input\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">[\u003C\u002Fspan>\u003Cspan class=\"shj-kwd\">type\u003C\u002Fspan>=\u003Cspan class=\"shj-str\">\"text\"\u003C\u002Fspan>\u003Cspan class=\"shj-bracket\">]\u003C\u002Fspan>\u003Cspan class=\"shj-var\">.auto-width\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">field-sizing\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">content\u003C\u002Fspan>;\n\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>\u003C\u002Fdiv>\u003Cdiv class=\"shj-code\">\u003Cspan class=\"shj-kwd\">textarea\u003C\u002Fspan> \u003Cspan class=\"shj-bracket\">{\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">field-sizing\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">content\u003C\u002Fspan>;\n  \u003Cspan class=\"shj-var\">max-height\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">12\u003C\u002Fspan>lh; \u003Cspan class=\"shj-cmnt\">\u002F* stop growing after ~12 lines *\u002F\u003C\u002Fspan>\n  \u003Cspan class=\"shj-var\">overflow-y\u003C\u002Fspan>: \u003Cspan class=\"shj-num\">auto\u003C\u002Fspan>; \u003Cspan class=\"shj-cmnt\">\u002F* scroll inside the box past that *\u002F\u003C\u002Fspan>\n\u003Cspan class=\"shj-bracket\">}\u003C\u002Fspan>\u003C\u002Fdiv>\u003C\u002Fdiv>\u003C\u002Fdiv>",{"locked":18,"total":19,"comments":400},[]]