Making This Website Accessible

30 August 2026 · technology

I have banged on before about accessibility working best as a starting constraint rather than a retrofit. This site is where I get to practise that habit continually: my core philosophy is that if something is inaccessible to some it may as well be inaccessible to all. I make sure of this with a pile of small habits, and an audit script that catches the bits I inevitably miss. Part of the series on how this site is built.

Quick jargon guide

  • Screen reader: software that reads a page aloud (or to a braille display) for blind and low-vision users, navigating by headings, links, and landmarks rather than by looking.
  • WCAG: the Web Content Accessibility Guidelines, the standard everyone measures against. Its techniques have catalogue numbers, like items at IKEA.
  • ARIA attributes: HTML attributes (aria-pressed, aria-label, aria-live) that tell assistive technology what a widget is and what state it's in when the visuals alone can't.
  • sr-only: a CSS class that hides text visually but leaves it for screen readers. The inverse of decoration.
  • Skip link: a link at the very top of the page, visible only when focused, that jumps keyboard users past the navigation to the content.
  • Reduced motion: an operating-system setting (prefers-reduced-motion) that asks websites to calm down with the animations. Vestibular disorders make this a health feature, not a taste.
  • Focus trap: keeping keyboard focus inside an open dialog so Tab doesn't wander off into the page behind it.

The keyboard path

The first element injected into every page is what is known as a skip link. Press Tab on any page here and "Skip to main content" appears before anything else. In short, this allows you to press Enter and get past any navigation fluff at the top of the page. It's invisible to mouse users, and is an old trick that is often forgotten in 'modern' websites.

The site header with a Skip to main content button overlaid on the navigation, outlined by a red focus ring
Press Tab anywhere on the site and this is the first stop. Mouse users never see it.

The Ctrl+K command palette got the full dialog treatment: focus moves into the input when it opens, Tab is trapped (it moves the selection instead of escaping the dialog), Escape closes it, and results are given to screen readers through an aria-live region. The filter buttons on the blog and gallery use aria-pressed, so "Photography, toggle button, pressed" comes through instead of a mystery link.

The Ctrl+K command palette open over a dimmed homepage, with the query accessib typed, six post results listed, the top one highlighted, and footer hints reading navigate, open, esc close
The palette moves focus into the input, traps Tab, closes on Escape, and announces results through an aria-live region.
The blog's tag filter bar with fourteen outlined tag buttons; the photography tag is solid red, showing its pressed state
The pressed filter reads as "photography, toggle button, pressed": the visual state and the aria-pressed state are the same fact.

The theme toggle updates aria-pressed and swaps its label between "Switch to light mode" and "Switch to dark mode", so the button always says what it will do, not what it did.

The screen reader path

Links that open a new tab get a screen-reader-only annotation, WCAG technique G201, applied automatically:

// Appends screen-reader-only "(opens in new tab)" to target="_blank" links
var span = document.createElement('span');
span.className = 'sr-only';
span.textContent = ' (opens in new tab)';
link.appendChild(span);

A MutationObserver reapplies it to links injected later (related posts, footers), because on this site half the content arrives after first paint. For a screen reader user, a link that teleports you into a new tab with no warning is awful.

Images carry real alternative text - I imagine not having it feels almost painful for screen-reader users, they likely feel they're missing out on important content and don't know what it is, like missing pages in a book. I aim for a description, not caption: the photo of my dog is "A picture of my puppy, Brutus", and the chart images in data posts describe the actual bars and numbers, so the information is actually useful, instead of "a photo" or "the results shown as an image". The interactive posts give every canvas role="img" and an aria-label saying what the simulation shows. A label is a thin substitute for "here is a live algorithm race you cannot see", so I'm open to suggestions on how to make it a better experience.

The vestibular path

Three separate systems check prefers-reduced-motion: the scroll-reveal animations (everything just appears, already visible), the homepage stats (they display their final number instead of counting up), and the CSS transitions (three media blocks calm them down). The site has to be complete with the animation subtracted, and only then is the animation allowed.

Checkpoints

Habits decay, so the audit script checks every page for the a11y basics (alt text, heading structure, link health, metadata) on every push, and CI fails loudly when a new post shows how bad my memory is at maintaining these standards without a checklist. I love automating stuff, I'd rather put in extra effort early to save effort later.

Terminal output of the audit script: four INFO lines about long meta descriptions, then a summary reading 86 pages audited, 0 errors, 0 warnings, 29 info, with the zeroes in green
The audit summary on a clean tree. CI runs the same script on every push and fails the build on any error or warning.

I'd love to hear from an actual screen reader user. If you use assistive tech and something here fights you, the contact page works, and I fix reported things fast.

Common questions

It's just a blog why do you put in so much effort?

Reverse the question: what justifies publishing a public page some readers can't use? The effort is also smaller than it looks. Nearly everything above was under an hour of work, done once, and enforced by automation afterwards. The expensive version of accessibility is the retrofit.

Does dark mode count as accessibility?

For me, yes. I get headaches and my eyes burn when I look at bright screens. So avoiding the white flash matters most for people like me. For everyone else it's comfort.

Back to all posts