The blog

Writing from the workshop

202 articles on the parts that actually hurt.

section
Your Browser Is Rejecting Every Drop On Purpose
JavaScript

Your Browser Is Rejecting Every Drop On Purpose

A drop zone with a correctly wired drop listener that never fires — no error, no typo, nothing in the console. The browser is silently refusing the drop by design, and only one specific line stops it.

Sep 20, 2026 · 6 min
Nuxt Hydration Mismatch: Why It Happens and How to Fix It
Nuxt

Nuxt Hydration Mismatch: Why It Happens and How to Fix It

A Nuxt hydration mismatch happens when the server's HTML disagrees with the client's first render. Learn why, and the fixes that actually work.

Sep 20, 2026 · 14 min
Chrome Already Has The Eyedropper You're Building
JavaScript

Chrome Already Has The Eyedropper You're Building

You're about to build a canvas-based color picker with a magnifier loupe. Chrome shipped a native one years ago — and it can sample pixels your own canvas can't even see.

Sep 20, 2026 · 5 min
NestJS Testing Module: Provider Overrides (with Cheat Sheet)
Nestjs

NestJS Testing Module: Provider Overrides (with Cheat Sheet)

A complete guide to NestJS's testing module: how overrideProvider really wires mocks, why get() fails on scoped providers, and using useMocker.

Sep 18, 2026 · 13 min
The CSS Property That Fixes Dark Mode's Ugly White Boxes
CSS

The CSS Property That Fixes Dark Mode's Ugly White Boxes

You shipped dark mode. The page is black, the text is white — and then the date picker opens like a floodlight. color-scheme is the one-line CSS property that tells the browser your native form controls need to catch up too.

Sep 18, 2026 · 4 min
Stop Debouncing Resize. The Browser Already Watches It.
JavaScript

Stop Debouncing Resize. The Browser Already Watches It.

A debounced resize handler comparing window.innerWidth to a magic number can't ever see dark mode or reduced motion — and it fires dozens of times for one thing matchMedia fires once for.

Sep 17, 2026 · 6 min
Your Fetch Already Streams. You're Buffering It Anyway.
JavaScript

Your Fetch Already Streams. You're Buffering It Anyway.

A chat UI that types out its reply letter by letter isn't proof it's streaming — it's often proof the opposite. fetch() delivers a real ReadableStream body, and most code drains it into one string before showing a single character.

Sep 16, 2026 · 5 min
Next.js Parallel & Intercepting Routes: Modals Done Right
Nextjs

Next.js Parallel & Intercepting Routes: Modals Done Right

How Next.js parallel routes (@slot) and intercepting routes ((.), (..), (...)) combine to build shareable, refreshable modals — verified against Next.js 16.3.

Sep 15, 2026 · 14 min
Your Custom Element Is Missing From FormData — ElementInternals Fixes It
JavaScript

Your Custom Element Is Missing From FormData — ElementInternals Fixes It

A custom <star-rating> element sits inside a <form>, looks like a form field, and gets left out of the submission entirely. The browser has a real fix for this — most teams reach for a hack instead.

Sep 15, 2026 · 5 min
Vue nextTick Explained: How DOM Updates Are Batched
Vue

Vue nextTick Explained: How DOM Updates Are Batched

How Vue batches reactive writes into one DOM update, why the DOM looks stale right after a change, and how nextTick and watch's flush option fix it.

Sep 14, 2026 · 13 min
The 'Read Aloud' Button That Only Works on the Second Click
JavaScript

The 'Read Aloud' Button That Only Works on the Second Click

A text-to-speech button that works perfectly every time you test it — and silently fails or picks the wrong voice for a real visitor's first click. The bug is a timing race baked into the Web Speech API that most code never accounts for.

Sep 14, 2026 · 5 min
useAsyncData Keys in Nuxt: Caching, Dedupe & the Sharing Bug
Nuxt

useAsyncData Keys in Nuxt: Caching, Dedupe & the Sharing Bug

Learn how Nuxt's useAsyncData and useFetch generate cache keys, how dedupe (cancel vs defer) really works, and why wrapper composables silently share data.

Sep 13, 2026 · 15 min