---
title: "This site's accessibility"
description: "The documentation site held to the standard it publishes. It records the places where it currently fails."
url: "https://opsinjs.pensievelabs.org/accessibility/this-site"
source: "https://opsinjs.pensievelabs.org/accessibility/this-site.md"
section: "Accessibility"
kind: "accessibility"
reviewed: "2026-09-02"
reviewer: "engineering"
aliases: ["docs site accessibility", "dogfooding"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="accessibility" />

## What we guarantee [#what-we-guarantee]

A design system that publishes an accessibility standard and does not apply it to
its own documentation has published a marketing document. This page is the
counterweight, and it is deliberately uncomfortable.

What the site does well by construction:

* **The docs chrome is fumadocs on Base UI primitives,** so the sidebar, search
  dialog, tab sets and popovers inherit APG-conformant keyboard behaviour rather
  than a hand-rolled approximation.
* **One persistent sidebar, no layout tabs.** Navigation does not change shape
  between sections, which is SC 3.2.3 by construction. The reasoning is that a
  developer reading about `Button` must be able to see that *Clinical safety*
  exists.
* **Heading structure is build-enforced.** Every page's `kind` fixes its H2
  outline and `assert-ia.mts` fails the build on a missing or invented heading,
  so heading order cannot rot page by page.
* **The single-key search hotkey is disabled.** fumadocs offers a bare-letter
  shortcut; it is turned off, because SC 2.1.4 exists and because a
  single-character shortcut is hostile to voice-control users.
* **Light theme is the default with system available.** The theme is not
  auto-selected from the operating system, so a reader who has not chosen gets a
  predictable, measured starting point.
* **A real print stylesheet.** See [Print and export](./print-and-export.mdx).

## What you own [#what-you-own]

You own nothing on this page, because it is about our site, not yours. What you
can take from it is the practice: publish your own version of this page for your
own product, including the failures, and update it when they change rather than
when somebody asks.

## How to check [#how-to-check]

Everything below is reproducible in a clone of the repository.

<Steps>
  ### Run the docs site and unplug the mouse [#run-the-docs-site-and-unplug-the-mouse]

  Using the keyboard only, navigate to a component page, open search, filter the
  status matrix, change the theme and return to the sidebar. This is the check
  that established the 404 finding below.

  ### Inspect the 404 route [#inspect-the-404-route]

  Visit any unmatched URL. The site deliberately has no top-level
  `app/layout.tsx`, because two sibling root layouts are what keep the chrome-less
  `(view)` preview surface isolated. `app/not-found.tsx` therefore renders inside
  a framework-generated document rather than inside our root layout.

  ### Check the language attribute [#check-the-language-attribute]

  On the 404 route specifically, inspect the root element. The absence of an
  authored root layout means the page's language is set on a wrapper rather than on
  the document element, which is a weaker answer to SC 3.1.1 than the rest of the
  site gives.

  ### Read the heading outline of any page [#read-the-heading-outline-of-any-page]

  Extract the headings. They should form a usable table of contents; if any page's
  outline does not, that is a build-gate failure and should be reported as one.
</Steps>

## Measured results [#measured-results]

<NoDataYet script="scripts/check-contrast.mts" />

The docs chrome uses the shadcn `lyra` style with a neutral base. Using a
different theme from the opsinjs product theme is deliberate, for the reasons on
[Lyra and the docs chrome](../theming/lyra-and-the-docs-chrome.mdx). The chrome's
own token pairs have not been measured; the contrast generator currently covers
the opsinjs axes rather than the shadcn chrome tokens. That is why this page
lists the gaps rather than claiming conformance.

## Known gaps [#known-gaps]

* **The chrome's token pairs are unmeasured.** We measure the product theme and
  publish the numbers; we do not yet measure the theme this documentation is
  rendered in. That is the wrong way round for a page about holding ourselves to
  our own standard.
* **The 404 page does not render inside the site's root layout,** and its
  language is therefore declared on a wrapper element rather than on the document
  element. This is a direct consequence of the two-root-layout structure, which
  is a deliberate trade recorded in the repository's own notes. It is a real
  SC 3.1.1 weakness and it is not yet fixed.
* **The lyra docs chrome is dense by design.** Its type is small, its controls
  are tight and its corners are square. That density is a legitimate choice for
  a developer reading at a desk and is the opposite of the product theme's
  guidance. The 44 px target floor is a *product* rule and the chrome does not
  meet it.
* **No skip link is shipped** on the documentation site.
* **The site has never been tested with a screen reader.**
* **No automated accessibility check runs against the site in CI.**
* **The interactive playgrounds are unaudited,** and a colour picker is a hard
  accessibility problem that we have not yet solved.

<Todo>
  Extend `scripts/check-contrast.mts` to cover the docs chrome token pairs as well
  as the product axes, add a skip link to the chrome root layout, and decide
  whether the 404 route's language weakness is fixed with a catch-all route inside
  the chrome layout or accepted and documented as a permanent trade.
</Todo>

## Updates to this page [#updates-to-this-page]

<Reviewed />
