Craft: the accessibility engine
This portfolio is hand-written: no framework, no build step, one stylesheet and one script. It carries an accessibility layer of eleven preferences a visitor can set, restored before the first paint, plus a keyboard and focus model. This page covers how that works and what it changed about the design.
Why this page exists
Accessibility is easy to claim on a portfolio and hard to check. This page documents the system the site actually runs, including the parts I got wrong first time.
Every number here is measured. Where a decision was wrong, it is written down as it happened.
Eleven preferences
Nine change how the page renders. Two change how it behaves: muting the interface sounds, and disabling the single-key shortcuts so speech input does not trigger them. They combine freely, and every pair was checked to confirm one preference never cancels another.
Preferences have to survive the first paint
A preference restored by the main script arrives too late. The visitor sees the page render wrong, then correct itself, and the people who set the preference are the ones that flash affects most.
The restore runs in a small render-blocking script, first element in the head, byte-identical across every page. It reads the saved preferences and applies them before anything paints. Storage access is wrapped in a try/catch: a browser with storage blocked throws on read, and an unguarded read there takes the whole script down with it.
495 bytes
The render-blocking restore script. Identical on all pages, so it is a single hash in the Content-Security-Policy.
Decisions that changed because of it
Four of these were bugs in my first implementation.
Focus, dialogs and one lockout
The site has four overlays: the accessibility panel, a command palette, a shortcuts sheet and an image lightbox. While a modal one is open the rest of the page is marked inert, so keyboard and screen-reader users cannot wander behind it.
That model shipped with a bug. The accessibility panel is deliberately non-modal, but it was listed in the guard that decides when to release inert. Opening the panel, then opening and closing the palette, left the page permanently inert, and the only way back was to open the panel again. The guard now lists just the modal dialogs. A check that nothing is left inert after any overlay closes runs on every page, in every engine, on every test pass.
Keyboard
A command palette on Cmd or Ctrl K, a shortcuts sheet on question mark, D for theme, T for top. The single-key shortcuts are guarded so they do not fire while text is being typed or while a modifier is held, and they can be switched off entirely for speech input.
Two preferences only make sense with a keyboard: enhanced focus, and disabling single-key shortcuts. Both are hidden on touch devices.
How it is checked
Automated auditing across every preference, both themes, both palettes and both viewports, in three browser engines. Then the checks a scanner cannot do: text faded by opacity, composited and measured by hand; focus rings driven by real keyboard input; and the page loaded with JavaScript disabled to confirm no content is left invisible.