Display settings

Theme
Text size
Reading and motion

Saved in this browser only.

Display settings: light and high-contrast themes, larger text and reading options

A Display settings button in every page's header now offers a light theme, a high-contrast theme, larger text, wider spacing, a readable font, underlined links with bold focus outlines, and reduced motion. Choices are saved in the visitor's browser and apply before the page appears.

The site had one look: dark, with type sized for a typical screen. Anyone who reads better on a light page, needs strong contrast, wants larger text or spacing, or finds motion uncomfortable had to fall back on browser settings that often do not reach a page built like this one.

What is in the panel

A button beside the navigation, a half-filled circle, opens Display settings on every page:

  • Theme: match my device (the default), dark, light or high contrast. “Match my device” follows the device’s light or dark setting, and its increased-contrast setting, even while the page is open.
  • Text size: default, large or larger. Page content and the footer grow and reflow the way browser zoom does. Large headings keep their size, because they are already large and are sized so their longest word fits the screen. On screens under 360 pixels wide, “larger” acts as “large”.
  • Wider line, word and letter spacing, using the WCAG text-spacing values.
  • Readable font: Atkinson Hyperlegible, made by the Braille Institute for legibility. Two 17 KB files are served from this site and load only when the option is on.
  • Underline links and show bold focus outlines.
  • Reduce motion, for anyone who wants it whatever their device says. It turns off the reveal animations, the pointer trail and smooth carousel scrolling. When a device already asks for reduced motion, the box is checked and fixed.

Choices are saved in the visitor’s browser only, and Reset clears them. A small script in each page’s head applies them before the page is drawn, so a saved theme or text size never flashes in after the page appears. Without JavaScript the site looks as designed and the button stays hidden. The panel is a modal dialog: focus moves into it, Escape or a click outside closes it, and focus returns to the button.

How the themes are made

The site was designed dark, with about 800 colours written directly into its stylesheets. tools/derive-theme-css.mjs reads every stylesheet and writes each colour declaration again for the light and high-contrast themes:

  • Colours are mapped by what they do (text, background, border, shadow) and worked in the OKLCH colour space, so lightness flips while hue stays: the warm browns become warm off-whites, and gold becomes a deeper amber.
  • In high contrast, backgrounds are black, text is white, accents and outlines are yellow, and decorative glows and gradients are removed.
  • Colour variables are resolved to their actual colour first. Each use is then mapped for the way it is used, so the same near-black is a page background in one place and text on a gold button in another.
  • Only colour longhands are written, so a theme never resets a background’s size, a border’s width or a heading’s gradient fill.
  • Every colour declaration is copied, changed or not, so the theme keeps the original cascade.

Each page carries only the theme rules for elements it has. That adds about 4 to 6 KB to a page after compression.

Verification

  • tools/test-themes.mjs, now part of npm run test:a11y, loads every sitemap route plus the error page in light and high contrast, at 1280 and 390 pixels wide. It runs axe’s colour-contrast and link rules, and checks the theme is in place before the page’s own scripts run. Both themes pass on all 49 routes.
  • tools/test-text-fit.mjs now also checks every route with larger text, wide spacing and the readable font together, at 320, 768, 1024 and 1440 pixels. That pass first found 82 problems, all fixed:
    • full-width sections shifted off the screen inside enlarged content;
    • the Live page’s heading was squeezed beside its portrait;
    • long names and code ran past phone screens;
    • the home page’s titles did not fit in the readable font.
  • tools/test-site-chrome.mjs checks every page for the settings button, all six controls and the head script.
  • The panel was exercised in a browser:
    • it opens from the keyboard, focus moves inside it, and Escape closes it;
    • each control applies;
    • choices carry to another page, and Reset clears them.
  • Screenshots of thirteen pages in both themes, at desktop and phone widths, were reviewed by eye.