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>