Skip to content

Accessible Modals: From dialog showModal() to Focus Traps

You built a modal. You press Tab, and the buttons in the background get focus. You press ESC, and nothing closes. A screen reader plows through the content without even saying “dialog.” And when you click the close button, focus vanishes into thin air. Anyone who builds modals runs into all four of these at some point. The name focus trap sounds great, but the problem is that the trap tends to get set in the wrong place. Let’s fix them one by one. ...

Published date: 2026-10-24 · Reading time: 27 min · Word count: 5686 words · Author: Isaac

Form Validation Timing: From Blur to aria-invalid

I was building a signup form. The moment I focused the email field, an error popped up: “Invalid email format.” I hadn’t typed a single character yet. The password confirmation field was the opposite. I typed the whole password, submitted the form, and only then did it say “Passwords don’t match” — after I’d already finished typing. Then I hit submit, and seven error messages showed up all at once. ...

Published date: 2026-09-15 · Reading time: 19 min · Word count: 4028 words · Author: Isaac

Focus Management in SPAs: Keeping Screen Readers on Track After Route Changes

I’d been happily shipping a React SPA for a while when an accessibility audit came back with this note: “The page changed, but the screen reader said nothing.” The page transition animated smoothly, the URL updated, the content changed… but as far as a screen reader user was concerned, nothing happened at all. That’s the hidden trap of SPAs. Traditional Websites vs. SPAs What happens when you click a link on a traditional multi-page site? ...

Published date: 2026-09-15 · Reading time: 8 min · Word count: 3742 words · Author: Isaac

The Color Is Right but the Contrast Is Wrong: The Accessibility Trap CSS opacity Sets

I had clearly written a dark gray into my CSS. color: #595959. On a white background that color has a contrast of 6.4:1, which comfortably clears the accessibility bar of 4.5:1. Pass. Then I ran an accessibility checker, and a red line appeared. This text has a contrast of 4.04:1, which is insufficient. Expected ratio: 4.5:1. I put in a color that measures 6.4:1, so why is it calling it 4:1? Was the checker wrong? ...

Published date: 2026-07-21 · Reading time: 10 min · Word count: 2051 words · Author: Isaac

Supporting High Contrast Mode Properly: forced-colors and System Colors

Some people set their screen to maximum contrast. Yellow text on black, white text on blue, that sort of thing. For low-vision users this is the difference between being able to read the text and not; people with migraines or light sensitivity go the other way, using a muted, low-contrast palette. They have one thing in common — the user decides the colors. And the moment they do, a good chunk of the colors we wrote in CSS get ignored. ...

Published date: 2026-07-18 · Reading time: 12 min · Word count: 2419 words · Author: Isaac

Dark Mode Done Right: CSS Variable Design to Contrast Verification

I turned on dark mode and my eyes hurt more. The background was black but the text was a heavy gray, so the contrast was far too low; links were still the default blue (#0000ff), which stung. Images blazed alone against the dark screen. One button was the same color as its background, so it was simply invisible. It said “dark mode supported.” But it was dark mode by color inversion, nothing more. ...

맨 위로