Whakerexa > Getting started > Accessibility

Accessibility

A web page is accessible when it can be read, understood, and operated by everyone — including people with visual, motor, cognitive, or hearing impairments, people using assistive technologies such as screen readers or keyboard-only navigation, and people in difficult conditions such as poor lighting, slow connections, or small screens. Accessibility is not a niche concern: it benefits all users, in all situations. Learn more: W3C — Introduction to Web Accessibility, WCAG 2.1 Guidelines.

In Whakerexa, accessibility is not a feature you add at the end. It is the foundation the framework is built on. Every component, every stylesheet, every color decision starts from the question: will this work for everyone? The result: your pages are accessible by default, with zero extra effort on your part.

Your users choose how they read

Two buttons — already in the navigation bar of this page — give your users direct control over their reading experience. No settings panel. No account required. One click.

Light / Dark mode

Switches the entire page between light and dark palette instantly. Respects the user's OS preference on first load. The choice travels with the URL — share a link, keep the preference.

<button id="btn-color" class="menuitem accessibility"
        type="button" aria-label="Switch light and dark" title="Switch light and dark" aria-pressed="false"
        onclick="Wexa.accessibility.switchColorScheme()"></button>

High contrast mode

Activates a reading profile designed for users with low vision or sensitivity to visual noise: larger font, increased spacing, heavier weights, stronger borders. Compliant with WCAG 2.1 — 1.4.12 Text Spacing.

<button id="btn-contrast" class="menuitem accessibility"
        type="button" aria-label="Switch contrast" title="Switch contrast" aria-pressed="false"
        onclick="Wexa.accessibility.switchContrastScheme()"></button>

Accessibility is architectural

Whakerexa uses CSS Cascade Layers to make accessibility guarantees that cannot be accidentally broken by custom styles. The @layer accessibility layer owns contrast mode and enforces its rules with !important — exclusively within that layer, never elsewhere. Your own CSS, written outside any layer, wins over everything else and can never interfere with contrast mode.

What this means in practice: you cannot accidentally break accessibility by adding your own styles, even without knowing how CSS layers work.

Semantic HTML, ready out of the box

Whakerexa styles standard HTML5 elements directly — <header>, <nav>, <main>, <footer>, <button>, <dialog> — without requiring extra wrapper classes. Screen readers, keyboard navigation, and browser accessibility tools all work because the HTML is correct, not because workarounds were added.

All interactive components carry the right aria-* attributes. Focus outlines are always visible. Skip-to-content links are included in every page template. External links are marked with a visible icon and an implicit warning for screen readers.

No stored preferences — no surprises

Whakerexa never uses localStorage or cookies to persist user preferences. Mode choices (dark, contrast, theme) travel as URL parameters. Every page state is reproducible from a link: send a URL, your colleague sees exactly what you see. Close the tab, the next visit starts fresh.

Printing just works

A dedicated print.css silences all animations, resets backgrounds, and enforces readable typography for print. No black boxes, no disappearing content, no broken layouts on paper.

Wire it up in two lines

Add two buttons to your navigation — IDs and aria-pressed are the only requirements. AccessibilityManager, loaded automatically with wexa.js or the bundle, handles everything else.

<button id="btn-contrast" class="menuitem accessibility"
        type="button" aria-pressed="false" aria-label="Switch contrast" title="Switch contrast"
        onclick="Wexa.accessibility.switchContrastScheme()"></button>

<button id="btn-color" class="menuitem accessibility"
        type="button" aria-pressed="false" aria-label="Switch light and dark" title="Switch light and dark"
        onclick="Wexa.accessibility.switchColorScheme()"></button>

The bar, written by the framework

A page that does not want to write the three buttons itself asks AccessibilityNav for them. It builds a nav holding the theme, the contrast and the color mode — and only those a document says it offers: a document with a single theme has nothing to cycle, and says so.

The bar is built, then added where it belongs. The buttons carry the identifiers the framework answers to, so what is written by hand and what is built here behave the same.

const bar = new Wexa.AccessibilityNav({ theme: true, contrast: true, color: true });
document.getElementById('header-nav-content').appendChild(await bar.build());

The wording is proposed, never imposed. NAV_WORDING carries what a button is called when the page says nothing — Switch theme, Switch contrast, Switch light and dark, each one written as the label and as the title. A page names the ones it wants to name, in its own language, and what it leaves out keeps the proposal.

await bar.build({
    wording: {
        contrast: { label: 'Contraste élevé', title: 'Contraste élevé' },
        color:    { label: 'Clair ou sombre', title: 'Clair ou sombre' }
    }
});

Keyboard shortcuts, without stealing the letters

A page that answers keys has one thing to get right: standing back when the person is typing. KeyboardController holds that rule, so no component writes it again. It listens once, answers the keys the page declared, and gives every key back as soon as the focus is on a field, a control, a link, a media with its own commands, or anything the page made reachable with the tab key. Enter and space are never answered: they operate whatever holds the focus.

A shortcut is its keys and what they do — a function, or the name of a CustomEvent dispatched on the document for a component whose work is done elsewhere. A key that would scroll the page says so.

const keyboard = new Wexa.KeyboardController();

keyboard.register({keys: ['h', 'H', '?'], label: 'Help',
                   action: () => document.getElementById('btn-help').click()});

keyboard.register({keys: ['ArrowRight'], label: 'Next',
                   action: 'page:navigate', detail: {action: 'next'},
                   preventsDefault: true});

keyboard.init();

What was declared is read back with keyboard.shortcuts, which gives the keys and their labels: a help dialog written from it says what the page answers, and nothing else. forget(keys) gives keys back, destroy() gives them all back. The slides of the framework are the first user of this class: their own file holds nothing but their fourteen shortcuts and the events each of them says.

Reusing a named tab

A plain <a> normally navigates the current tab. Set data-named-target to a window name instead, and the click opens — or switches to, if it is already open — the tab carrying that name, leaving the current tab untouched. A page claims a name for its own tab by setting window.name on load; any later link pointing to that same name reuses it rather than opening a duplicate.

This differs from LinkController's data-target (see Links): that one opens a NEW tab under the given name every time none exists yet under that exact name at the moment of the click. data-named-target is for the reverse case — a link meant to rejoin a tab that already claimed its own identity independently, whether or not it was opened by this click.

<!-- The page that owns the tab claims its name once, on load -->
<script>window.name = 'app-main';</script>

<!-- Any link elsewhere reuses that tab instead of duplicating it -->
<a href="main.html" data-named-target="app-main">Back to Main</a>