DOCS

Accessibility

How the Docyard theme meets WCAG 2.1 AA, what's covered automatically, and what content authors need to keep in mind.

Docyard's theme targets WCAG 2.1 Level AA conformance. This page documents what the theme handles automatically, known limitations, and guidance for authors.

What the theme covers

The theme is built on Nuxt UI 3, which provides a solid semantic HTML baseline. On top of that, Docyard ships with several explicit accessibility decisions.

Color contrast

All interactive and informational text is checked against the 4.5:1 minimum for normal-weight text below 18pt (WCAG SC 1.4.3).

ElementContrast (light mode)Notes
Body prose≥ 7:1--ui-text on off-white
Headings≥ 7:1--ui-text-highlighted
Active nav link≥ 6.5:1oklch(41% 0.15 155) — primary-700
Prose inline links≥ 6.5:1Same primary-700 override
Secondary nav links≥ 5:1oklch(49% 0.17 155) — primary-600
Section label buttons≥ 5:1oklch(46% 0.005 80) neutral
Code block "Copy" button (rest)≥ 4.8:1oklch(48% 0.005 80) neutral gray at full opacity
Code block "Copy" button (hover)≥ 7:1--ui-text-highlighted
Code block bordersN/ADecorative only

Note on opacity: At opacity values below ~85% on a near-white background, no text color can achieve 4.5:1 contrast regardless of its luminance. The Copy button therefore uses a color-and-border transition (ghost at rest, visible on hover/focus) rather than an opacity transition.

In dark mode the palette inverts; all the above values are verified at their dark-mode resolved colors by Nuxt UI's own design token system.

Landmark navigation

Every <nav> element on a page carries a distinct aria-label so screen readers can differentiate them:

  • aria-label="Main navigation" — header site links
  • aria-label="Mobile navigation" — mobile-expanded header nav
  • aria-label="Section navigation" — left sidebar
  • aria-label="On this page" — right-side table of contents

The page structure uses <header>, <main> (UMain), and <footer> landmarks from Nuxt UI, so the landmark outline is complete.

Keyboard navigation

All interactive elements — navigation links, the Copy button on code blocks, search, color-mode toggle, and accordion section headers — are reachable and operable by keyboard alone. Focus rings follow Nuxt UI defaults (2px offset ring in the primary color).

The UMain component renders a visually-hidden "Skip to main content" link as the first focusable element, allowing keyboard users to bypass repeated navigation.

Reduced motion

Animations that use transform or opacity transitions are suppressed when the user sets prefers-reduced-motion: reduce. The page-load .reveal animation class and code-block transitions both include this media query.

Forced colors (Windows High Contrast)

The theme includes an explicit @media (forced-colors: active) block in main.css that restores OS-managed system color keywords for:

  • Code block backgrounds and text (Shiki per-token colors are reset to CanvasText)
  • Inline code borders (ButtonBorder)
  • The h2 gradient rule (collapses to a solid CanvasText line)
  • Blockquote and callout left borders (Highlight)
  • Prev/next surround card borders (ButtonBorder)

Semantic markup

Headings follow a strict hierarchy. ContentRenderer outputs h2h6 from Markdown ########; the page <h1> comes from DocsPageHeader and is never duplicated.

Tables use <th> for column headers. Code blocks use <pre><code> with a language data-lang attribute. Callouts use role="note" semantics from Nuxt UI's UAlert.


Known limitations

Syntax highlighting token contrast

The default light-mode Shiki theme (material-theme-lighter) includes some token colors — notably #91b859 (strings, paths) and #90a4ae (variable names) — that fall below 4.5:1 on the off-white page background. This is an upstream theme-design decision.

To improve token contrast, switch to github-light in your site's Nuxt config, which has been designed with higher contrast token colors:

// nuxt.config.ts
export default defineNuxtConfig({
  content: {
    highlight: {
      theme: {
        light: 'github-light',   // better token contrast
        dark:  'github-dark',
      }
    }
  }
})

Nuxt UI's UContentNavigation marks the active page link with the text-primary CSS class rather than aria-current="page". Screen readers therefore cannot identify the current page from the sidebar navigation alone. The browser's own URL indication and the page <h1> serve as the primary location cues. This is tracked as a Nuxt UI upstream issue.


Authoring guidance

The theme handles structural accessibility, but content authors share responsibility for the final result.

Images

Always provide descriptive alt text on images. For decorative images, use an empty alt="":

![Chart showing monthly active users over 12 months](./usage-chart.png)

<!-- decorative divider -->
![](./divider.png)

Avoid non-descriptive link text like "click here" or "read more". Screen reader users often navigate by jumping between links, so each link's text should make sense in isolation:

<!-- avoid -->
See the docs [here](./getting-started).

<!-- prefer -->
Read the [getting started guide](./getting-started).

Tables

Use tables only for tabular data. Every table should have a header row using |---| separator syntax. For complex tables, add a caption using Quarto's tbl-cap syntax.

Callouts

Callouts map to role="note" semantics. Use them for genuinely advisory content, not for decorative pull-quotes. The callout type (note, warning, important, caution, tip) is conveyed visually and via text content — it is not currently mapped to an ARIA role variant like role="alert".


Testing

To run an accessibility audit against your built docs site, use axe-core via Playwright or a browser extension. The theme ships no built-in audit tooling, but the following command exercises the full pipeline:

cd docs/site
DOCYARD_THEME_PATH="$PWD/../../packages/docyard-theme" DOCYARD_SKIP=1 bun run build
bunx playwright test --grep "a11y"   # if you have a11y tests configured

The axe DevTools browser extension is the recommended manual audit tool during authoring.