Accessibility
What the built-ins do for readers who use a keyboard, a screen reader or voice control, what the compiler checks on every build, and what is still yours to do.
Accessibility is not a plugin here: the built-ins render the markup assistive technology expects, the router and the form manage focus, and the compiler checks every page on every build.
What you get without asking
- Semantic elements. A
Buttonis a<button>, aLinkan<a>,Heading(…).h2an<h2>,Header/Section/Footerlandmarks, aTablea<table>whose head cells are<th scope="col">, aLista<ul>or<ol>, the page's content a<main>. - A skip link, first in the tab order and visible when focused, jumping past the navigation to the content.
- Route changes that behave like page loads. Focus moves to the new page's heading, the scroll returns to the top, and the new title is announced.
- Labelled fields.
label:is a real<label for>;hint:and the error are tied to the control witharia-describedby; an error setsaria-invalidand is announced when it appears. - Forms that point at the problem. A submit that fails moves focus to the first field with an error and announces it.
- Dialogs that trap focus.
ModalandDialogare real<dialog>elements: the rest of the page is inert, Escape closes, and focus returns to what opened them. - Menus with the keys people expect. Arrows move, Home and End jump, Enter and Space choose, Escape closes.
- Announcements. A
Toastis spoken politely;WF.announce(text)says anything else. - The active link carries
aria-current="page". - Reduced motion. A reader who asked for it gets no animation at all, and a carousel does not auto-play.
- Visible focus. Every interactive built-in has a focus ring for the keyboard (
:focus-visible), not on every click.
What the compiler checks
Warnings on every build and in the editor (Diagnostics):
| Code | Checks |
|---|---|
|
|
Every |
|
|
Every |
|
|
Every |
|
|
Buttons, links and headings have text |
|
|
Every |
|
|
Video has captions and controls; audio has a transcript |
|
|
Tables have a header row |
|
|
One |
|
|
The theme's text and background colours have AA contrast |
|
|
A |
|
|
A control's |
What is still yours
The compiler can see markup; it cannot see meaning.
- Alt text that says what the picture shows, in context. "Photo" passes
A01and helps nobody. - Link text that says where it goes. Twelve "Read more" links pass
A06. - An order that makes sense. The reading order is the source order; do not rearrange it visually with
order:or absolute positioning. - Colour that is not the only signal. An error in red also says so in words; a chart has labels.
- Your own styles' contrast.
A13checks the theme's tokens, not a colour written raw in astyle { }. - Custom controls. A
Cardmade clickable withon clickis not a button to a keyboard. UseButton— or give itrole: "button",tabindex: 0andon key("Enter") { }andon key("space") { }. - Time limits. A
Toastdisappears; anything the reader must act on belongs in anAlertthat stays.
Labelling things well
wf
page Search(path: "/", title: "Search", description: "Find a page.") {
state q = ""
Heading("Search").h1
Input(bind: q, label: "Search the docs", hint: "Try “routing” or “forms”").search
IconButton(icon: "close", label: "Clear the search") { on click { q = "" } }
Button("Search", aria-label: "Search the docs") { on click { log(q) } }
}
- A visible
label:is better than a placeholder, which disappears. - An icon-only control is named by
label:(IconButton) oraria-label:. - An
aria-labelbegins with the visible words, so someone saying "click Search" hits the right button (A15).
Testing with assistive technology
- Keyboard. Put the mouse away: Tab through the page, open and close every menu and dialog, submit every form. Focus should always be visible and never lost.
- A screen reader. VoiceOver (macOS: ⌘F5), NVDA (Windows, free) or TalkBack (Android). Listen to a page's headings list and landmarks, and to what a form says when it fails.
- Zoom to 200%, and a narrow window: nothing should need horizontal scrolling.
- Reduced motion: turn it on in your system settings and reload.
- Tests that find things by name.
wf test'sclick "Save"andtype "…" into "Email"find controls the way assistive technology does, so a test that passes is a page someone can operate (Testing).
On this page
What you get without asking What the compiler checks What is still yours Labelling things well Testing with assistive technologyChecked by the test suite
Every code block in the guide is parsed, checked and type-checked on each release.