[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"verticals":3,"search-suggestions":44,"tag-webdev-6":91,"tags-all":331},[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,[45,49,53,57,61,65,69,73,77,81,84,88],{"slug":46,"name":47,"articles":48},"webdev","Webdev",107,{"slug":50,"name":51,"articles":52},"javascript","Javascript",89,{"slug":54,"name":55,"articles":56},"frontend","Frontend",72,{"slug":58,"name":59,"articles":60},"css","Css",36,{"slug":62,"name":63,"articles":64},"tutorial","Tutorial",35,{"slug":66,"name":67,"articles":68},"typescript","Typescript",15,{"slug":70,"name":71,"articles":72},"react","React",13,{"slug":74,"name":75,"articles":76},"performance","Performance",12,{"slug":78,"name":79,"articles":80},"browser","Browser",9,{"slug":82,"name":83,"articles":80},"node","Node",{"slug":85,"name":86,"articles":87},"html","Html",7,{"slug":89,"name":90,"articles":87},"accessibility","Accessibility",{"items":92,"meta":329},[93,119,138,159,177,198,218,236,257,275,293,311],{"id":94,"slug":95,"title":96,"subtitle":97,"excerpt":98,"coverUrl":99,"locale":13,"readingMinutes":100,"publishedAt":101,"viewCount":102,"likeCount":19,"commentCount":19,"author":103,"vertical":108,"topic":109,"tags":111,"_count":116,"playground":118,"hasQuiz":17,"hasPlayground":17},"019ff19b-f388-73f8-aa5b-93d73337d6fd","dialog-element-native-modal","You built your modal with a `\u003Cdiv>` and a focus trap library. The native `\u003Cdialog>` does all of that.",null,"The `\u003Cdialog>` element ships with focus trapping, Escape-to-close, backdrop rendering, and correct ARIA semantics — no library, no boilerplate. It's been Baseline since 2022.","\u002Fmedia\u002Fcovers\u002Fdialog-element-native-modal.png",5,"2026-08-08T07:00:10.810Z",459,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},"019fe637-3c25-7088-9034-39c9f15dc3c8","Parsa Jiravand","parsa","Frontend engineer · building bestpractic",{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":85,"name":110},"HTML",[112,113,114,115],{"slug":85,"name":86,"color":97},{"slug":46,"name":47,"color":97},{"slug":54,"name":55,"color":97},{"slug":50,"name":51,"color":97},{"assessments":117},1,{"slug":95},{"id":120,"slug":121,"title":122,"subtitle":97,"excerpt":123,"coverUrl":124,"locale":13,"readingMinutes":43,"publishedAt":125,"viewCount":126,"likeCount":19,"commentCount":19,"author":127,"vertical":128,"topic":129,"tags":131,"_count":136,"playground":137,"hasQuiz":17,"hasPlayground":17},"019fe660-f552-73c8-9f52-d036e672c053","crypto-randomuuid-native-uuid","Stop importing `uuid`. `crypto.randomUUID()` has been native since 2021.","The `uuid` package is downloaded hundreds of millions of times a week. Most installs exist to call uuid\u002Fv4 — exactly what `crypto.randomUUID()` does natively, with no import, no bundle cost, and guaranteed cryptographic randomness.","\u002Fmedia\u002Fcovers\u002Fcrypto-randomuuid-native-uuid.png","2026-08-08T07:00:09.988Z",515,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":130},"JavaScript",[132,133,134,135],{"slug":50,"name":51,"color":97},{"slug":46,"name":47,"color":97},{"slug":54,"name":55,"color":97},{"slug":74,"name":75,"color":97},{"assessments":117},{"slug":121},{"id":139,"slug":140,"title":141,"subtitle":97,"excerpt":142,"coverUrl":143,"locale":13,"readingMinutes":144,"publishedAt":145,"viewCount":146,"likeCount":19,"commentCount":19,"author":147,"vertical":148,"topic":149,"tags":150,"_count":157,"playground":158,"hasQuiz":17,"hasPlayground":17},"019fe660-edbb-714e-b0e8-8488ccbb6940","intl-relativetimeformat-relative-dates","You're installing date-fns for 'X hours ago'. `Intl.RelativeTimeFormat` does it natively.","Most apps display relative timestamps — '3 minutes ago', 'last week'. Developers still reach for date-fns or moment for this. The Intl.RelativeTimeFormat API handles it natively, with full locale support and zero dependencies.","\u002Fmedia\u002Fcovers\u002Fintl-relativetimeformat-relative-dates.png",4,"2026-08-07T07:22:54.352Z",341,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":130},[151,152,153,154],{"slug":50,"name":51,"color":97},{"slug":46,"name":47,"color":97},{"slug":54,"name":55,"color":97},{"slug":155,"name":156,"color":97},"intl","Intl",{"assessments":117},{"slug":140},{"id":160,"slug":161,"title":162,"subtitle":97,"excerpt":163,"coverUrl":164,"locale":13,"readingMinutes":144,"publishedAt":165,"viewCount":166,"likeCount":19,"commentCount":19,"author":167,"vertical":168,"topic":169,"tags":170,"_count":175,"playground":176,"hasQuiz":17,"hasPlayground":17},"019ff19b-f047-748a-800d-00912a4d95c9","object-hasown-safe-property-check","You use `obj.hasOwnProperty(key)` to check property ownership. It can silently fail. `Object.hasOwn()` is the safe replacement.","Two real cases where `obj.hasOwnProperty(key)` throws or returns wrong results — and why `Object.hasOwn(obj, key)` is the ES2022 fix that ESLint's `no-prototype-builtins` rule has been nudging you toward for years.","\u002Fmedia\u002Fcovers\u002Fobject-hasown-safe-property-check.png","2026-08-06T08:44:20.722Z",321,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":130},[171,172,173,174],{"slug":50,"name":51,"color":97},{"slug":46,"name":47,"color":97},{"slug":54,"name":55,"color":97},{"slug":66,"name":67,"color":97},{"assessments":117},{"slug":161},{"id":178,"slug":179,"title":180,"subtitle":97,"excerpt":181,"coverUrl":182,"locale":13,"readingMinutes":144,"publishedAt":183,"viewCount":184,"likeCount":19,"commentCount":19,"author":185,"vertical":186,"topic":187,"tags":189,"_count":196,"playground":197,"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",329,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":58,"name":188},"CSS",[190,191,192,193],{"slug":58,"name":59,"color":97},{"slug":46,"name":47,"color":97},{"slug":54,"name":55,"color":97},{"slug":194,"name":195,"color":97},"components","Components",{"assessments":117},{"slug":179},{"id":199,"slug":200,"title":201,"subtitle":97,"excerpt":202,"coverUrl":203,"locale":13,"readingMinutes":144,"publishedAt":204,"viewCount":205,"likeCount":19,"commentCount":19,"author":206,"vertical":207,"topic":208,"tags":209,"_count":216,"playground":217,"hasQuiz":17,"hasPlayground":17},"019fe660-de51-759d-935f-dbda9e7e38b5","array-from-async-async-iterable","You're collecting async iterable results with a for-await loop. `Array.fromAsync` does it in one call.","A for-await loop that pushes into an array, or the `await Promise.all([...asyncIterable])` workaround that silently fails — `Array.fromAsync` replaces both with a single awaited call. It's Baseline 2024.","\u002Fmedia\u002Fcovers\u002Farray-from-async-async-iterable.png","2026-08-05T08:42:50.920Z",561,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":130},[210,211,212,213],{"slug":50,"name":51,"color":97},{"slug":46,"name":47,"color":97},{"slug":54,"name":55,"color":97},{"slug":214,"name":215,"color":97},"async","Async",{"assessments":117},{"slug":200},{"id":219,"slug":220,"title":221,"subtitle":97,"excerpt":222,"coverUrl":223,"locale":13,"readingMinutes":100,"publishedAt":224,"viewCount":225,"likeCount":19,"commentCount":19,"author":226,"vertical":227,"topic":228,"tags":229,"_count":234,"playground":235,"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",644,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":58,"name":188},[230,231,232,233],{"slug":58,"name":59,"color":97},{"slug":46,"name":47,"color":97},{"slug":74,"name":75,"color":97},{"slug":54,"name":55,"color":97},{"assessments":117},{"slug":220},{"id":237,"slug":238,"title":239,"subtitle":97,"excerpt":240,"coverUrl":241,"locale":13,"readingMinutes":242,"publishedAt":243,"viewCount":244,"likeCount":19,"commentCount":19,"author":245,"vertical":246,"topic":247,"tags":250,"_count":255,"playground":256,"hasQuiz":17,"hasPlayground":17},"019fe660-cf2a-71df-b517-8d722f92cc37","weekly-cors-explained","CORS Explained: The Complete Guide (with Cheat Sheet)","A complete guide to CORS: the same-origin policy, preflight requests, and Access-Control-Allow-Origin headers — with worked examples and a cheat sheet.","\u002Fmedia\u002Fcovers\u002Fweekly-cors-explained.png",14,"2026-08-03T09:54:26.268Z",700,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":248,"name":249},"security","Security",[251,252,253,254],{"slug":46,"name":47,"color":97},{"slug":248,"name":249,"color":97},{"slug":50,"name":51,"color":97},{"slug":62,"name":63,"color":97},{"assessments":117},{"slug":238},{"id":258,"slug":259,"title":260,"subtitle":97,"excerpt":261,"coverUrl":262,"locale":13,"readingMinutes":100,"publishedAt":263,"viewCount":264,"likeCount":19,"commentCount":19,"author":265,"vertical":266,"topic":267,"tags":268,"_count":273,"playground":274,"hasQuiz":17,"hasPlayground":17},"019fe660-c754-703b-80fb-2254bb102ab7","scheduler-yield-main-thread","Your UI is freezing because you never gave the browser a break","Long synchronous tasks block the main thread and make your interface unresponsive. `scheduler.yield()` is the native way to hand control back to the browser mid-task so it can paint, handle input, and resume your work exactly where it left off.","\u002Fmedia\u002Fcovers\u002Fscheduler-yield-main-thread.png","2026-08-03T09:54:25.323Z",500,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":130},[269,270,271,272],{"slug":50,"name":51,"color":97},{"slug":46,"name":47,"color":97},{"slug":74,"name":75,"color":97},{"slug":54,"name":55,"color":97},{"assessments":117},{"slug":259},{"id":276,"slug":277,"title":278,"subtitle":97,"excerpt":279,"coverUrl":280,"locale":13,"readingMinutes":100,"publishedAt":281,"viewCount":282,"likeCount":19,"commentCount":19,"author":283,"vertical":284,"topic":285,"tags":286,"_count":291,"playground":292,"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",476,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":58,"name":188},[287,288,289,290],{"slug":58,"name":59,"color":97},{"slug":46,"name":47,"color":97},{"slug":54,"name":55,"color":97},{"slug":50,"name":51,"color":97},{"assessments":117},{"slug":277},{"id":294,"slug":295,"title":296,"subtitle":97,"excerpt":297,"coverUrl":298,"locale":13,"readingMinutes":100,"publishedAt":299,"viewCount":300,"likeCount":19,"commentCount":19,"author":301,"vertical":302,"topic":303,"tags":304,"_count":309,"playground":310,"hasQuiz":17,"hasPlayground":17},"019fe660-b8d7-7515-892c-bd576110158c","url-parse-safe-parsing","You've been wrapping `new URL()` in try\u002Fcatch. `URL.parse()` does it natively.","Validating an untrusted URL before parsing it used to mean a try\u002Fcatch wrapper or a regex. `URL.canParse()` checks validity without constructing. `URL.parse()` parses and returns null on failure. Both are Baseline 2024 — no wrapper needed.","\u002Fmedia\u002Fcovers\u002Furl-parse-safe-parsing.png","2026-08-01T08:27:13.710Z",431,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":50,"name":130},[305,306,307,308],{"slug":50,"name":51,"color":97},{"slug":46,"name":47,"color":97},{"slug":54,"name":55,"color":97},{"slug":66,"name":67,"color":97},{"assessments":117},{"slug":295},{"id":312,"slug":313,"title":314,"subtitle":97,"excerpt":315,"coverUrl":316,"locale":13,"readingMinutes":100,"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-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",409,{"id":104,"name":105,"username":106,"avatarUrl":97,"headline":107},{"slug":6,"name":7,"accentFrom":10,"accentTo":11},{"slug":58,"name":188},[323,324,325,326],{"slug":58,"name":59,"color":97},{"slug":46,"name":47,"color":97},{"slug":54,"name":55,"color":97},{"slug":50,"name":51,"color":97},{"assessments":117},{"slug":313},{"page":330,"perPage":76,"total":48,"totalPages":80},6,[332,333,334,335,336,337,338,339,340,341,342,343,344,347,350,353,356,359,362,365,368,371,372,373,376,379,382,385,388,391,394,397,400,403,406,409],{"slug":46,"name":47,"color":97,"articles":48},{"slug":50,"name":51,"color":97,"articles":52},{"slug":54,"name":55,"color":97,"articles":56},{"slug":58,"name":59,"color":97,"articles":60},{"slug":62,"name":63,"color":97,"articles":64},{"slug":66,"name":67,"color":97,"articles":68},{"slug":70,"name":71,"color":97,"articles":72},{"slug":74,"name":75,"color":97,"articles":76},{"slug":82,"name":83,"color":97,"articles":80},{"slug":78,"name":79,"color":97,"articles":80},{"slug":85,"name":86,"color":97,"articles":87},{"slug":89,"name":90,"color":97,"articles":87},{"slug":345,"name":346,"color":97,"articles":330},"programming","Programming",{"slug":348,"name":349,"color":97,"articles":330},"grammar","Grammar",{"slug":351,"name":352,"color":97,"articles":144},"nestjs","Nestjs",{"slug":354,"name":355,"color":97,"articles":144},"nextjs","Nextjs",{"slug":357,"name":358,"color":97,"articles":144},"apis","Apis",{"slug":360,"name":361,"color":97,"articles":43},"nuxt","Nuxt",{"slug":363,"name":364,"color":97,"articles":43},"ssr","SSR",{"slug":366,"name":367,"color":97,"articles":43},"ai","Ai",{"slug":369,"name":370,"color":97,"articles":43},"vue","Vue",{"slug":248,"name":249,"color":97,"articles":31},{"slug":214,"name":215,"color":97,"articles":31},{"slug":374,"name":375,"color":97,"articles":31},"forms","Forms",{"slug":377,"name":378,"color":97,"articles":31},"api","API",{"slug":380,"name":381,"color":97,"articles":31},"english","English",{"slug":383,"name":384,"color":97,"articles":31},"verbs","Verbs",{"slug":386,"name":387,"color":97,"articles":31},"spanish","Spanish",{"slug":389,"name":390,"color":97,"articles":31},"a1","A1",{"slug":392,"name":393,"color":97,"articles":31},"a2","A2",{"slug":395,"name":396,"color":97,"articles":31},"tenses","Tenses",{"slug":398,"name":399,"color":97,"articles":31},"turkish","Turkish",{"slug":401,"name":402,"color":97,"articles":31},"career","Career",{"slug":404,"name":405,"color":97,"articles":31},"beginners","Beginners",{"slug":407,"name":408,"color":97,"articles":31},"productivity","Productivity",{"slug":410,"name":411,"color":97,"articles":31},"discuss","Discuss"]