Legal
Accessibility Conformance Document
Moonwhisk’s public marketing site is designed to meet WCAG 2.2 Level AAA, with accessibility tested across dark, light, and decaf themes. This statement outlines the site’s scope, verification methods, conformance status, known limitations, and areas for ongoing review
Last updated: 3 June 2026
WCAG 2.2 AAA Conformance — Moonwhisk
Scope: the public marketing site (home, legal pages, global nav/footer) targets Level AAA. The auth-gated admin CMS (/admin, noindex) is held to a Level AA baseline.
Themes: all visual criteria are met in every theme — dark (default), light, and decaf (low-stimulation). Contrast was verified by computation and by automated testing in all three.
How this was verified
- Automated:
pnpm test:a11yruns axe-core over/,/privacy,/terms,/cookie-policyin each theme (12 runs) with Level A/AA + the AAA rules axe supports (notablycolor-contrast-enhanced, 1.4.6). 0 violations. - Lint:
pnpm lint:a11y(eslint-plugin-jsx-a11y, strict) — 0 errors. - Manual: keyboard walkthrough, screen-reader landmark/heading pass, 200% zoom + 320px reflow,
prefers-reduced-motion,prefers-contrast, and forced-colors emulation.
Automated tooling can only confirm ~30–40% of WCAG criteria. Items marked Manual below were checked by hand; items marked Auto are covered by the axe suite as a regression guard.
Principle 1 — Perceivable
| SC | Title | Lvl | Status | Notes |
|---|---|---|---|---|
| 1.1.1 | Non-text Content | A | Pass (Auto) | Icons aria-hidden; social/logo links have aria-label; cert iframe has title; decorative canvas/parallax aria-hidden. |
| 1.2.x | Time-based Media | A–AAA | N/A | No audio or video. |
| 1.3.1 | Info and Relationships | A | Pass (Auto) | header/nav/main/footer landmarks, heading hierarchy, lists, <dl> glossary, programmatic label↔control association. |
| 1.3.2 | Meaningful Sequence | A | Pass | DOM order matches visual order. |
| 1.3.3 | Sensory Characteristics | A | Pass | No instructions rely on shape/position/sound alone. |
| 1.3.4 | Orientation | AA | Pass | Responsive; no orientation lock. |
| 1.3.5 | Identify Input Purpose | AA | Pass | Login uses autocomplete="current-password". |
| 1.3.6 | Identify Purpose | AAA | Pass | Landmark roles + ARIA state on controls; autocomplete on inputs. |
| 1.4.1 | Use of Color | A | Pass (Manual) | State never conveyed by colour alone; "opens in new tab" is text; prefers-contrast underlines links. |
| 1.4.2 | Audio Control | A | N/A | No auto audio. |
| 1.4.3 | Contrast (Minimum) | AA | Pass (Auto) | Superseded by 1.4.6. |
| 1.4.4 | Resize Text | AA | Pass (Manual) | rem/clamp type; legible at 200% with no loss. |
| 1.4.5 | Images of Text | AA | Pass | Live web fonts; logo is SVG/text. |
| 1.4.6 | Contrast (Enhanced) | AAA | Pass (Auto) | All body text ≥7:1, large text ≥4.5:1, in all 3 themes. Tokens --text-muted, --text-body, --gold (light), --gold-dim/--gold-light text uses, and --c-cyan (light) were re-tuned; verified by axe color-contrast-enhanced. |
| 1.4.7 | Low / No Background Audio | AAA | N/A | No audio. |
| 1.4.8 | Visual Presentation | AAA | Pass (Manual) | Reading columns ≤~72ch; body line-height ≥1.5; no justified text; user-selectable themes incl. low-stimulation decaf; prefers-contrast honored. |
| 1.4.9 | Images of Text (No Exception) | AAA | Pass | No images of text. |
| 1.4.10 | Reflow | AA | Pass (Manual) | Single-column reflow to 320px; no 2-D scroll. |
| 1.4.11 | Non-text Contrast | AA | Pass | Focus ring ≥3:1; control borders meet 3:1 (strengthened under prefers-contrast). |
| 1.4.12 | Text Spacing | AA | Pass | No clipped text under user spacing overrides. |
| 1.4.13 | Content on Hover or Focus | AAA | Pass (Manual) | No hover-only/title-based tooltips; glossary is a persistent <details>; nothing appears on hover that can't be dismissed/hovered. |
Principle 2 — Operable
| SC | Title | Lvl | Status | Notes |
|---|---|---|---|---|
| 2.1.1 | Keyboard | A | Pass (Auto) | All controls are native a/button; burger menu keyboard-operable; cert iframe is focusable (removed tabindex=-1). |
| 2.1.2 | No Keyboard Trap | A | Pass (Manual) | Verified; Esc closes the mobile menu. |
| 2.1.3 | Keyboard (No Exception) | AAA | Pass (Manual) | No mouse-only functionality. |
| 2.1.4 | Character Key Shortcuts | A | N/A | No single-character shortcuts. |
| 2.2.1 | Timing Adjustable | A | N/A | No time limits. |
| 2.2.2 | Pause, Stop, Hide | A | Pass (Manual) | Decorative motion stops via prefers-reduced-motion and the decaf theme (a persistent, user-accessible control); animations also pause off-screen/when tab hidden. |
| 2.2.3 | No Timing | AAA | Pass | No time limits anywhere. |
| 2.2.4 | Interruptions | AAA | Pass | Only the cookie banner; dismissible, no re-prompts. |
| 2.2.5 / 2.2.6 | Re-authenticating / Timeouts | AAA | N/A | Admin only (AA scope); no public sessions. |
| 2.3.1 | Three Flashes or Below | A | Pass (Manual) | No content flashes; slowest pulse is 2s (0.5 Hz). |
| 2.3.2 | Three Flashes | AAA | Pass (Manual) | No flashing. |
| 2.3.3 | Animation from Interactions | AAA | Pass | Motion disabled under prefers-reduced-motion. |
| 2.4.1 | Bypass Blocks | A | Pass (Manual) | "Skip to main content" link → #main-content. |
| 2.4.2 | Page Titled | A | Pass | Unique per-page <title>. |
| 2.4.3 | Focus Order | A | Pass (Manual) | Logical order; skip-link first. |
| 2.4.4 | Link Purpose (In Context) | A | Pass (Auto) | Descriptive links. |
| 2.4.5 | Multiple Ways | AA | Pass | Nav, in-page anchors, footer links, sitemap.xml. |
| 2.4.6 | Headings and Labels | AA | Pass (Auto) | Descriptive headings/labels. |
| 2.4.7 | Focus Visible | AA | Pass (Auto) | Global :focus-visible ring. |
| 2.4.8 | Location | AAA | Pass (Manual) | Sitemap + consistent nav; legal pages are single-level (no breadcrumb needed). |
| 2.4.9 | Link Purpose (Link Only) | AAA | Pass (Manual) | Link text self-describes; external links append a hidden "(opens in new tab)". |
| 2.4.10 | Section Headings | AAA | Pass | Each section has a heading (visually-hidden where the design omits a visible one). |
| 2.4.11 | Focus Not Obscured (Min) | AA | Pass | scroll-padding-top keeps focus clear of the sticky nav. |
| 2.4.12 | Focus Not Obscured (Enhanced) | AAA | Pass (Manual) | Same; focused targets fully visible. |
| 2.4.13 | Focus Appearance | AAA | Pass (Manual) | 3px ring, 3px offset, ≥7:1 against the surface (well over the 3:1 / 2px minimum). |
| 2.5.1 | Pointer Gestures | A | Pass | No path/multipoint gestures. |
| 2.5.2 | Pointer Cancellation | A | Pass | Activation on click/up. |
| 2.5.3 | Label in Name | A | Pass (Auto) | Visible text is within the accessible name. |
| 2.5.4 | Motion Actuation | A | N/A | No motion actuation. |
| 2.5.5 | Target Size (Enhanced) | AAA | Pass (Manual) | Nav links, CTA, burger, social, theme-toggle ≥44×44 px. |
| 2.5.6 | Concurrent Input Mechanisms | AAA | Pass | No input-mode restrictions. |
| 2.5.7 | Dragging Movements | AA | N/A | No dragging on the public site (demos are non-interactive). |
| 2.5.8 | Target Size (Minimum) | AA | Pass | Superseded by 2.5.5. |
Principle 3 — Understandable
| SC | Title | Lvl | Status | Notes |
|---|---|---|---|---|
| 3.1.1 | Language of Page | A | Pass | <html lang="en">. |
| 3.1.2 | Language of Parts | A | Pass | No foreign-language passages. |
| 3.1.3 | Unusual Words | AAA | Pass | Footer glossary defines jargon (lib/glossary.ts). |
| 3.1.4 | Abbreviations | AAA | Pass | Glossary lists expansions (CLT, SDK, ONNX, TCN, Ltd). |
| 3.1.5 | Reading Level | AAA | Deferred | Not claimed. Per project decision, marketing copy is not rewritten; the glossary + help affordance support comprehension instead. The only AAA criterion not asserted. |
| 3.1.6 | Pronunciation | AAA | N/A | Meaning does not depend on pronunciation. |
| 3.2.1 | On Focus | A | Pass | No context change on focus. |
| 3.2.2 | On Input | A | Pass | No surprise context change on input. |
| 3.2.3 | Consistent Navigation | AA | Pass | Nav/footer consistent across pages. |
| 3.2.4 | Consistent Identification | AA | Pass | Components identified consistently. |
| 3.2.5 | Change on Request | AAA | Pass (Manual) | New windows announced; no automatic context changes. |
| 3.2.6 | Consistent Help | A | Pass | Glossary/help in the footer on every page; contact links consistent. |
| 3.3.1 | Error Identification | A | Pass | Login: aria-invalid + role="alert". |
| 3.3.2 | Labels or Instructions | A | Pass (Auto) | All inputs labelled (Field ties label↔control + hint via aria-describedby). |
| 3.3.3 | Error Suggestion | AA | Pass | Login errors are descriptive. |
| 3.3.4 | Error Prevention (Legal/Financial) | AA | N/A | No legal/financial transactions (sign-up is an external Tally form). |
| 3.3.5 | Help | AAA | Pass | Consistent footer help + glossary; contact links. |
| 3.3.6 | Error Prevention (All) | AAA | Pass (Manual, admin) | Admin saves are explicit and reversible (re-editable); no destructive auto-submit. |
| 3.3.7 | Redundant Entry | A | Pass | No multi-step re-entry. |
| 3.3.8 | Accessible Authentication (Min) | AA | Pass (admin) | Password field; paste & password managers allowed; no cognitive test. |
| 3.3.9 | Accessible Authentication (Enhanced) | AAA | Pass (admin) | autocomplete="current-password"; no cognitive-function test. |
Principle 4 — Robust
| SC | Title | Lvl | Status | Notes |
|---|---|---|---|---|
| 4.1.2 | Name, Role, Value | A | Pass (Auto) | Burger exposes aria-expanded/aria-controls; theme toggle exposes state; form controls named. |
| 4.1.3 | Status Messages | AA | Pass | Save progress via aria-live="polite"; errors via role="alert". |
Residual risk & caveats
- 3.1.5 Reading Level (AAA) is intentionally not claimed — copy is left to the editorial team; the glossary/help mechanism is the chosen support.
- Third-party widgets — the Tally pop-up form and the
vanilla-cookieconsentbanner (#cc-main) are excluded from our own axe assertion. Both are keyboard-operable and reputable, but their internal conformance is outside this codebase. Re-verify if either is upgraded. - Automated coverage — axe confirms a subset of criteria; the Manual rows above are the ones a human verified and the ones to re-check after large visual changes.