Accessibility Page
v1.0.0Added in 1.8.0
An accessibility statement: live keyboard, contrast, motion and zoom demos, WCAG 2.2 conformance, known limitations and a feedback form.
The hero rises in once beside a drawn Acme board where a focus ring travels from card to card while a caption shows what a screen reader says (still under reduced motion and in thumbnails). Download the VPAT spins while onDownload runs, then confirms and announces it; Report an issue scrolls to the form and focuses its first field. Chips under the commitment summary jump to each section. Try it yourself: Tab into the mini board, arrows move between cards (Home / End too), Space or Enter picks a card up, arrows move it between columns, Space drops and Escape cancels, with every move announced in a live region and echoed in the visible transcript (pointer users get Move left / Move right / Drop / Cancel buttons); the contrast checker switches Light / Dark / High contrast and the text color, updating the WCAG ratio, the APCA Lc score and Pass / Fail badges; the Reduce motion switch (following the system setting) replays the same transition as a spring slide or a plain fade; the Text size slider grows a card to 200% and the Text spacing switch applies WCAG 1.4.12 spacing while it reflows. The conformance table becomes cards below 768px. The feedback form validates on submit, moves focus to a GOV.UK style error summary whose links focus each field, reveals a phone field when Phone is chosen, spins while onReport runs, then shows a reference number. All controls work with the keyboard and have visible focus rings.
Built with
The sections this page is made of. In the full-screen preview, hold Alt to outline each one on the page.
Add to a project
npx wingo-ui@latest add accessibility-pagePro item: the CLI needs a Pro account. See plans
Usage
// app/(marketing)/accessibility/page.tsx
"use client"
import Link from "next/link"
import { AccessibilityPage } from "@/components/templates/accessibility-page"
export default function AccessibilityRoute() {
return (
<AccessibilityPage
linkComponent={Link}
onDownload={() => {
window.location.href = "/files/acme-vpat.pdf"
}}
onReport={async (values) => {
const response = await fetch("/api/accessibility-report", { method: "POST", body: JSON.stringify(values) })
if (!response.ok) throw new Error("Could not send the report")
}}
/>
)
}Props
designdefault"classic""classic" | "minimal" | "high-contrast"The whole page's look. classic follows the layout props; minimal: a transparent header, a centered hero without the drawn board, outlined panels, flat tiles under hairlines, no gray bands and a simple footer; high-contrast: pure black and white tokens, solid 2px outlines instead of shadows, heavier headings, full-strength secondary text and 3px focus rings. The *Props overrides still win.
factsdefault"ACCESSIBILITY_FACTS"AccessibilityFact[]{ value, label } commitment summary under the hero.
principlesdefault"ACCESSIBILITY_PRINCIPLES"AccessibilityPrinciple[]{ id, name, summary, levelA, levelAA, supports, partial, notSupported } rows of the conformance table.
limitationsdefault"ACCESSIBILITY_LIMITATIONS"AccessibilityLimitation[]{ id, title, criterion, description, workaround, status, target? }; status is in-progress, planned or ongoing, target an ISO day.
featuresdefault"ACCESSIBILITY_FEATURES"AccessibilityFeature[]{ id, icon?, title, description, chips?, shortcuts? } product features; shortcuts use mod+k notation.
stepsdefault"ACCESSIBILITY_STEPS"AccessibilityStep[]{ id, icon?, title, description, cadence } steps of the process.
quotedefault"ACCESSIBILITY_QUOTE"AccessibilityQuote | null{ quote, name, role } under the process; null hides it.
boarddefault"ACCESSIBILITY_BOARD"AccessibilityBoardColumn[]{ id, title, cards: { id, title }[] } columns of the keyboard demo.
contrastThemesdefault"ACCESSIBILITY_CONTRAST_THEMES"AccessibilityContrastTheme[]{ id, label, background, samples: { id, label, color }[] } for the contrast checker; colors are hex.
contactsdefault"ACCESSIBILITY_CONTACTS"AccessibilityContact[]{ id, icon?, title, value, href?, description } other ways to reach the team.
formatsdefault"ACCESSIBILITY_FORMATS"AccessibilityFormat[]{ id, title, description, href? } alternate formats of the statement.
statementdefault"ACCESSIBILITY_STATEMENT"AccessibilityStatement{ prepared, reviewed, nextReview, standard, method, scope }; dates are ISO days.
featureOptionsdefault"ACCESSIBILITY_FEATURE_OPTIONS"AccessibilityOption[]{ value, label } choices of the Page or feature select.
assistiveTechOptionsdefault"ACCESSIBILITY_ASSISTIVE_TECH"AccessibilityOption[]{ value, label } choices of the Assistive technology select.
scenedefault"ACCESSIBILITY_SCENE"AccessibilitySceneThe strings of the drawn hero board: { url, board, columns, captions, zoom, zoomText, highContrast }.
labelsdefault"DEFAULT_ACCESSIBILITY_PAGE_LABELS"Partial<AccessibilityPageLabels>Every visible string: headings, demo copy and announcements, table headers, form fields and errors, success copy.
heroLayoutdefault"split""split" | "centered"The hero copy beside the drawn board, or centered over it. Applies to the classic design.
defaultContrastThemedefault"light"stringThe contrast checker's theme on load: an id from contrastThemes.
onDownload() => (void | Promise<unknown>)Download the VPAT. A returned promise keeps the button loading; without it the button waits 900 ms and confirms.
onReport(values: AccessibilityReportValues) => (void | Promise<unknown>)Called with { details, feature, assistiveTech, contactBy, email, phone } once the form is valid. A returned promise keeps the button loading; a rejection shows labels.failed. Without it the form waits 900 ms and shows a reference number.
showSummarydefaulttruebooleanThe commitment summary under the hero.
showJumpLinksdefaulttruebooleanThe row of section chips.
showTrydefaulttruebooleanThe four Try it yourself demos.
showConformancedefaulttruebooleanThe conformance section.
showLimitationsdefaulttruebooleanThe known limitations inside the conformance section.
showFeaturesdefaulttruebooleanThe product features grid.
showProcessdefaulttruebooleanThe process steps and the quote.
showReportdefaulttruebooleanThe feedback form.
showContactdefaulttruebooleanOther ways to reach us, alternate formats and the statement details.
alternatedefaulttruebooleanGray bands on every other visible section. Applies to the classic design.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Icon tiles, progress bars, step numbers, the hero accents and the demos' switches and slider.
ctaTonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"Get started, Download the VPAT and Send report; brand is the marketing orange.
radiusdefault"md""none" | "sm" | "md" | "lg" | "full"The corners of the form fields.
headerdefaulttruebooleanThe marketing header; off inside a site layout.
footerdefaulttruebooleanThe marketing footer.
headerPropsPartial<MarketingHeaderProps>Passed to MarketingHeader.
footerPropsPartial<MarketingFooterProps>Passed to MarketingFooter.
heroPropsPartial<HeroSectionProps>Overrides for the HeroSection (media, background...).
animateIndefaulttruebooleanThe hero rises in once and the focus ring travels the drawn board; false keeps both still (thumbnails). Reduced motion always does.
localedefault"en-US"stringIntl locale for the dates, percentages and contrast ratios.
linkComponentdefault"<a>"(props: LinkComponentProps) => ReactNodeThe router link for every link on the page (next/link in an app).
classNamestringClasses for the root.
classNamesPartial<Record<AccessibilityPageSlot, string>>Classes for header, hero, summary, jump, try, demo, conformance, limitations, features, process, report, form, success, contact, statement and footer.
Parts
contrastRatio
functioncontrastRatio(foreground, background): the WCAG 2 contrast ratio of two hex colors.
apcaContrast
functionapcaContrast(foreground, background): the APCA 0.0.98G lightness contrast (Lc), negative for light text on dark.
ACCESSIBILITY_PAGE_SAMPLE
constantAll the sample content in one object.
ACCESSIBILITY_FACTS
constantThe four commitment facts.
ACCESSIBILITY_PRINCIPLES
constantPerceivable, Operable, Understandable and Robust with WCAG 2.2 A and AA counts (55 criteria).
ACCESSIBILITY_LIMITATIONS
constantFour known limitations with workarounds and target dates.
ACCESSIBILITY_FEATURES
constantEight accessibility features of the product.
ACCESSIBILITY_STEPS
constantDesign reviews, CI tests, manual audits and research.
ACCESSIBILITY_QUOTE
constantA quote from Olivia Bennett, Head of Design.
ACCESSIBILITY_BOARD
constantThe three columns of the keyboard demo.
ACCESSIBILITY_CONTRAST_THEMES
constantLight, dark and high contrast palettes for the checker.
ACCESSIBILITY_CONTACTS
constantEmail, phone with relay and postal address in New York.
ACCESSIBILITY_FORMATS
constantLarge print, plain text and easy read.
ACCESSIBILITY_STATEMENT
constantPrepared, reviewed and next review dates, standard, method and scope.
ACCESSIBILITY_FEATURE_OPTIONS
constantThe Page or feature choices.
ACCESSIBILITY_ASSISTIVE_TECH
constantThe Assistive technology choices.
ACCESSIBILITY_SCENE
constantThe strings of the drawn hero board.
DEFAULT_ACCESSIBILITY_PAGE_LABELS
constantThe English strings.
ACCESSIBILITY_PAGE_DESIGNS
constantThe design names, classic first: ["classic", "minimal", "high-contrast"].
AccessibilityPageProps
typeProps of AccessibilityPage.
AccessibilityPageDesign
type"classic" | "minimal" | "high-contrast".
AccessibilityPageLabels
typeThe strings labels can override.
AccessibilityReportValues
type{ details, feature, assistiveTech, contactBy, email, phone } passed to onReport.
AccessibilityPrinciple
typeOne row of the conformance table.
AccessibilityLimitation
typeOne known limitation.
AccessibilityFeature
typeOne feature card.
AccessibilityContrastTheme
typeOne theme of the contrast checker.
Examples
High contrast design
design="high-contrast": pure black and white tokens, solid 2px outlines instead of shadows, heavier headings and 3px focus rings.
Minimal design
design="minimal": a transparent header, a centered hero without the drawn board, outlined panels, no gray bands and a simple footer.
Brand orange, centered hero
heroLayout centered with tone and ctaTone brand; the contrast checker opens on the dark theme.
Statement only
Without the demos, the features and the process: the conformance table, the form and the contact details, in en-GB.
When the report fails
onReport rejects: after the spinner the form keeps everything and shows the error above the button.
Dependencies
Files
- components/templates/accessibility-page.tsx
- components/templates/accessibility-page-data.ts