Docs

Accessibility

How Proa catches common WCAG, WAI-ARIA, and ARIA-in-HTML issues at compile time.

Open Markdown

Proa checks accessibility rules at compile time via the html! macro. No separate linter to install, no runtime cost, no CI step to forget. Violations are caught the same way type errors are, during cargo build.

What Proa Checks

Proa's compile-time checks cover three specs:

Text Alternatives (WCAG 1.1.1)

Every non-text content element needs a text alternative for screen readers.

// Warning: <img> without alt
html! { <img src="/photo.jpg" /> }
//       ^^^^ missing `alt` attribute — screen readers cannot describe this image.
//            Use alt="description" or alt="" for decorative images.

// OK: descriptive alt
html! { <img src="/photo.jpg" alt="Team photo from the company retreat" /> }

// OK: decorative image (empty alt)
html! { <img src="/divider.svg" alt="" /> }

This also applies to <input type="image"> (must have alt) and <area> with href (must have alt).

ARIA Role Validation

The role attribute must be one of the 82 roles defined in WAI-ARIA 1.2. Abstract roles (like widget, landmark, structure) cannot be used directly.

// Error: invalid role
html! { <div role="buttn">"Click"</div> }
//                 ^^^^^^ "buttn" is not a valid ARIA role. Did you mean "button"?

// Error: abstract role
html! { <div role="widget">"Click"</div> }
//                 ^^^^^^^ "widget" is an abstract role and cannot be used in content.
//                          Use a concrete role like "button", "checkbox", or "slider".

Required ARIA Attributes

Some roles require specific ARIA attributes to function correctly with assistive technology:

// Error: role="checkbox" requires aria-checked
html! { <div role="checkbox">"Accept terms"</div> }
//       ^^^^ role="checkbox" requires the `aria-checked` attribute.
//            Add aria-checked="true", aria-checked="false", or aria-checked="mixed".

// OK
html! {
    <div role="checkbox" aria_checked="false">"Accept terms"</div>
}
RoleRequired attribute(s)
checkboxaria-checked
comboboxaria-controls, aria-expanded
headingaria-level
menuitemcheckboxaria-checked
menuitemradioaria-checked
meteraria-valuenow
optionaria-selected
radioaria-checked
scrollbararia-valuenow
separator (with tabindex)aria-valuenow
slideraria-valuenow
spinbuttonaria-valuenow
switcharia-checked

ARIA Attribute Validation

ARIA attribute names must be ones the macro recognizes (the 48 defined in WAI-ARIA 1.2, plus the newer braille and description attributes), and enumerated values must be valid:

// Error: not a real ARIA attribute
html! { <div aria_labeledby="title">"content"</div> }
//            ^^^^^^^^^^^^^^^ "aria-labeledby" is not a valid ARIA attribute.
//                             Did you mean "aria-labelledby"?

// Error: invalid value
html! { <div aria_hidden="yes">"content"</div> }
//                        ^^^^ "yes" is not a valid value for aria-hidden.
//                              Use "true" or "false".

Redundant Roles

HTML elements have implicit ARIA roles. Adding an explicit role that matches is unnecessary noise:

// Warning: redundant role
html! { <button role="button">"Save"</button> }
//               ^^^^^^^^^^^^^ <button> already has an implicit role of "button".
//                              Remove the role attribute.

// Warning: prefer native element
html! { <div role="button" tabindex="0">"Save"</div> }
//            ^^^^^^^^^^^^^ prefer <button> over <div role="button"> —
//                           native elements provide keyboard support and
//                           accessibility semantics for free.

Tabindex

Positive tabindex values disrupt the natural tab order and are almost always wrong:

// Warning: positive tabindex
html! { <input tabindex="5" /> }
//              ^^^^^^^^^^^^^ tabindex="5" overrides the natural document tab order.
//                            Use tabindex="0" to add to natural order, or
//                            tabindex="-1" for programmatic focus only.

Language

Screen readers use the lang attribute to determine pronunciation rules:

// Warning: <html> without lang
html! {
    <html>
        <head><title>"My Page"</title></head>
    </html>
}
// ^^^^^ <html> should have a `lang` attribute (e.g., lang="en").
//       Screen readers use this to select the correct pronunciation rules.

What Proa Cannot Check

These accessibility requirements depend on runtime behavior, computed CSS, or cross-component context that the html! macro can't see:

ConcernWhyWhat to use
Color contrast ratiosRequires computed CSS stylesaxe-core, Lighthouse
Keyboard navigationRequires runtime focus testingManual testing, axe-core
Focus orderRequires runtime DOMaxe-core
Focus visibilityRequires CSS + runtimeBrowser DevTools
Screen reader announcementsRequires assistive technologyNVDA, VoiceOver
Dynamic ARIA state changesRequires runtime JSaxe-core
Cross-component nestingFragments composed at different call sitesNu HTML Checker

Proa's compile-time checks cover what's visible in the HTML output. For the full picture, pair them with axe-core in your integration tests:

// Example: axe-core in Playwright
import AxeBuilder from "@axe-core/playwright";

test("home page is accessible", async ({ page }) => {
  await page.goto("/");
  const results = await new AxeBuilder({ page }).analyze();
  expect(results.violations).toEqual([]);
});

Standards Reference

StandardWhat it coversLink
WCAG 2.2Success criteria for accessible web content (A, AA, AAA)w3.org/TR/WCAG22
WAI-ARIA 1.2Roles, states, and properties for rich UI widgetsw3.org/TR/wai-aria-1.2
ARIA in HTMLWhich ARIA is valid on which HTML elementsw3.org/TR/html-aria
HTML-AAMHow HTML maps to platform accessibility APIsw3.org/TR/html-aam-1.0
Section 508US federal accessibility law (references WCAG 2.0 AA)section508.gov
EN 301 549EU accessibility standard (references WCAG 2.1 AA)etsi.org

Search

Type at least 2 characters