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).
| Element | Contrast (light mode) | Notes |
|---|---|---|
| Body prose | ≥ 7:1 | --ui-text on off-white |
| Headings | ≥ 7:1 | --ui-text-highlighted |
| Active nav link | ≥ 6.5:1 | oklch(41% 0.15 155) — primary-700 |
| Prose inline links | ≥ 6.5:1 | Same primary-700 override |
| Secondary nav links | ≥ 5:1 | oklch(49% 0.17 155) — primary-600 |
| Section label buttons | ≥ 5:1 | oklch(46% 0.005 80) neutral |
| Code block "Copy" button (rest) | ≥ 4.8:1 | oklch(48% 0.005 80) neutral gray at full opacity |
| Code block "Copy" button (hover) | ≥ 7:1 | --ui-text-highlighted |
| Code block borders | N/A | Decorative 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 linksaria-label="Mobile navigation"— mobile-expanded header navaria-label="Section navigation"— left sidebararia-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).
Skip links
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
CanvasTextline) - Blockquote and callout left borders (
Highlight) - Prev/next surround card borders (
ButtonBorder)
Semantic markup
Headings follow a strict hierarchy. ContentRenderer outputs h2–h6 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',
}
}
}
})
Active nav link without aria-current
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="":

<!-- decorative divider -->

Links
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.