Luxembourg-accessibility-skillset raweb-code
git clone https://github.com/geoffreycrofte/luxembourg-accessibility-skillset
T=$(mktemp -d) && git clone --depth=1 https://github.com/geoffreycrofte/luxembourg-accessibility-skillset "$T" && mkdir -p ~/.claude/skills && cp -r "$T/raweb-code" ~/.claude/skills/geoffreycrofte-luxembourg-accessibility-skillset-raweb-code && rm -rf "$T"
raweb-code/SKILL.mdRAWeb 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:
— All 136+ criteria with tests and WCAG/EN 301 549 mappings${CLAUDE_SKILL_DIR}/../references/raweb/criteres.json
— Step-by-step test procedures${CLAUDE_SKILL_DIR}/../references/raweb/methodologies.json
— Glossary of accessibility terms${CLAUDE_SKILL_DIR}/../references/raweb/glossaire.json
— Topic names${CLAUDE_SKILL_DIR}/../references/raweb/themes.json
— WCAG conformance levels per criterion${CLAUDE_SKILL_DIR}/../references/raweb/niveaux.json
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
conveying information MUST have a meaningful<img>
attribute (1.1)alt - Every decorative
MUST have<img>
and noalt=""
,title
, oraria-label
(1.2)aria-labelledby - Every
conveying information MUST have<svg>
+ text alternative viarole="img"
oraria-label
(1.1)aria-labelledby - Decorative
MUST have<svg>
(1.2)aria-hidden="true" - Text alternatives must be short and concise — 80 characters max recommended (1.3)
- Complex images (charts, infographics) need a detailed description accessible via adjacent link or
(1.6)aria-describedby - Avoid images of text unless the visual effect cannot be achieved with CSS (1.8, 1.9 — Level AA)
NEVER:
- Leave
undefined on an informative imagealt - Use
,alt="image"
,alt="photo"
— describe what the image conveysalt="icon" - Use
as the sole text alternative (poor assistive technology support)title
<!-- 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
MUST have a descriptive<iframe>
attribute (2.1)title - The
must be relevant to the frame content (2.2)title
<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)
- Text contrast ratio: at least 4.5:1 (normal text) or 3:1 (large text ≥18pt / bold ≥14pt) (3.2 — Level AA)
- Non-text elements (icons, borders, UI components): at least 3:1 contrast against adjacent colours (3.3 — Level AA)
<!-- 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.1, 4.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)
- Auto-playing media MUST be controllable: pause, stop, or mute within 3 seconds (4.10)
- No auto-playing audio longer than 3 seconds without a control mechanism (4.11)
<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)
- Data tables MUST use
elements for headers with appropriate<th>
attribute (5.6, 5.7)scope - Complex tables MUST use
/id
associations (5.7)headers - Every data table MUST have a caption or title via
,<caption>
, oraria-label
(5.4)aria-labelledby - Layout tables MUST NOT use
,<th>
,<caption>
, orscope
(5.8)headers - Every data table MUST have a summary to help understand its structure when the table is complex (5.5)
<!-- 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 explicit accessible name (6.1)
- 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
oraria-label
— but thearia-labelledby
MUST include the visible text (6.1, 6.2)aria-label
<!-- 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 MUST be operable by keyboard AND any pointing device (7.1)
- All script-driven UI must be compatible with assistive technologies (7.1)
- Status messages must be communicated without focus change using
,role="status"
,role="alert"
,role="log"
, oraria-live
(7.4)aria-relevant - Script-inserted content must be accessible (7.2)
- Moving, blinking, or auto-updating content must be controllable: pause, stop, hide (7.5 — Level AA)
<!-- 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
(8.1)<!DOCTYPE html> - Every page MUST have a
attribute onlang
matching the primary language (8.3)<html> - Language changes in content MUST be marked with
on the relevant element (8.7 — Level AA)lang - Every page MUST have a unique and relevant
(8.5, 8.6)<title> - The page
MUST be updated when the page state changes significantly (8.6)<title> - No duplicate
attributes in the page (8.2)id - Opening and closing tags must be used per specification; nesting must be valid (8.1)
<!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>
) with a logical hierarchy — no skipped levels (9.1)<h6> - Use semantic HTML5 landmarks:
,<header>
,<nav>
,<main>
,<footer>
(9.2)<aside> - Use lists (
,<ul>
,<ol>
) for list content (9.3)<dl> - Use
for quotations with proper<blockquote>
if applicable (9.4)cite
<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 (10.11 — Level AA)
- Use CSS for all visual presentation; do not use HTML presentation attributes (10.1)
- No loss of information when CSS is disabled (10.2, 10.3)
- Visible focus indicator must be present on all interactive elements (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
when implementing forms.bash ${CLAUDE_SKILL_DIR}/../scripts/raweb-lookup.sh topic 11
- Every form field MUST have a visible label (11.1) AND a programmatically associated label (11.1)
- Label association:
,<label for="id">
,aria-labelledby
, oraria-label
(11.1)title - The
element with<label>
attribute is the preferred method (11.1)for - The accessible name MUST include the visible label text (11.2)
- Labels must be relevant — clearly describe the expected input (11.2)
- Related fields MUST be grouped with
and<fieldset>
(11.5)<legend> - Required fields must be indicated before the form or at the field level (11.10)
- Use
and/orrequired
for mandatory fields (11.10)aria-required="true" - Error messages must be linked to the field and describe the error and expected format (11.11 — Level AA)
- Input purpose for personal data must use
with appropriate values (11.13 — Level AA)autocomplete
<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)
- Skip links must be present to bypass repeated content blocks (12.7)
- Skip links must be visible on focus (12.7)
- Tab order must be logical and consistent with visual reading order (12.8)
- Never use positive
values (12.8)tabindex - Navigation landmarks must be consistent across pages (12.2 — Level AA)
- The active page must be indicated in navigation menus (12.2)
<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)
- Links to downloadable files must indicate format and size (13.3)
- Refreshes and redirects must not be automatic unless controllable by user (13.1)
- Time limits must be adjustable, extendable, or removable (13.1)
- Opening a new window must be indicated to the user (13.2)
- No unexpected context change on focus or input without prior warning (13.1)
- Moving or flashing content must be controllable (13.8)
- No more than 3 flashes per second (13.8)
<!-- 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 full pattern details (keyboard, ARIA roles, attributes, notes) bash ${CLAUDE_SKILL_DIR}/../scripts/raweb-component-lookup.sh show <slug> # 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
with the most natural keyword the developer usedfind - Load the full pattern with
to get keyboard interactions and ARIA requirementsshow <slug> - Cross-reference with RAWeb criteria — especially Topics 7 (Scripts), 11 (Forms), and 12 (Navigation)
- Apply the pattern, preferring native HTML elements over ARIA roles when possible
Available patterns
The individual pattern files are located at:
${CLAUDE_SKILL_DIR}/../references/raweb/components/<slug>.json
Each file contains:
— What the component isdescription
— All required and optional keyboard behaviourskeyboard_interactions
— Which ARIA roles to use and wherearia.roles
— Attributes that MUST be presentaria.required_attributes
— Attributes to add when applicablearia.optional_attributes
— Implementation tips and common pitfallsnotes
Quick mapping: common requests → pattern slugs
| When the developer says... | Look up slug |
|---|---|
| modal, dialog, popup, overlay, lightbox | |
| tabs, tab panel, tabbed interface | |
| accordion, collapsible, FAQ | |
| dropdown menu, context menu, submenu | or |
| toast, notification, flash message | |
| confirm dialog, delete confirmation | |
| carousel, slideshow, image gallery | |
| autocomplete, typeahead, combobox | |
| select, dropdown list, listbox | |
| breadcrumb, navigation trail | |
| tooltip, hint, hover text | |
| toggle, switch, on/off | |
| slider, range, volume control | or |
| tree, file browser, nested list | |
| data grid, spreadsheet, editable table | |
| static table, data table | |
| toolbar, action bar, button group | |
| radio buttons, option group | |
| checkbox, check all | |
| show/hide, details, read more | |
| spinner, number input, stepper | |
| meter, gauge, battery level | |
| feed, infinite scroll, timeline | |
| page structure, landmarks | |
| split view, resizable panes | |
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:
- All images have appropriate
(informative) oralt
(decorative)alt="" - All form fields have associated labels
- Colour is never the sole means of conveying information
- All interactive elements are keyboard accessible
- Focus order is logical and follows visual order
- Focus indicator is visible on all interactive elements
- Page has valid
attribute and language changes are markedlang - Page has a unique, descriptive
<title> - Headings follow a logical hierarchy (no skipped levels)
- Semantic HTML elements are used (
,<nav>
,<main>
,<header>
)<footer> - ARIA attributes are used correctly (prefer native HTML semantics first)
- Dynamic content changes are announced to assistive technologies
- No auto-playing media or animation without user control
- Links opening new windows are indicated
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:
thenbash ${CLAUDE_SKILL_DIR}/../scripts/raweb-component-lookup.sh find "<keyword>"show <slug> - 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
is better than<button><div role="button"> - When building an interactive widget, ALWAYS load the APG pattern first — do not guess ARIA attributes from memory