Writing from the workshop
119 articles on the parts that actually hurt.

You built your modal with a <div> and a focus trap library. The native <dialog> does all of that.
The `<dialog>` element ships with focus trapping, Escape-to-close, backdrop rendering, and correct ARIA semantics — no library, no boilerplate. It's been Baseline since 2022.
Aug 8, 2026 · 5 min
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/v4 — exactly what `crypto.randomUUID()` does natively, with no import, no bundle cost, and guaranteed cryptographic randomness.
Aug 8, 2026 · 3 min
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.
Aug 7, 2026 · 4 min
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.
Aug 6, 2026 · 4 min
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.
Aug 6, 2026 · 4 min
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.
Aug 5, 2026 · 4 min
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.
Aug 4, 2026 · 5 min
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.
Aug 3, 2026 · 14 min
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.
Aug 3, 2026 · 5 min
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.
Aug 2, 2026 · 5 min
You've been wrapping new URL() in try/catch. URL.parse() does it natively.
Validating an untrusted URL before parsing it used to mean a try/catch 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.
Aug 1, 2026 · 5 min
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.
Jul 31, 2026 · 5 min