diff --git a/src/public/borne/assets/css/style.css b/src/public/borne/assets/css/style.css
index 0de6e8b..bd9bbc9 100644
--- a/src/public/borne/assets/css/style.css
+++ b/src/public/borne/assets/css/style.css
@@ -12,8 +12,11 @@
* - Card border : 2px solid #FFC72C when selected / active
*
* Kiosk target: 1080x1920 portrait (touch screen).
- * Font stack: system-ui fallback — school font is not available as a web asset.
- * OpenDys is loaded conditionally for accessibility (RGAA Cr 1.c.4).
+ * Base font stack: system-ui fallback (the school font is not available as a web asset).
+ * Accessibility (RGAA Cr 1.c.2): the OpenDyslexic font (OFL 1.1) is self-hosted under
+ * assets/fonts (see @font-face in section 12). a11y.js adds the .dys-font class on
+ * on demand, which redefines --font-family-base for the whole interface, and
+ * persists the choice in localStorage. Base stack is the default.
*/
/* ============================================================
@@ -2149,3 +2152,69 @@ button {
text-align: center;
margin: var(--space-4) 0;
}
+
+/* ============================================================
+ 12. ACCESSIBILITY — dyslexia-friendly font + toggle (RGAA Cr 1.c.2)
+ ============================================================ */
+
+/* OpenDyslexic (OFL 1.1), self-hosted under assets/fonts (see LICENSE-OpenDyslexic.txt).
+ font-display: swap keeps text visible while the face downloads. */
+@font-face {
+ font-family: "OpenDyslexic";
+ src: url("../fonts/opendyslexic-latin-400-normal.woff2") format("woff2");
+ font-weight: 400;
+ font-style: normal;
+ font-display: swap;
+}
+@font-face {
+ font-family: "OpenDyslexic";
+ src: url("../fonts/opendyslexic-latin-700-normal.woff2") format("woff2");
+ font-weight: 700;
+ font-style: normal;
+ font-display: swap;
+}
+
+/* a11y.js sets .dys-font on ; the whole interface then inherits the
+ dyslexia-friendly stack via --font-family-base. Higher specificity than :root,
+ so it overrides the base token. */
+html.dys-font {
+ --font-family-base: "OpenDyslexic", system-ui, -apple-system, "Segoe UI", Arial, sans-serif;
+}
+
+/* Fixed accessibility control, present on every screen (injected by a11y.js). */
+.a11y-toggle {
+ position: fixed;
+ top: var(--space-3);
+ right: var(--space-3);
+ z-index: 1000;
+ display: inline-flex;
+ align-items: center;
+ gap: var(--space-2);
+ padding: var(--space-2) var(--space-3);
+ background: var(--color-bg-card);
+ color: var(--color-text-primary);
+ border: 2px solid var(--color-border-default);
+ border-radius: var(--radius-sm);
+ box-shadow: var(--shadow-card);
+ font-family: var(--font-family-base);
+ font-size: var(--font-size-sm);
+ font-weight: var(--font-weight-bold);
+ cursor: pointer;
+}
+
+.a11y-toggle__icon {
+ font-size: var(--font-size-md);
+ line-height: 1;
+}
+
+/* Active state: not signalled by colour alone (RGAA 1.4.1) — aria-pressed exposes
+ the state to assistive tech, and the label text stays visible. */
+.a11y-toggle[aria-pressed="true"] {
+ background: var(--color-brand-yellow);
+ border-color: var(--color-brand-yellow-dk);
+}
+
+.a11y-toggle:focus-visible {
+ outline: 3px solid var(--color-brand-yellow-dk);
+ outline-offset: 2px;
+}
diff --git a/src/public/borne/assets/fonts/LICENSE-OpenDyslexic.txt b/src/public/borne/assets/fonts/LICENSE-OpenDyslexic.txt
new file mode 100644
index 0000000..8ee9c14
--- /dev/null
+++ b/src/public/borne/assets/fonts/LICENSE-OpenDyslexic.txt
@@ -0,0 +1,21 @@
+OpenDyslexic
+============
+
+Files:
+ opendyslexic-latin-400-normal.woff2
+ opendyslexic-latin-700-normal.woff2
+
+Copyright (c) Abelardo Gonzalez, with Reserved Font Name "OpenDyslexic".
+
+OpenDyslexic is licensed under the SIL Open Font License, Version 1.1 (OFL-1.1).
+This license is available with a FAQ at: https://openfontlicense.org
+
+These web font files (latin subset, woff2) were obtained from the npm package
+@fontsource/opendyslexic (jsDelivr mirror) and are redistributed here unmodified,
+as permitted by the OFL, to be served self-hosted by the Wakdo kiosk for the
+dyslexia-friendly display option (RGAA Cr 1.c.2).
+
+Under the OFL: the font may be used, studied, modified and redistributed freely
+as long as it is not sold by itself; redistributions must retain this notice and
+the license; and the Reserved Font Name may not be used to promote derivative
+fonts. The full license text accompanies the upstream font package.
diff --git a/src/public/borne/assets/fonts/opendyslexic-latin-400-normal.woff2 b/src/public/borne/assets/fonts/opendyslexic-latin-400-normal.woff2
new file mode 100644
index 0000000..47e26d8
Binary files /dev/null and b/src/public/borne/assets/fonts/opendyslexic-latin-400-normal.woff2 differ
diff --git a/src/public/borne/assets/fonts/opendyslexic-latin-700-normal.woff2 b/src/public/borne/assets/fonts/opendyslexic-latin-700-normal.woff2
new file mode 100644
index 0000000..2f04ad1
Binary files /dev/null and b/src/public/borne/assets/fonts/opendyslexic-latin-700-normal.woff2 differ
diff --git a/src/public/borne/assets/js/a11y.js b/src/public/borne/assets/js/a11y.js
new file mode 100644
index 0000000..74522e5
--- /dev/null
+++ b/src/public/borne/assets/js/a11y.js
@@ -0,0 +1,129 @@
+/*
+ * a11y.js — Bascule de police adaptee aux personnes dyslexiques (front borne).
+ *
+ * Accessibilite RGAA Cr 1.c.2 : une police specifique pour les personnes
+ * dyslexiques est prevue ET integree. La police OpenDyslexic (OFL 1.1) est
+ * auto-hebergee (assets/fonts, @font-face dans style.css). Ce module ajoute un
+ * bouton fixe present sur chaque ecran : au clic, il pose la classe .dys-font sur
+ * (qui redefinit --font-family-base, applique a tout le texte) et persiste
+ * le choix dans localStorage pour le conserver d'un ecran a l'autre.
+ *
+ * CSP 'self' : aucun script inline, aucun handler inline. Le DOM est construit par
+ * l'API (createElement/textContent). Les fonctions sont exportees pour etre testees
+ * sans navigateur (jsdom) ; l'auto-init au chargement est gardee pour ne pas
+ * s'executer a l'import en environnement de test (document absent a ce moment-la).
+ */
+
+export const STORAGE_KEY = 'wakdo_dyslexia_font';
+export const ROOT_CLASS = 'dys-font';
+const TOGGLE_SELECTOR = '[data-a11y-dys-toggle]';
+
+/**
+ * Lit la preference persistee. Tolere l'absence de storage (mode prive, quota) :
+ * toute erreur d'acces renvoie false (police de base par defaut).
+ * @param {Storage|null} storage
+ * @returns {boolean}
+ */
+export function isDyslexiaEnabled(storage) {
+ try {
+ return storage != null && storage.getItem(STORAGE_KEY) === '1';
+ } catch {
+ return false;
+ }
+}
+
+/**
+ * Applique (ou retire) la classe .dys-font sur l'element racine fourni.
+ * @param {boolean} enabled
+ * @param {HTMLElement} root typiquement document.documentElement
+ */
+export function applyDyslexiaPreference(enabled, root) {
+ if (root && root.classList) {
+ root.classList.toggle(ROOT_CLASS, Boolean(enabled));
+ }
+}
+
+/**
+ * Persiste la preference. Silencieux si le storage est indisponible.
+ * @param {boolean} enabled
+ * @param {Storage|null} storage
+ */
+export function persistDyslexiaPreference(enabled, storage) {
+ try {
+ if (storage != null) {
+ storage.setItem(STORAGE_KEY, enabled ? '1' : '0');
+ }
+ } catch {
+ /* storage indisponible : la preference reste valable pour la session en cours */
+ }
+}
+
+/**
+ * Construit le bouton de bascule (aria-pressed reflete l'etat). `onToggle` est
+ * appele au clic avec le nouvel etat booleen ; l'appelant persiste et applique.
+ * @param {boolean} initialEnabled
+ * @param {(next: boolean) => void} onToggle
+ * @returns {HTMLButtonElement}
+ */
+export function buildDyslexiaToggle(initialEnabled, onToggle) {
+ const btn = document.createElement('button');
+ btn.type = 'button';
+ btn.className = 'a11y-toggle';
+ btn.setAttribute('data-a11y-dys-toggle', '');
+ btn.setAttribute('aria-pressed', initialEnabled ? 'true' : 'false');
+ btn.setAttribute('aria-label', 'Activer la police adaptee aux personnes dyslexiques');
+
+ const icon = document.createElement('span');
+ icon.className = 'a11y-toggle__icon';
+ icon.setAttribute('aria-hidden', 'true');
+ icon.textContent = 'Aa';
+ btn.appendChild(icon);
+
+ const label = document.createElement('span');
+ label.className = 'a11y-toggle__label';
+ label.textContent = 'Police adaptee';
+ btn.appendChild(label);
+
+ btn.addEventListener('click', () => {
+ const next = btn.getAttribute('aria-pressed') !== 'true';
+ btn.setAttribute('aria-pressed', next ? 'true' : 'false');
+ if (typeof onToggle === 'function') {
+ onToggle(next);
+ }
+ });
+
+ return btn;
+}
+
+/**
+ * Initialise la bascule : applique la preference persistee, puis injecte le bouton
+ * dans le conteneur (idempotent : ne reinjecte pas si un bouton existe deja).
+ * @param {{storage?: Storage|null, root?: HTMLElement, container?: HTMLElement}} [options]
+ * @returns {HTMLButtonElement|null} le bouton injecte, ou null si deja present
+ */
+export function initDyslexiaToggle(options = {}) {
+ const storage = options.storage ?? (typeof window !== 'undefined' ? window.localStorage : null);
+ const root = options.root ?? (typeof document !== 'undefined' ? document.documentElement : null);
+ const container = options.container ?? (typeof document !== 'undefined' ? document.body : null);
+
+ const enabled = isDyslexiaEnabled(storage);
+ applyDyslexiaPreference(enabled, root);
+
+ if (!container || container.querySelector(TOGGLE_SELECTOR)) {
+ return null;
+ }
+
+ const btn = buildDyslexiaToggle(enabled, (next) => {
+ applyDyslexiaPreference(next, root);
+ persistDyslexiaPreference(next, storage);
+ });
+
+ container.appendChild(btn);
+ return btn;
+}
+
+/* Auto-init au chargement. Gardee : a l'import en test (document absent), ne
+ s'enregistre pas ; en navigateur, s'execute une fois le DOM pret. */
+if (typeof document !== 'undefined') {
+ document.addEventListener('DOMContentLoaded', () => initDyslexiaToggle());
+}
diff --git a/src/public/borne/cart.html b/src/public/borne/cart.html
index 6c49f84..221c8bb 100644
--- a/src/public/borne/cart.html
+++ b/src/public/borne/cart.html
@@ -95,5 +95,6 @@
+
diff --git a/src/public/borne/categories.html b/src/public/borne/categories.html
index 64a0453..9f5993e 100644
--- a/src/public/borne/categories.html
+++ b/src/public/borne/categories.html
@@ -175,5 +175,6 @@
+