RAWeb 1.1 — Accessible Code Development Guide
You are an accessibility-aware developer. Every piece of HTML, CSS, and JavaScript
you write MUST conform to RAWeb 1.1 (Level AA by default). RAWeb is Luxembourg's
official web accessibility framework implementing EN 301 549 and WCAG 2.1.
How to use the reference data
The full RAWeb 1.1 criteria, test methodologies, and glossary are available as
JSON files. Use the lookup script to query specific criteria on demand:
# List all topics
!`${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh topics`
# Look up a specific criterion
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh criterion 1.1
# Look up test methodology
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh methodology 1.1.1
# Search criteria by keyword
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh search "form"
# Check glossary definition
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh glossary "text alternative"
The raw JSON reference files are located at:
${CLAUDE_SKILL_DIR}/references/criteres.json — All 136+ criteria with tests and WCAG/EN 301 549 mappings
${CLAUDE_SKILL_DIR}/references/methodologies.json — Step-by-step test procedures
${CLAUDE_SKILL_DIR}/references/glossaire.json — Glossary of accessibility terms
${CLAUDE_SKILL_DIR}/references/themes.json — Topic names
${CLAUDE_SKILL_DIR}/references/niveaux.json — WCAG conformance levels per criterion
When writing code for a specific component, ALWAYS look up the relevant RAWeb
criteria first to ensure full compliance. For example, before writing a form,
run search "form" and topic 11.
Core rules (apply to ALL code you write)
1. Images (Topic 1)
ALWAYS:
- Every
<img> conveying information MUST have a meaningful alt attribute (1.1)
- Every decorative
<img> MUST have alt="" and no title, aria-label, or aria-labelledby (1.2)
- Every
<svg> conveying information MUST have role="img" — this one is mandatory, 1.1.5 invalidates the
test without it — plus a text alternative via aria-label, aria-labelledby, or a <title> child (1.1)
- Decorative
<svg> MUST have aria-hidden="true" (1.2)
- Every text alternative MUST be relevant — it must convey what the image conveys (1.3), and short and
concise (test 1.3.9). RAWeb's glossary defines short and concise as "a maximum length of 80 characters
is strongly recommended" — the 80 is a strong recommendation, but brevity itself is tested by 1.3
- Complex images (charts, infographics) need a detailed description (1.6) — but the accepted routes differ
by element type:
<img>, <object>, <embed>: only two routes — a text alternative referencing an adjacent detailed
description, or an adjacent link/button to it. aria-describedby is not a route here (1.6.1–1.6.3)
<input type="image">, <svg>, role="img": aria-describedby is accepted, alongside
aria-label/aria-labelledby and the adjacent link/button (1.6.4, 1.6.5, 1.6.10)
<canvas>: also accepts a detailed description placed between <canvas> and </canvas> (1.6.7)
- Whichever you use, it must actually resolve for AT — tests 1.6.6, 1.6.8 and 1.6.9 validate the wiring
rather than grant a route
- Avoid images of text unless a replacement mechanism exists, or the visual effect cannot be reproduced
in CSS — 1.8 accepts either escape (1.8 — Level AA)
- Where an image has a visible caption, that caption MUST be correctly linked to the image —
<figure>/<figcaption> (1.9)
NEVER:
- Leave
alt undefined on an informative image
- Use
alt="image", alt="photo", alt="icon" — describe what the image conveys
- Use
title as the sole text alternative (poor assistive technology support)
<!-- GOOD: informative image -->
<img src="chart.png" alt="Sales increased 40% in Q3 2024">
<!-- GOOD: decorative image -->
<img src="decoration.svg" alt="">
<!-- GOOD: complex image with detailed description -->
<figure>
<img src="orgchart.png" alt="Company organisation chart. Full description below.">
<figcaption>
<details>
<summary>Full description of the organisation chart</summary>
<p>The CEO reports to the board. Three departments report to the CEO...</p>
</details>
</figcaption>
</figure>
<!-- GOOD: SVG informative -->
<svg role="img" aria-label="Warning: connection unstable">
<use href="#icon-warning"/>
</svg>
<!-- GOOD: SVG decorative -->
<svg aria-hidden="true" focusable="false"><use href="#flourish"/></svg>
2. Frames (Topic 2)
- Every
<iframe> MUST have a descriptive title attribute (2.1)
- The
title must be relevant to the frame content (2.2)
<iframe src="map.html" title="Interactive map of our office locations"></iframe>
3. Colours (Topic 3)
- Information must NEVER be conveyed by colour alone (3.1) — and RAWeb requires both complements, not
either: additional information in the code (
aria-label, visually hidden text, aria-current…)
and an additional visual cue (icon, shape, position, typographic effect). An icon alone does not
discharge 3.1. Applies to text, colour named in text ("click the green button"), images, CSS
properties, and media
- Text contrast ratio: at least 4.5:1, or 3:1 for large text — which RAWeb defines in px:
≥24px non-bold, ≥18.5px bold (3.2 — Level AA)
- Non-text elements (icons, borders, UI components): at least 3:1 contrast against adjacent colours (3.3)
- Both 3.2 and 3.3 also accept a mechanism that lets the user display a compliant ratio; if you ship one,
it must itself be compliant (3.2.5, 3.3.4)
<!-- BAD: colour alone conveys meaning -->
<span class="text-red">Required field</span>
<!-- GOOD: colour + text indicator -->
<span class="text-red">Required field *</span>
<span class="sr-only">(required)</span>
<!-- Or better with native HTML -->
<input type="text" required aria-required="true">
4. Multimedia (Topic 4)
- Pre-recorded video with audio MUST have captions (4.3 — captions are 4.3, not 4.1; 4.1 is the
transcript/audio-description criterion). Captions delivered via
<track> MUST use kind="captions"
(4.3.2). Live synchronised media needs captions too (4.3.3)
- Pre-recorded audio MUST have a text transcript (4.1)
- Pre-recorded video MUST have audio description if visual information is not in the audio track (4.5, 4.6 — Level AA)
- Every automatically triggered sound MUST be controllable by the user — stop it, or control its volume
independently of the system volume (4.10). Exempt if the sequence lasts 3 seconds or less
- Media viewing controls MUST exist (4.11.1): at least play/pause or stop; sound on/off if there is
sound; a captions toggle if there are captions; an audio-description toggle if there is AD. Each MUST
then be operable by keyboard AND any pointing device (4.11.2/4.11.3)
- Time-based and non-time-based media MUST be compatible with assistive technologies (4.13)
- Interactive
<canvas>/<svg> content is non-time-based media: it MUST have an alternative reachable
via a clearly identifiable adjacent link or button (4.8), that alternative MUST give the same content and
similar features (4.9), and its controls MUST be keyboard- and pointer-operable (4.12)
These have no WCAG equivalent — they are EN 301 549 §7 requirements that no WCAG-based scanner reports:
- Caption and audio-description toggles MUST be presented at the same level as play/pause — not buried
one extra click deep in a menu (4.14 — Level AA)
- Any transmit/convert/record feature MUST preserve captions (4.15) and audio description (4.16) — Level AA
- Caption presentation MUST be user-controllable: honour the OS caption settings, or provide an on-page
control (4.17 — Level AA)
- Subtitles MUST be vocalisable — a spoken-subtitle track, a TTS feature, or an alternative version
(4.18 — Level AA)
<video controls>
<source src="presentation.mp4" type="video/mp4">
<track kind="captions" src="captions-en.vtt" srclang="en" label="English" default>
<track kind="descriptions" src="descriptions-en.vtt" srclang="en" label="English audio descriptions">
</video>
5. Tables (Topic 5)
- Every complex data table MUST have a summary (5.1) — in the
<caption> or a passage of text linked by
aria-describedby, not the obsolete summary attribute — and that summary must be relevant (5.2).
A table is "complex" when its headers are not confined to the first row and/or first column, or a header's
scope is not valid for the whole row/column
- For each layout table, the linearised content must remain comprehensible and the
<table> MUST
carry role="presentation" — 5.3 requires both
- Where a data table has a title, it MUST be correctly associated —
<caption>, aria-label, or aria-labelledby (5.4) — and the title must be relevant (5.5)
- Data tables MUST declare headers that span a whole column/row with
<th> or role="columnheader"/
role="rowheader" (5.6.1/5.6.2). A header that does not span the whole row/column must use <th> —
no ARIA route there (5.6.3)
- Each cell MUST be associated with its headers:
scope for simple tables, headers/id for complex ones (5.7).
Use scope="rowgroup"/scope="colgroup" for header groups (5.7.6). A <th> that does not span the
whole row/column must have a unique id and must not carry scope or a header role (5.7.3)
- Layout tables MUST NOT use
<th>, <caption>, <thead>, <tfoot>, role="rowheader"/"columnheader",
a summary attribute, or scope/headers/axis on cells (5.8)
Note: 5.4 requires correct association of a title that exists — it does not
itself mandate one. In practice always give a data table a <caption>; just
don't cite 5.4 as the reason.
<!-- GOOD: simple data table -->
<table>
<caption>Q3 2024 Revenue by Region</caption>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col">Revenue</th>
<th scope="col">Growth</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Europe</th>
<td>€2.4M</td>
<td>+12%</td>
</tr>
</tbody>
</table>
6. Links (Topic 6)
- Every link MUST have an accessible name (6.2), and that name must be explicit (6.1)
- Links with identical accessible names MUST lead to the same destination, or be distinguished by their
context (6.1.6)
- The link's accessible name must be relevant and describe the destination or function (6.1)
- Avoid ambiguous link text: never use "click here", "read more", "learn more" without context (6.1)
- If the visible text is not sufficient, add context via
aria-label or aria-labelledby — but the
aria-label MUST include the visible text (6.1, test 6.1.5 — Label in Name)
<!-- BAD -->
<a href="/report.pdf">Click here</a>
<!-- GOOD -->
<a href="/report.pdf">Download the Q3 2024 financial report (PDF, 2.4 MB)</a>
<!-- GOOD: contextual with aria-label containing visible text -->
<article>
<h3>RAWeb 1.1 released</h3>
<p>The new version adds 17 additional criteria...</p>
<a href="/news/raweb" aria-label="Read more about RAWeb 1.1 released">Read more</a>
</article>
7. Scripts (Topic 7)
Every script-driven UI MUST be compatible with assistive technologies — correct role, name, and value (7.1)
Where a script has an alternative, that alternative MUST be relevant (7.2)
Every script MUST be accessible and operable by keyboard AND any pointing device (7.3)
Any script that initiates a change of context MUST warn the user beforehand or leave them in control (7.4)
Status messages MUST be communicated without moving focus (7.5 — Level AA). RAWeb maps message type to
technique — the wrong role for the type fails the test:
| Message type |
Use (7.5.1–7.5.3) |
| Success, result of an action, app state |
role="status" or aria-live="polite" + aria-atomic="true" |
| A suggestion, or warning of an error |
role="alert" or aria-live="assertive" + aria-atomic="true" |
| Progress of a process |
role="log", role="progressbar" or role="status" (or the matching aria-live="polite") |
Every aria-live branch requires aria-atomic="true" alongside it — bare aria-live="polite" fails
7.5.1. aria-relevant is not a technique 7.5 recognises; it satisfies nothing here
A script MUST NOT remove focus from an element that receives it (7.3.2)
Related criteria that live in other topics — do not cite them as Topic 7:
- Moving or blinking content must be controllable (pause, stop, hide) → 13.8
- Sudden changes in brightness / flashing → 13.7
- Keyboard traps → 12.9
<!-- GOOD: live region for status updates -->
<div role="status" aria-live="polite" aria-atomic="true">
<p>3 results found.</p>
</div>
<!-- GOOD: alert for important messages -->
<div role="alert">
<p>Your session will expire in 2 minutes.</p>
</div>
// GOOD: keyboard + pointer support
button.addEventListener('click', handler); // covers mouse + Enter/Space
button.addEventListener('keydown', (e) => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
handler(e);
}
});
8. Mandatory Elements (Topic 8)
- Every page MUST have a valid
<!DOCTYPE html> (8.1)
- Every page MUST have a
lang attribute on <html> (8.3); its code must be valid and relevant to the
page's main language (8.4)
- Language changes in content MUST be marked with
lang on the relevant element (8.7 — Level AA)
- Every page MUST have a unique and relevant
<title> (8.5, 8.6)
- In an SPA, update the page
<title> when the view changes — a title left at "Home" across views stops
being relevant, which is what 8.6 tests (8.6 tests relevance; the update rule is the practical consequence)
- No duplicate
id attributes in the page (8.2)
- Opening and closing tags must be used per specification; nesting must be valid (8.2 — source-code
validity. 8.1 is only the presence of a document type)
- Text whose reading direction differs from the page default must sit in a tag with a
dir attribute
(8.10), and that value must be a valid, relevant rtl/ltr (8.10.2)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Contact Us — Acme Corp</title>
</head>
<body>
<p>Visit our <span lang="fr">bureau principal</span> in Luxembourg.</p>
</body>
</html>
9. Information Structure (Topic 9)
- Use headings (
<h1>–<h6>, or role="heading" + aria-level) with a logical hierarchy (9.1.1). Each
heading's text must be relevant (9.1.2), and every passage of text that acts as a heading must be marked
up as one (9.1.3) — a styled <div class="title"> fails 9.1.3 even when the hierarchy is clean
- Use the landmarks 9.2.1 actually requires:
<header>→banner, <nav>→navigation,
<search>→search, <main>→main, <footer>→contentinfo. (<aside>/complementary is fine to
use but is not required by 9.2)
- Exactly one visible
main, banner and contentinfo per page, and reserve navigation for primary
and secondary navigation only (9.2.2)
- A
<header>/<footer> without an explicit role must not be nested inside article, complementary,
main, navigation or section (9.2.3/9.2.4) — a <header> inside <main> fails
- Use lists (
<ul>, <ol>, <dl>) for list content (9.3)
- Use
<q> for short inline quotations (9.4.1) and <blockquote> for quotation blocks (9.4.2). cite is
good practice — 9.4 does not test it
<body>
<header role="banner">
<nav aria-label="Main navigation">...</nav>
</header>
<main role="main">
<h1>Page Title</h1>
<section aria-labelledby="section-heading">
<h2 id="section-heading">Section</h2>
<h3>Sub-section</h3>
</section>
</main>
<footer role="contentinfo">...</footer>
</body>
10. Presentation of Information (Topic 10)
- Content must remain readable and functional at 200% zoom (10.4 — Level AA)
- Content must reflow at 320px CSS width without horizontal scrolling — and, for vertically-read content, at
256px height without vertical scrolling (10.11 — Level AA)
- Use CSS for all visual presentation. 10.1 forbids an enumerated set —
<font>, <center>, <big>,
<blink>, <marquee>, <s>, <strike>, <tt> and attributes like align, bgcolor, border,
cellpadding, hspace, valign, vspace. width/height remain allowed on <canvas>, <embed>,
<iframe>, <img>, <object>, <source>, <svg> (10.1.2) — keep them on images to prevent layout shift
- Never use spaces to separate letters in a word, or to simulate tables or columns (10.1.3)
- Visible content that conveys information must be reachable by AT — a CSS background image carrying meaning
needs a text alternative on the page (10.2). Separately, the reading order must stay understandable
with CSS disabled (10.3)
- Visible focus indicator on all interactive elements, with a contrast ratio ≥ 3:1 (10.7)
- Do not hide content in a way that becomes unreachable (10.8)
- Text spacing: users must be able to override line-height (1.5×), paragraph spacing (2×), letter-spacing (0.12em), word-spacing (0.16em) without loss of content (10.12 — Level AA)
/* GOOD: visible focus indicator */
:focus-visible {
outline: 3px solid #0056b3;
outline-offset: 2px;
}
/* GOOD: ensure reflow support */
.container {
max-width: 100%;
overflow-wrap: break-word;
}
/* NEVER: hide focus outline globally */
/* *:focus { outline: none; } ← FORBIDDEN */
11. Forms (Topic 11)
This is a critical topic. Always look up criteria with bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh topic 11 when implementing forms.
- Every form field MUST have a programmatically associated label (11.1). Always give it a visible one
too — that is 11.4 (label next to its field) plus good practice; 11.1 itself also accepts
aria-label
or title with nothing visible
- Label association:
<label for="id">, aria-labelledby, aria-label, or title (11.1)
- The
<label> element with for attribute is the preferred method (11.1)
- The accessible name MUST include the visible label text (11.2)
- Labels must be relevant — clearly describe the expected input (11.2)
- Labels for fields with the same function MUST stay consistent across the set of pages (11.3 — Level AA)
- The label MUST sit next to its field (11.4): immediately above or left for normal fields (LTR), and
immediately below or right for
checkbox, radio, and role="switch"
- Related fields MUST be grouped —
<fieldset>, role="group", or role="radiogroup" for radios (11.5)
- Every group MUST have a legend (11.6):
<legend> in a <fieldset>, or aria-label/aria-labelledby
on a role="group"/role="radiogroup". The legend must be relevant (11.7)
- Group
<select> options of the same type with <optgroup>, each with a relevant label (11.8)
- Button labels MUST be relevant, and the accessible name MUST contain at least the visible label (11.9)
- Required fields must be indicated before the form or at the field level (11.10)
- Use
required and/or aria-required="true" for mandatory fields (11.10) — and keep a visible
indication: the attribute alone passes 11.10.1 but fails 11.10.2, which applies specifically to fields
carrying required/aria-required and demands a visible indication in the label, an associated passage
of text, or the legend
- Error messages MUST identify the field by name and be linked to it via
aria-invalid="true"
(11.10 — Level A)
- Error messages MUST also suggest the expected data type/format and examples of valid values
(11.11 — Level AA)
- Forms that modify or delete data, submit exam answers, or carry financial/legal consequences MUST be
reversible, checkable before submit, or explicitly confirmed (11.12 — Level AA)
- Input purpose for personal data must use
autocomplete with appropriate values (11.13 — Level AA)
Note: error identification is 11.10 (Level A); 11.11 is only error suggestion (AA). Linking a
message to its field is 11.10, not 11.11 — filing it under 11.11 downgrades a Level A requirement.
<legend> is 11.6, not 11.5 — 11.5 is the grouping alone.
<form novalidate>
<fieldset>
<legend>Personal information</legend>
<div>
<label for="fname">First name <span aria-hidden="true">*</span></label>
<input type="text" id="fname" name="fname"
autocomplete="given-name"
required aria-required="true"
aria-describedby="fname-error">
<p id="fname-error" role="alert" class="error" hidden>
Please enter your first name.
</p>
</div>
<div>
<label for="email">Email address <span aria-hidden="true">*</span></label>
<input type="email" id="email" name="email"
autocomplete="email"
required aria-required="true"
aria-describedby="email-hint email-error">
<p id="email-hint" class="hint">Format: name@example.com</p>
<p id="email-error" role="alert" class="error" hidden>
Please enter a valid email address (e.g., name@example.com).
</p>
</div>
</fieldset>
<button type="submit">Submit</button>
</form>
12. Navigation (Topic 12)
- At least two navigation mechanisms among: main nav, sitemap, search engine (12.1 — Level AA)
- Repeated content blocks must be reachable or avoidable (12.6) — a landmark role, a heading, a hide
button, or a skip link all satisfy it
- A skip link to the main content region must be present and functional (12.7)
- Skip links must be visible on focus (12.7)
- Tab order must be logical and consistent (12.8), and must stay consistent after a script inserts or
updates content — reposition focus correctly (12.8.2)
- Avoid positive
tabindex values — they almost always break the coherent order 12.8.1 requires. (12.8
tests the order, not the attribute; RAWeb notes the sequence need not follow natural reading order as
long as it stays coherent.)
- Navigation MUST NOT contain keyboard traps (12.9) — a modal's Tab loop is legal only because Escape and a close button let the user out
- Single-key shortcuts must be remappable or disableable (12.10)
- Content appearing on hover or focus (tooltips, popovers) must be keyboard accessible (12.11 — Level AA)
- Menus and navigation bars must stay in the same place in the presentation and the same relative
order in the source across the set of pages (12.2 — Level AA)
- Indicating the active page (e.g.
aria-current="page") is good practice, not a RAWeb criterion — 12.2
only tests consistent placement. Do it, but don't cite 12.2 for it
<body>
<!-- Skip link: first focusable element -->
<a href="#main-content" class="skip-link">Skip to main content</a>
<nav aria-label="Main navigation">
<ul>
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
<main id="main-content" tabindex="-1">
<!-- Page content -->
</main>
</body>
.skip-link {
position: absolute;
top: -100%;
left: 0;
z-index: 1000;
padding: 0.5rem 1rem;
background: #000;
color: #fff;
}
.skip-link:focus {
top: 0;
}
13. Consultation (Topic 13)
- The user must have control over every time limit that modifies content — adjustable, extendable, or removable (13.1)
- A
<meta http-equiv="refresh"> redirect admits no user control: it must be immediate (content="0")
or at least twenty hours (content ≥ 72000) (13.1.2)
- A new window MUST NOT open without a user action (13.2)
- Every downloadable office document must have an accessible version (13.3), and that version must offer the same information (13.4)
- Sudden changes in brightness or flashing must be used correctly — no more than 3 flashes per second, or
a cumulative effect area ≤ 21824 px² (13.7). Applies to media, scripted effects, and CSS animation alike
- Moving or blinking content must be controllable by the user: stop/restart, show/hide, or offer the same
information without the movement (13.8). Exempt if it lasts ≤ 5 seconds. Pausing on focus only does
not count; content that cannot be stopped (a progress bar) is NA
- Content must be viewable in any screen orientation (13.9 — Level AA)
- Any feature using a complex gesture must also be usable with a simple gesture (13.10) — this covers
multi-touch (13.10.1) and path-based gestures (13.10.2). A drag with no single-point
alternative — a slider that only drags, a splitter with no reset — fails 13.10.2. Widely missed, because
13.10 reads as though it were only about pinch
- Single-point actions must be cancellable (13.11): trigger on release, or on press-then-cancel-on-
release, or provide an abort/undo mechanism
- Motion-actuated features must work via UI components instead (13.12), and the user must be able to
disable motion detection entirely (13.12.3)
- Biometric identification or control (fingerprint, face, voice) MUST have an alternative — non-biometric,
or a sufficiently different biological characteristic (13.14). EN 301 549 §5.3, Level A, no WCAG
equivalent
- Document conversion features must preserve accessibility information (13.13 — Level AA, EN 301 549 §5.4)
Note: indicating a link's file format and size is good practice, not a RAWeb
criterion — do it, but do not cite 13.3 for it. Warning the user before a
context change is 7.4, not 13.1.
<!-- GOOD: file download with format and size -->
<a href="/report.pdf">
Annual report 2024 <span class="file-info">(PDF, 3.2 MB)</span>
</a>
<!-- GOOD: new window warning -->
<a href="https://external.example.com" target="_blank"
rel="noopener noreferrer">
External resource
<span class="sr-only">(opens in a new window)</span>
</a>
Component patterns (WAI-ARIA APG)
When building interactive components, look up the correct ARIA pattern from the
local reference data BEFORE writing any code. The component library contains 30
patterns extracted from the WAI-ARIA Authoring Practices Guide
with keyboard interactions, required/optional ARIA attributes, and implementation notes.
How to use
# Find the right pattern by keyword (e.g., "modal", "dropdown", "toggle")
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh find "<keyword>"
# Show the pattern contract as markdown tables:
# RAWeb criteria + keyboard interaction + ARIA roles/attributes/states
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh show <slug>
# Show working code examples (do + don't) for one framework
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh code <slug> [vanilla|react|angular|web-component]
# List all 30 available patterns
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh list
# Find patterns that use a specific ARIA role
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh roles "<role>"
Workflow
- Identify the component the developer is building (dialog, tabs, slider, etc.)
- Search using
find with the most natural keyword the developer used
- Load the contract with
show <slug> — the RAWeb criteria that apply, the
keyboard interaction, and every ARIA attribute with its allowed values/states
- Load the code with
code <slug> — read the whole pattern file the first
time; use code <slug> <framework> when you already know the universal rules
- Apply the pattern, preferring native HTML elements over ARIA roles
- Cross-reference anything the pattern doesn't cover against the criteria —
especially Topics 7 (Scripts), 11 (Forms), and 12 (Navigation)
If a pattern has no code examples yet, code tells you so and lists what does.
Fall back to show <slug> — the contract is complete for all 30 patterns.
Where the data lives
| Path |
Holds |
Status |
references/components/<slug>.json |
Source of truth: ARIA roles/attributes/values, keyboard interaction, RAWeb criteria mapping |
All 30 patterns |
references/patterns/<slug>.md |
Code examples: vanilla, React, Angular, Web Component, each with dos and don'ts |
Growing — see code output |
references/patterns/_TEMPLATE.md |
Authoring contract for adding a new pattern file |
— |
Criterion levels and official titles are resolved at render time from
niveaux.json and criteres.json, so a pattern can never quote a stale level.
Never retype an ARIA table into a .md — add it to the JSON and let show
render it.
Quick mapping: common requests → pattern slugs
| When the developer says... |
Look up slug |
| modal, dialog, popup, overlay, lightbox |
dialog-modal |
| tabs, tab panel, tabbed interface |
tabs |
| accordion, collapsible, FAQ |
accordion |
| dropdown menu, context menu, submenu |
menubar or menu-button |
| toast, notification, flash message |
alert |
| confirm dialog, delete confirmation |
alertdialog |
| carousel, slideshow, image gallery |
carousel |
| autocomplete, typeahead, combobox |
combobox |
| select, dropdown list, listbox |
listbox |
| breadcrumb, navigation trail |
breadcrumb |
| tooltip, hint, hover text |
tooltip |
| toggle, switch, on/off |
switch |
| slider, range, volume control |
slider or slider-multithumb |
| tree, file browser, nested list |
treeview |
| data grid, spreadsheet, editable table |
grid |
| static table, data table |
table |
| toolbar, action bar, button group |
toolbar |
| radio buttons, option group |
radio |
| checkbox, check all |
checkbox |
| show/hide, details, read more |
disclosure |
| spinner, number input, stepper |
spinbutton |
| meter, gauge, battery level |
meter |
| feed, infinite scroll, timeline |
feed |
| page structure, landmarks |
landmarks |
| split view, resizable panes |
windowsplitter |
Screen-reader-only utility class
Always include and use this utility in your CSS:
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
Pre-commit checklist (apply before finalizing any code)
Before considering any code complete, verify:
When in doubt
- Look up the RAWeb criterion:
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh criterion <topic.criterion>
- Check the test methodology:
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh methodology <topic.criterion.test>
- Look up the ARIA pattern for a component:
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh find "<keyword>", then show <slug> for the contract and code <slug> for worked examples
- Consult the glossary for precise definitions:
bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh glossary "<term>"
- Default to the most accessible approach — when two implementations are possible, choose the one with better assistive technology support
- Prefer native HTML semantics over ARIA: a
<button> is better than <div role="button">
- When building an interactive widget, ALWAYS load the APG pattern first — do not guess ARIA attributes from memory
- Never cite a criterion number from memory. RAWeb numbering is not WCAG
numbering: RAWeb 7.3 is keyboard operability, WCAG 2.1.1 is. Verify with
criterion <x.y> before writing a number into code, a comment, or a report
1---2name: raweb-code3description: Write accessible HTML, CSS, and JavaScript that conforms to RAWeb 1.1 (Luxembourg Web Accessibility Framework, based on EN 301 549 / WCAG 2.1). Use when developing web interfaces, components, forms, navigation, or any front-end code that must meet Luxembourg accessibility law. Covers all 17 RAWeb themes with code-level guidance, accessible patterns, and anti-patterns. Default conformance target: Level AA.4---56# RAWeb 1.1 — Accessible Code Development Guide78You are an accessibility-aware developer. Every piece of HTML, CSS, and JavaScript9you write MUST conform to **RAWeb 1.1** (Level AA by default). RAWeb is Luxembourg's10official web accessibility framework implementing EN 301 549 and WCAG 2.1.1112## How to use the reference data1314The full RAWeb 1.1 criteria, test methodologies, and glossary are available as15JSON files. Use the lookup script to query specific criteria on demand:1617```bash18# List all topics19!`${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh topics`2021# Look up a specific criterion22bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh criterion 1.12324# Look up test methodology25bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh methodology 1.1.12627# Search criteria by keyword28bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh search "form"2930# Check glossary definition31bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh glossary "text alternative"32```3334The raw JSON reference files are located at:35- `${CLAUDE_SKILL_DIR}/references/criteres.json` — All 136+ criteria with tests and WCAG/EN 301 549 mappings36- `${CLAUDE_SKILL_DIR}/references/methodologies.json` — Step-by-step test procedures37- `${CLAUDE_SKILL_DIR}/references/glossaire.json` — Glossary of accessibility terms38- `${CLAUDE_SKILL_DIR}/references/themes.json` — Topic names39- `${CLAUDE_SKILL_DIR}/references/niveaux.json` — WCAG conformance levels per criterion4041When writing code for a specific component, ALWAYS look up the relevant RAWeb42criteria first to ensure full compliance. For example, before writing a form,43run `search "form"` and `topic 11`.4445---4647## Core rules (apply to ALL code you write)4849### 1. Images (Topic 1)5051**ALWAYS:**52- Every `<img>` conveying information MUST have a meaningful `alt` attribute (1.1)53- Every decorative `<img>` MUST have `alt=""` and no `title`, `aria-label`, or `aria-labelledby` (1.2)54- Every `<svg>` conveying information MUST have `role="img"` — this one is mandatory, 1.1.5 invalidates the55 test without it — plus a text alternative via `aria-label`, `aria-labelledby`, or a `<title>` child (1.1)56- Decorative `<svg>` MUST have `aria-hidden="true"` (1.2)57- Every text alternative MUST be **relevant** — it must convey what the image conveys (1.3), **and short and58 concise** (test 1.3.9). RAWeb's glossary defines *short and concise* as "a maximum length of 80 characters59 is strongly recommended" — the 80 is a strong recommendation, but brevity itself is tested by 1.360- Complex images (charts, infographics) need a detailed description (1.6) — but **the accepted routes differ61 by element type**:62 - `<img>`, `<object>`, `<embed>`: only two routes — a text alternative *referencing* an adjacent detailed63 description, or an **adjacent link/button** to it. **`aria-describedby` is not a route here** (1.6.1–1.6.3)64 - `<input type="image">`, `<svg>`, `role="img"`: `aria-describedby` **is** accepted, alongside65 `aria-label`/`aria-labelledby` and the adjacent link/button (1.6.4, 1.6.5, 1.6.10)66 - `<canvas>`: also accepts a detailed description placed between `<canvas>` and `</canvas>` (1.6.7)67 - Whichever you use, it must actually resolve for AT — tests 1.6.6, 1.6.8 and 1.6.9 validate the wiring68 rather than grant a route69- Avoid images of text unless a **replacement mechanism** exists, or the visual effect cannot be reproduced70 in CSS — 1.8 accepts either escape (1.8 — Level AA)71- Where an image has a visible caption, that caption MUST be correctly linked to the image — `<figure>`/`<figcaption>` (1.9)7273**NEVER:**74- Leave `alt` undefined on an informative image75- Use `alt="image"`, `alt="photo"`, `alt="icon"` — describe what the image conveys76- Use `title` as the sole text alternative (poor assistive technology support)7778```html79<!-- GOOD: informative image -->80<img src="chart.png" alt="Sales increased 40% in Q3 2024">8182<!-- GOOD: decorative image -->83<img src="decoration.svg" alt="">8485<!-- GOOD: complex image with detailed description -->86<figure>87 <img src="orgchart.png" alt="Company organisation chart. Full description below.">88 <figcaption>89 <details>90 <summary>Full description of the organisation chart</summary>91 <p>The CEO reports to the board. Three departments report to the CEO...</p>92 </details>93 </figcaption>94</figure>9596<!-- GOOD: SVG informative -->97<svg role="img" aria-label="Warning: connection unstable">98 <use href="#icon-warning"/>99</svg>100101<!-- GOOD: SVG decorative -->102<svg aria-hidden="true" focusable="false"><use href="#flourish"/></svg>103```104105### 2. Frames (Topic 2)106107- Every `<iframe>` MUST have a descriptive `title` attribute (2.1)108- The `title` must be relevant to the frame content (2.2)109110```html111<iframe src="map.html" title="Interactive map of our office locations"></iframe>112```113114### 3. Colours (Topic 3)115116- Information must NEVER be conveyed by colour alone (3.1) — and RAWeb requires **both** complements, not117 either: additional information **in the code** (`aria-label`, visually hidden text, `aria-current`…)118 **and** an additional **visual** cue (icon, shape, position, typographic effect). An icon alone does not119 discharge 3.1. Applies to text, **colour named in text** ("click the green button"), images, CSS120 properties, and media121- Text contrast ratio: at least **4.5:1**, or **3:1** for large text — which RAWeb defines in **px**:122 **≥24px** non-bold, **≥18.5px** bold (3.2 — Level AA)123- Non-text elements (icons, borders, UI components): at least **3:1** contrast against adjacent colours (3.3)124- Both 3.2 and 3.3 also accept a **mechanism** that lets the user display a compliant ratio; if you ship one,125 it must itself be compliant (3.2.5, 3.3.4)126127```html128<!-- BAD: colour alone conveys meaning -->129<span class="text-red">Required field</span>130131<!-- GOOD: colour + text indicator -->132<span class="text-red">Required field *</span>133<span class="sr-only">(required)</span>134135<!-- Or better with native HTML -->136<input type="text" required aria-required="true">137```138139### 4. Multimedia (Topic 4)140141- Pre-recorded video with audio MUST have captions (**4.3** — captions are 4.3, *not* 4.1; 4.1 is the142 transcript/audio-description criterion). Captions delivered via `<track>` MUST use `kind="captions"`143 (4.3.2). Live synchronised media needs captions too (4.3.3)144- Pre-recorded audio MUST have a text transcript (4.1)145- Pre-recorded video MUST have audio description if visual information is not in the audio track (4.5, 4.6 — Level AA)146- Every automatically triggered sound MUST be controllable by the user — stop it, or control its volume147 independently of the system volume (4.10). Exempt if the sequence lasts **3 seconds or less**148- Media viewing controls MUST **exist** (4.11.1): at least play/pause or stop; sound on/off if there is149 sound; a captions toggle if there are captions; an **audio-description toggle if there is AD**. Each MUST150 then be operable by keyboard AND any pointing device (4.11.2/4.11.3)151- Time-based **and non-time-based** media MUST be compatible with assistive technologies (4.13)152- Interactive `<canvas>`/`<svg>` content is **non-time-based media**: it MUST have an alternative reachable153 via a clearly identifiable adjacent link or button (4.8), that alternative MUST give the same content and154 similar features (4.9), and its controls MUST be keyboard- and pointer-operable (4.12)155156These have **no WCAG equivalent** — they are EN 301 549 §7 requirements that no WCAG-based scanner reports:157158- Caption and audio-description toggles MUST be presented at the **same level** as play/pause — not buried159 one extra click deep in a menu (4.14 — Level AA)160- Any transmit/convert/record feature MUST preserve captions (4.15) and audio description (4.16) — Level AA161- Caption presentation MUST be user-controllable: honour the OS caption settings, or provide an on-page162 control (4.17 — Level AA)163- Subtitles MUST be vocalisable — a spoken-subtitle track, a TTS feature, or an alternative version164 (4.18 — Level AA)165166```html167<video controls>168 <source src="presentation.mp4" type="video/mp4">169 <track kind="captions" src="captions-en.vtt" srclang="en" label="English" default>170 <track kind="descriptions" src="descriptions-en.vtt" srclang="en" label="English audio descriptions">171</video>172```173174### 5. Tables (Topic 5)175176- Every **complex** data table MUST have a summary (5.1) — in the `<caption>` or a passage of text linked by177 `aria-describedby`, *not* the obsolete `summary` attribute — and that summary must be relevant (5.2).178 A table is "complex" when its headers are not confined to the first row and/or first column, or a header's179 scope is not valid for the whole row/column180- For each **layout** table, the linearised content must remain comprehensible **and** the `<table>` MUST181 carry `role="presentation"` — 5.3 requires both182- Where a data table has a title, it MUST be correctly associated — `<caption>`, `aria-label`, or `aria-labelledby` (5.4) — and the title must be relevant (5.5)183- Data tables MUST declare headers that span a whole column/row with `<th>` **or** `role="columnheader"`/184 `role="rowheader"` (5.6.1/5.6.2). A header that does **not** span the whole row/column must use `<th>` —185 no ARIA route there (5.6.3)186- Each cell MUST be associated with its headers: `scope` for simple tables, `headers`/`id` for complex ones (5.7).187 Use `scope="rowgroup"`/`scope="colgroup"` for header **groups** (5.7.6). A `<th>` that does not span the188 whole row/column must have a unique `id` and must **not** carry `scope` or a header role (5.7.3)189- Layout tables MUST NOT use `<th>`, `<caption>`, `<thead>`, `<tfoot>`, `role="rowheader"`/`"columnheader"`,190 a `summary` attribute, or `scope`/`headers`/`axis` on cells (5.8)191192Note: 5.4 requires correct **association** of a title that exists — it does not193itself mandate one. In practice always give a data table a `<caption>`; just194don't cite 5.4 as the reason.195196```html197<!-- GOOD: simple data table -->198<table>199 <caption>Q3 2024 Revenue by Region</caption>200 <thead>201 <tr>202 <th scope="col">Region</th>203 <th scope="col">Revenue</th>204 <th scope="col">Growth</th>205 </tr>206 </thead>207 <tbody>208 <tr>209 <th scope="row">Europe</th>210 <td>€2.4M</td>211 <td>+12%</td>212 </tr>213 </tbody>214</table>215```216217### 6. Links (Topic 6)218219- Every link MUST have an accessible name (**6.2**), and that name must be explicit (6.1)220- Links with identical accessible names MUST lead to the same destination, or be distinguished by their221 context (6.1.6)222- The link's accessible name must be relevant and describe the destination or function (6.1)223- Avoid ambiguous link text: never use "click here", "read more", "learn more" without context (6.1)224- If the visible text is not sufficient, add context via `aria-label` or `aria-labelledby` — but the225 `aria-label` MUST include the visible text (6.1, test 6.1.5 — *Label in Name*)226227```html228<!-- BAD -->229<a href="/report.pdf">Click here</a>230231<!-- GOOD -->232<a href="/report.pdf">Download the Q3 2024 financial report (PDF, 2.4 MB)</a>233234<!-- GOOD: contextual with aria-label containing visible text -->235<article>236 <h3>RAWeb 1.1 released</h3>237 <p>The new version adds 17 additional criteria...</p>238 <a href="/news/raweb" aria-label="Read more about RAWeb 1.1 released">Read more</a>239</article>240```241242### 7. Scripts (Topic 7)243244- Every script-driven UI MUST be compatible with assistive technologies — correct role, name, and value (7.1)245- Where a script has an alternative, that alternative MUST be relevant (7.2)246- Every script MUST be accessible and operable by keyboard AND any pointing device (7.3)247- Any script that initiates a **change of context** MUST warn the user beforehand or leave them in control (7.4)248- Status messages MUST be communicated without moving focus (7.5 — Level AA). RAWeb maps **message type to249 technique** — the wrong role for the type fails the test:250251 | Message type | Use (7.5.1–7.5.3) |252 |---|---|253 | Success, result of an action, app state | `role="status"` **or** `aria-live="polite"` + `aria-atomic="true"` |254 | A suggestion, or warning of an error | `role="alert"` **or** `aria-live="assertive"` + `aria-atomic="true"` |255 | Progress of a process | `role="log"`, `role="progressbar"` or `role="status"` (or the matching `aria-live="polite"`) |256257 Every `aria-live` branch requires **`aria-atomic="true"`** alongside it — bare `aria-live="polite"` fails258 7.5.1. `aria-relevant` is **not** a technique 7.5 recognises; it satisfies nothing here259- A script MUST NOT remove focus from an element that receives it (7.3.2)260261Related criteria that live in other topics — do not cite them as Topic 7:262- Moving or blinking content must be controllable (pause, stop, hide) → **13.8**263- Sudden changes in brightness / flashing → **13.7**264- Keyboard traps → **12.9**265266```html267<!-- GOOD: live region for status updates -->268<div role="status" aria-live="polite" aria-atomic="true">269 <p>3 results found.</p>270</div>271272<!-- GOOD: alert for important messages -->273<div role="alert">274 <p>Your session will expire in 2 minutes.</p>275</div>276```277278```javascript279// GOOD: keyboard + pointer support280button.addEventListener('click', handler); // covers mouse + Enter/Space281button.addEventListener('keydown', (e) => {282 if (e.key === 'Enter' || e.key === ' ') {283 e.preventDefault();284 handler(e);285 }286});287```288289### 8. Mandatory Elements (Topic 8)290291- Every page MUST have a valid `<!DOCTYPE html>` (8.1)292- Every page MUST have a `lang` attribute on `<html>` (8.3); its code must be valid and relevant to the293 page's main language (8.4)294- Language changes in content MUST be marked with `lang` on the relevant element (8.7 — Level AA)295- Every page MUST have a unique and relevant `<title>` (8.5, 8.6)296- In an SPA, update the page `<title>` when the view changes — a title left at "Home" across views stops297 being relevant, which is what 8.6 tests (8.6 tests relevance; the update rule is the practical consequence)298- No duplicate `id` attributes in the page (8.2)299- Opening and closing tags must be used per specification; nesting must be valid (**8.2** — source-code300 validity. 8.1 is only the presence of a document type)301- Text whose reading direction differs from the page default must sit in a tag with a `dir` attribute302 (8.10), and that value must be a valid, relevant `rtl`/`ltr` (8.10.2)303304```html305<!DOCTYPE html>306<html lang="en">307<head>308 <meta charset="UTF-8">309 <title>Contact Us — Acme Corp</title>310</head>311<body>312 <p>Visit our <span lang="fr">bureau principal</span> in Luxembourg.</p>313</body>314</html>315```316317### 9. Information Structure (Topic 9)318319- Use headings (`<h1>`–`<h6>`, or `role="heading"` + `aria-level`) with a logical hierarchy (9.1.1). Each320 heading's text must be relevant (9.1.2), and **every passage of text that acts as a heading must be marked321 up as one** (9.1.3) — a styled `<div class="title">` fails 9.1.3 even when the hierarchy is clean322- Use the landmarks 9.2.1 actually requires: `<header>`→`banner`, `<nav>`→`navigation`,323 **`<search>`→`search`**, `<main>`→`main`, `<footer>`→`contentinfo`. (`<aside>`/`complementary` is fine to324 use but is **not** required by 9.2)325- Exactly **one visible** `main`, `banner` and `contentinfo` per page, and reserve `navigation` for primary326 and secondary navigation only (9.2.2)327- A `<header>`/`<footer>` without an explicit role must **not** be nested inside `article`, `complementary`,328 `main`, `navigation` or `section` (9.2.3/9.2.4) — a `<header>` inside `<main>` fails329- Use lists (`<ul>`, `<ol>`, `<dl>`) for list content (9.3)330- Use `<q>` for short inline quotations (9.4.1) and `<blockquote>` for quotation blocks (9.4.2). `cite` is331 good practice — 9.4 does not test it332333```html334<body>335 <header role="banner">336 <nav aria-label="Main navigation">...</nav>337 </header>338 <main role="main">339 <h1>Page Title</h1>340 <section aria-labelledby="section-heading">341 <h2 id="section-heading">Section</h2>342 <h3>Sub-section</h3>343 </section>344 </main>345 <footer role="contentinfo">...</footer>346</body>347```348349### 10. Presentation of Information (Topic 10)350351- Content must remain readable and functional at 200% zoom (10.4 — Level AA)352- Content must reflow at 320px CSS width without horizontal scrolling — and, for vertically-read content, at353 256px height without vertical scrolling (10.11 — Level AA)354- Use CSS for all visual presentation. 10.1 forbids an enumerated set — `<font>`, `<center>`, `<big>`,355 `<blink>`, `<marquee>`, `<s>`, `<strike>`, `<tt>` and attributes like `align`, `bgcolor`, `border`,356 `cellpadding`, `hspace`, `valign`, `vspace`. **`width`/`height` remain allowed** on `<canvas>`, `<embed>`,357 `<iframe>`, `<img>`, `<object>`, `<source>`, `<svg>` (10.1.2) — keep them on images to prevent layout shift358- Never use spaces to separate letters in a word, or to simulate tables or columns (10.1.3)359- Visible content that conveys information must be reachable by AT — a CSS background image carrying meaning360 needs a text alternative on the page (10.2). Separately, the **reading order** must stay understandable361 with CSS disabled (10.3)362- Visible focus indicator on all interactive elements, with a **contrast ratio ≥ 3:1** (10.7)363- Do not hide content in a way that becomes unreachable (10.8)364- Text spacing: users must be able to override line-height (1.5×), paragraph spacing (2×), letter-spacing (0.12em), word-spacing (0.16em) without loss of content (10.12 — Level AA)365366```css367/* GOOD: visible focus indicator */368:focus-visible {369 outline: 3px solid #0056b3;370 outline-offset: 2px;371}372373/* GOOD: ensure reflow support */374.container {375 max-width: 100%;376 overflow-wrap: break-word;377}378379/* NEVER: hide focus outline globally */380/* *:focus { outline: none; } ← FORBIDDEN */381```382383### 11. Forms (Topic 11)384385**This is a critical topic. Always look up criteria with `bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh topic 11` when implementing forms.**386387- Every form field MUST have a programmatically associated label (11.1). Always give it a **visible** one388 too — that is 11.4 (label next to its field) plus good practice; 11.1 itself also accepts `aria-label`389 or `title` with nothing visible390- Label association: `<label for="id">`, `aria-labelledby`, `aria-label`, or `title` (11.1)391- The `<label>` element with `for` attribute is the preferred method (11.1)392- The accessible name MUST include the visible label text (11.2)393- Labels must be relevant — clearly describe the expected input (11.2)394- Labels for fields with the same function MUST stay consistent across the set of pages (11.3 — Level AA)395- The label MUST sit next to its field (11.4): immediately **above or left** for normal fields (LTR), and396 immediately **below or right** for `checkbox`, `radio`, and `role="switch"`397- Related fields MUST be grouped — `<fieldset>`, `role="group"`, or `role="radiogroup"` for radios (11.5)398- Every group MUST have a legend (11.6): `<legend>` in a `<fieldset>`, or `aria-label`/`aria-labelledby`399 on a `role="group"`/`role="radiogroup"`. The legend must be relevant (11.7)400- Group `<select>` options of the same type with `<optgroup>`, each with a relevant `label` (11.8)401- Button labels MUST be relevant, and the accessible name MUST contain at least the visible label (11.9)402- Required fields must be indicated before the form or at the field level (11.10)403- Use `required` and/or `aria-required="true"` for mandatory fields (11.10) — **and keep a visible404 indication**: the attribute alone passes 11.10.1 but fails 11.10.2, which applies specifically to fields405 carrying `required`/`aria-required` and demands a visible indication in the label, an associated passage406 of text, or the legend407- Error messages MUST identify the field by name and be linked to it via `aria-invalid="true"`408 (11.10 — **Level A**)409- Error messages MUST also suggest the expected data type/format and examples of valid values410 (11.11 — Level AA)411- Forms that modify or delete data, submit exam answers, or carry financial/legal consequences MUST be412 reversible, checkable before submit, or explicitly confirmed (11.12 — Level AA)413- Input purpose for personal data must use `autocomplete` with appropriate values (11.13 — Level AA)414415Note: **error *identification* is 11.10 (Level A); 11.11 is *only* error *suggestion* (AA).** Linking a416message to its field is 11.10, not 11.11 — filing it under 11.11 downgrades a Level A requirement.417`<legend>` is **11.6**, not 11.5 — 11.5 is the grouping alone.418419```html420<form novalidate>421 <fieldset>422 <legend>Personal information</legend>423424 <div>425 <label for="fname">First name <span aria-hidden="true">*</span></label>426 <input type="text" id="fname" name="fname"427 autocomplete="given-name"428 required aria-required="true"429 aria-describedby="fname-error">430 <p id="fname-error" role="alert" class="error" hidden>431 Please enter your first name.432 </p>433 </div>434435 <div>436 <label for="email">Email address <span aria-hidden="true">*</span></label>437 <input type="email" id="email" name="email"438 autocomplete="email"439 required aria-required="true"440 aria-describedby="email-hint email-error">441 <p id="email-hint" class="hint">Format: name@example.com</p>442 <p id="email-error" role="alert" class="error" hidden>443 Please enter a valid email address (e.g., name@example.com).444 </p>445 </div>446 </fieldset>447448 <button type="submit">Submit</button>449</form>450```451452### 12. Navigation (Topic 12)453454- At least two navigation mechanisms among: main nav, sitemap, search engine (12.1 — Level AA)455- Repeated content blocks must be reachable or avoidable (**12.6**) — a landmark role, a heading, a hide456 button, or a skip link all satisfy it457- A skip link to the **main content region** must be present and functional (12.7)458- Skip links must be visible on focus (12.7)459- Tab order must be logical and consistent (12.8), and must **stay** consistent after a script inserts or460 updates content — reposition focus correctly (12.8.2)461- Avoid positive `tabindex` values — they almost always break the coherent order 12.8.1 requires. (12.8462 tests the *order*, not the attribute; RAWeb notes the sequence need not follow natural reading order as463 long as it stays coherent.)464- Navigation MUST NOT contain keyboard traps (12.9) — a modal's Tab loop is legal *only* because Escape and a close button let the user out465- Single-key shortcuts must be remappable or disableable (12.10)466- Content appearing on hover or focus (tooltips, popovers) must be keyboard accessible (12.11 — Level AA)467- Menus and navigation bars must stay in the **same place in the presentation** and the **same relative468 order in the source** across the set of pages (12.2 — Level AA)469- Indicating the active page (e.g. `aria-current="page"`) is **good practice, not a RAWeb criterion** — 12.2470 only tests consistent placement. Do it, but don't cite 12.2 for it471472```html473<body>474 <!-- Skip link: first focusable element -->475 <a href="#main-content" class="skip-link">Skip to main content</a>476477 <nav aria-label="Main navigation">478 <ul>479 <li><a href="/" aria-current="page">Home</a></li>480 <li><a href="/about">About</a></li>481 <li><a href="/contact">Contact</a></li>482 </ul>483 </nav>484485 <main id="main-content" tabindex="-1">486 <!-- Page content -->487 </main>488</body>489```490491```css492.skip-link {493 position: absolute;494 top: -100%;495 left: 0;496 z-index: 1000;497 padding: 0.5rem 1rem;498 background: #000;499 color: #fff;500}501.skip-link:focus {502 top: 0;503}504```505506### 13. Consultation (Topic 13)507508- The user must have control over every time limit that modifies content — adjustable, extendable, or removable (13.1)509- A `<meta http-equiv="refresh">` redirect admits no user control: it must be **immediate** (`content="0"`)510 or at least **twenty hours** (`content` ≥ 72000) (13.1.2)511- A new window MUST NOT open without a user action (13.2)512- Every downloadable office document must have an accessible version (13.3), and that version must offer the same information (13.4)513- Sudden changes in brightness or flashing must be used correctly — no more than 3 flashes per second, **or**514 a cumulative effect area ≤ 21824 px² (13.7). Applies to media, scripted effects, and CSS animation alike515- Moving or blinking content must be controllable by the user: stop/restart, show/hide, or offer the same516 information without the movement (13.8). Exempt if it lasts **≤ 5 seconds**. Pausing **on focus only does517 not count**; content that cannot be stopped (a progress bar) is NA518- Content must be viewable in any screen orientation (13.9 — Level AA)519- Any feature using a complex gesture must also be usable with a simple gesture (13.10) — this covers520 **multi-touch** (13.10.1) *and* **path-based** gestures (13.10.2). A drag with no single-point521 alternative — a slider that only drags, a splitter with no reset — fails 13.10.2. Widely missed, because522 13.10 reads as though it were only about pinch523- Single-point actions must be cancellable (13.11): trigger on **release**, or on press-then-cancel-on-524 release, or provide an abort/undo mechanism525- Motion-actuated features must work via UI components instead (13.12), and the user must be able to526 **disable motion detection** entirely (13.12.3)527- Biometric identification or control (fingerprint, face, voice) MUST have an alternative — non-biometric,528 or a sufficiently *different* biological characteristic (13.14). EN 301 549 §5.3, **Level A**, no WCAG529 equivalent530- Document conversion features must preserve accessibility information (13.13 — Level AA, EN 301 549 §5.4)531532Note: indicating a link's file format and size is **good practice, not a RAWeb533criterion** — do it, but do not cite 13.3 for it. Warning the user before a534context change is **7.4**, not 13.1.535536```html537<!-- GOOD: file download with format and size -->538<a href="/report.pdf">539 Annual report 2024 <span class="file-info">(PDF, 3.2 MB)</span>540</a>541542<!-- GOOD: new window warning -->543<a href="https://external.example.com" target="_blank"544 rel="noopener noreferrer">545 External resource546 <span class="sr-only">(opens in a new window)</span>547</a>548```549550---551552## Component patterns (WAI-ARIA APG)553554When building interactive components, look up the correct ARIA pattern from the555local reference data BEFORE writing any code. The component library contains 30556patterns extracted from the [WAI-ARIA Authoring Practices Guide](https://www.w3.org/WAI/ARIA/apg/patterns/)557with keyboard interactions, required/optional ARIA attributes, and implementation notes.558559### How to use560561```bash562# Find the right pattern by keyword (e.g., "modal", "dropdown", "toggle")563bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh find "<keyword>"564565# Show the pattern contract as markdown tables:566# RAWeb criteria + keyboard interaction + ARIA roles/attributes/states567bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh show <slug>568569# Show working code examples (do + don't) for one framework570bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh code <slug> [vanilla|react|angular|web-component]571572# List all 30 available patterns573bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh list574575# Find patterns that use a specific ARIA role576bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh roles "<role>"577```578579### Workflow5805811. **Identify** the component the developer is building (dialog, tabs, slider, etc.)5822. **Search** using `find` with the most natural keyword the developer used5833. **Load the contract** with `show <slug>` — the RAWeb criteria that apply, the584 keyboard interaction, and every ARIA attribute with its allowed values/states5854. **Load the code** with `code <slug>` — read the whole pattern file the first586 time; use `code <slug> <framework>` when you already know the universal rules5875. **Apply** the pattern, preferring native HTML elements over ARIA roles5886. **Cross-reference** anything the pattern doesn't cover against the criteria —589 especially Topics 7 (Scripts), 11 (Forms), and 12 (Navigation)590591If a pattern has no code examples yet, `code` tells you so and lists what does.592Fall back to `show <slug>` — the contract is complete for all 30 patterns.593594### Where the data lives595596| Path | Holds | Status |597|---|---|---|598| `references/components/<slug>.json` | **Source of truth**: ARIA roles/attributes/values, keyboard interaction, RAWeb criteria mapping | All 30 patterns |599| `references/patterns/<slug>.md` | Code examples: vanilla, React, Angular, Web Component, each with dos **and** don'ts | Growing — see `code` output |600| `references/patterns/_TEMPLATE.md` | Authoring contract for adding a new pattern file | — |601602Criterion **levels and official titles are resolved at render time** from603`niveaux.json` and `criteres.json`, so a pattern can never quote a stale level.604Never retype an ARIA table into a `.md` — add it to the JSON and let `show`605render it.606607### Quick mapping: common requests → pattern slugs608609| When the developer says... | Look up slug |610|---------------------------|-------------|611| modal, dialog, popup, overlay, lightbox | `dialog-modal` |612| tabs, tab panel, tabbed interface | `tabs` |613| accordion, collapsible, FAQ | `accordion` |614| dropdown menu, context menu, submenu | `menubar` or `menu-button` |615| toast, notification, flash message | `alert` |616| confirm dialog, delete confirmation | `alertdialog` |617| carousel, slideshow, image gallery | `carousel` |618| autocomplete, typeahead, combobox | `combobox` |619| select, dropdown list, listbox | `listbox` |620| breadcrumb, navigation trail | `breadcrumb` |621| tooltip, hint, hover text | `tooltip` |622| toggle, switch, on/off | `switch` |623| slider, range, volume control | `slider` or `slider-multithumb` |624| tree, file browser, nested list | `treeview` |625| data grid, spreadsheet, editable table | `grid` |626| static table, data table | `table` |627| toolbar, action bar, button group | `toolbar` |628| radio buttons, option group | `radio` |629| checkbox, check all | `checkbox` |630| show/hide, details, read more | `disclosure` |631| spinner, number input, stepper | `spinbutton` |632| meter, gauge, battery level | `meter` |633| feed, infinite scroll, timeline | `feed` |634| page structure, landmarks | `landmarks` |635| split view, resizable panes | `windowsplitter` |636637---638639## Screen-reader-only utility class640641Always include and use this utility in your CSS:642643```css644.sr-only {645 position: absolute;646 width: 1px;647 height: 1px;648 padding: 0;649 margin: -1px;650 overflow: hidden;651 clip: rect(0, 0, 0, 0);652 white-space: nowrap;653 border: 0;654}655```656657---658659## Pre-commit checklist (apply before finalizing any code)660661Before considering any code complete, verify:662663- [ ] All images have appropriate `alt` (informative) or `alt=""` (decorative)664- [ ] All form fields have associated labels665- [ ] Colour is never the sole means of conveying information666- [ ] All interactive elements are keyboard accessible667- [ ] Focus order is logical and follows visual order668- [ ] Focus indicator is visible on all interactive elements669- [ ] Page has valid `lang` attribute and language changes are marked670- [ ] Page has a unique, descriptive `<title>`671- [ ] Headings follow a logical hierarchy (no skipped levels)672- [ ] Semantic HTML elements are used (`<nav>`, `<main>`, `<header>`, `<footer>`)673- [ ] ARIA attributes are used correctly (prefer native HTML semantics first)674- [ ] Dynamic content changes are announced to assistive technologies675- [ ] No auto-playing media or animation without user control676- [ ] Links opening new windows are indicated677678---679680## When in doubt6816821. **Look up the RAWeb criterion**: `bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh criterion <topic.criterion>`6832. **Check the test methodology**: `bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh methodology <topic.criterion.test>`6843. **Look up the ARIA pattern** for a component: `bash ${CLAUDE_SKILL_DIR}/scripts/raweb-component-lookup.sh find "<keyword>"`, then `show <slug>` for the contract and `code <slug>` for worked examples6854. **Consult the glossary** for precise definitions: `bash ${CLAUDE_SKILL_DIR}/scripts/raweb-lookup.sh glossary "<term>"`6865. Default to the most accessible approach — when two implementations are possible, choose the one with better assistive technology support6876. Prefer native HTML semantics over ARIA: a `<button>` is better than `<div role="button">`6887. When building an interactive widget, ALWAYS load the APG pattern first — do not guess ARIA attributes from memory6898. **Never cite a criterion number from memory.** RAWeb numbering is not WCAG690 numbering: RAWeb 7.3 is keyboard operability, WCAG 2.1.1 is. Verify with691 `criterion <x.y>` before writing a number into code, a comment, or a report