@intside/accessibility
v1.1.0
Published
Module d'accessibilité ACSBLT (profils + réglages) avec installeur pour Next.js/React et WordPress/vanilla.
Readme
Module d'accessibilité ACSBLT
Widget d'accessibilité réutilisable, extrait du prototype ACSBLT. Il propose :
- Profils d'assistance (vision réduite, dyslexie, TDAH, sensibilité aux animations, daltonisme, senior) + rappel des standards toujours actifs (lecteur d'écran, clavier).
- Réglages personnalisés : taille du texte, interligne, police lisible, contraste, mode sombre, niveaux de gris, focus renforcé, grandes cibles, curseur agrandi, etc.
- Persistance via
localStorage(cléacsblt_a11y_v1) et application de classes sur<html>. - Autonome : aucune dépendance UI (pas de Tailwind, pas de jQuery requis). Couleur d'accent personnalisable via
--a11y-accent.
Deux implémentations interchangeables, même clé de stockage et mêmes classes CSS :
| Dossier | Pour | Format |
|---|---|---|
| react/ | Next.js (App ou Pages Router), React 18+ | TypeScript / TSX |
| vanilla/ | WordPress, sites statiques, tout HTML | JS + CSS, zéro dépendance |
0. Installation rapide (CLI)
Un installeur copie le bon build dans un projet cible et affiche les étapes de câblage. Il ne modifie jamais votre code (il copie des fichiers et imprime le snippet à coller).
# Next.js / React
npx @intside/accessibility install --target nextjs --dest ../mon-app --accent '#112337'
# WordPress (thème)
npx @intside/accessibility install --target wordpress \
--dest wp-content/themes/mon-theme --accent '#0A2240' --position left
# Sans options : mode interactif
npx @intside/accessibility install
# Forcer la récupération des fichiers depuis le dépôt git (et non la copie locale)
node bin/cli.mjs install --target nextjs --dest ../mon-app --from-git --ref mainDepuis un clone du repo, sans publication : node bin/cli.mjs install …
(ou npm link puis acsblt-a11y install …).
D'où viennent les fichiers copiés ? Par défaut, des fichiers livrés à côté
de la CLI : ceux du clone si vous lancez node bin/cli.mjs, ou ceux du tarball
téléchargé si vous lancez npx <url-git> (npm clone le dépôt et exécute le bin
depuis ce clone — donc déjà la version du dépôt). Pour forcer une récupération
explicite depuis le dépôt distant à une réf donnée — utile si votre clone local
est ancien — passez --from-git [--ref <branche|tag>] [--repo <url>] : la CLI
clone le dépôt dans un dossier temporaire, copie, puis le supprime.
| Option | Défaut | Rôle |
|---|---|---|
| --target | (demandé) | nextjs ou wordpress |
| --dest | dossier courant | Racine du projet cible |
| --subdir | components/accessibility / assets/a11y | Sous-dossier de destination |
| --accent | #006828 | Couleur d'accent (--a11y-accent) |
| --position | left | Coin du bouton (WordPress) |
| --label | Accessibilité | Libellé / aria-label |
| --report-email | [email protected] | Lien « Signaler un problème » (WordPress) |
| --cookie-domain | — | Partage cross-sous-domaine : auto ou .exemple.com |
| --from-git | — | Récupère les fichiers depuis le dépôt distant |
| --ref | main | Réf git à récupérer (avec --from-git) |
| --repo | GitLab packages/accessibility | URL du dépôt (avec --from-git) |
| -y, --yes | — | Valeurs par défaut, sans prompt |
Les deux sections ci-dessous détaillent le câblage manuel produit par la CLI.
Mettre à jour un projet existant
L'installeur copie les fichiers du module dans le projet (il ne les lie pas). Pour récupérer une nouvelle version, relancez la même commande : elle réécrit les fichiers du module avec la version à jour.
# Cas standard : fichiers copiés par la CLI
# Le @latest évite le cache npx et force la dernière version publiée
npx @intside/accessibility@latest install --target nextjs --dest .
npx @intside/accessibility@latest install --target wordpress --dest .
# Si le module est en dépendance dans package.json
npm install @intside/accessibility@latest⚠️ La mise à jour écrase les fichiers du module (
AccessibilityModal.tsx,acsblt-accessibility.js/.css, …). Commitez avant de relancer, puis vérifiez legit diff. Gardez vos personnalisations hors des fichiers du module — via les options (--accent,--position, …) ou la configwindow.ACSBLT_A11Y— pour qu'elles survivent aux mises à jour.
1. Version React / Next.js (react/)
Copiez le dossier react/ dans votre projet (ex. src/components/accessibility/) et importez.
Bouton prêt à l'emploi
// app/layout.tsx (ou un header/footer)
import { AccessibilityButton } from '@/components/accessibility';
export default function Layout({ children }: { children: React.ReactNode }) {
return (
<html lang="fr">
<body>
{children}
<AccessibilityButton label="Accessibilité" />
</body>
</html>
);
}AccessibilityButton accepte label, className, style, hideIcon. Sans className, il est neutre (hérite de la couleur du parent) — placez-le dans votre top-bar, votre footer, etc.
Deux déclencheurs au choix
Le module fournit deux points d'entrée interchangeables (ils ouvrent le même modal) — choisissez l'un ou l'autre :
| Composant | Usage | Rendu |
|---|---|---|
| AccessibilityButton | Lien inline dans une barre (top-bar, footer, menu) | Hérite du style du parent |
| AccessibilityFab | Bouton flottant ancré dans un coin | Pastille ronde autonome (icône seule) |
import { AccessibilityFab } from '@/components/accessibility';
// Bouton flottant rond en bas à gauche (défaut)
<AccessibilityFab />
// Autres coins + variantes
<AccessibilityFab position="bottom-right" />
<AccessibilityFab hideLabel={false} label="Accès." /> // pilule avec libellé
<AccessibilityFab offset={16} />AccessibilityFab accepte position ('bottom-left' défaut, 'bottom-right', 'top-left', 'top-right'), label, hideLabel (défaut true → pastille ronde ; false → pilule avec texte), offset (px depuis les bords), className, style. Par défaut : pastille ronde, couleur --a11y-accent, léger agrandissement au survol. Avec className, le style par défaut est entièrement remplacé par le vôtre.
Côté vanilla (WordPress), le bouton flottant est déjà géré :
window.ACSBLT_A11Y = { fab: true, position: 'left' }l'ancre en bas à gauche.
Contrôle manuel (votre propre déclencheur)
'use client';
import { useState } from 'react';
import { AccessibilityModal } from '@/components/accessibility';
export function MyTrigger() {
const [open, setOpen] = useState(false);
return (
<>
<button onClick={() => setOpen(true)}>Accessibilité</button>
{open && <AccessibilityModal onClose={() => setOpen(false)} />}
</>
);
}Éviter le flash au chargement (recommandé)
Appliquez les préférences avant le premier paint avec le script exporté :
// app/layout.tsx — App Router
import Script from 'next/script';
import { A11Y_INIT_SCRIPT } from '@/components/accessibility';
// dans <head> ou juste après <body>
<Script id="a11y-init" strategy="beforeInteractive">
{A11Y_INIT_SCRIPT}
</Script>Personnalisation du thème
Le modal lit des CSS variables avec valeurs par défaut. Surchargez-les au niveau :root :
:root {
--a11y-accent: #0a66c2; /* couleur principale */
--a11y-accent-soft: #0a66c21a;
--a11y-surface: #fff;
--a11y-text: #111;
/* … voir A11Y_TOKENS dans react/engine.ts */
}API exportée
AccessibilityModal, AccessibilityButton, A11YIcon, et le moteur : loadA11y, saveA11y, applyA11yClasses, ensureA11yStyles, les données ACC_PROFILES / ACC_SETTINGS / ACC_STANDARDS, et les constantes A11Y_CSS, A11Y_TOKENS, A11Y_INIT_SCRIPT, STORAGE_KEY.
Composants en
'use client': ils utilisent les hooks React,localStorageetdocument. SSR-safe (les accès navigateur sont dansuseEffect).
2. Version WordPress / vanilla JS (vanilla/)
Trois fichiers, aucune dépendance (fonctionne avec ou sans jQuery) :
acsblt-accessibility.css— effets runtime + styles du widgetacsblt-accessibility.js— logique + UI (bouton flottant + modal), auto-initacsblt-accessibility-head.js— script pré-paint optionnel (anti-flash, à mettre dans<head>)
Intégration HTML minimale
<link rel="stylesheet" href="/a11y/acsblt-accessibility.css">
<!-- dans <head>, avant le rendu (optionnel mais recommandé) -->
<script src="/a11y/acsblt-accessibility-head.js"></script>
<!-- config optionnelle AVANT le script principal -->
<script>window.ACSBLT_A11Y = { fab: true, position: 'right', accent: '#006828' };</script>
<!-- en fin de <body> -->
<script src="/a11y/acsblt-accessibility.js" defer></script>Un bouton flottant apparaît automatiquement (bas-droite). Pour utiliser votre propre déclencheur à la place :
<script>window.ACSBLT_A11Y = { fab: false };</script>
<a href="#" data-acsblt-a11y-open>Accessibilité</a>WordPress
Voir vanilla/wordpress-example.php : enqueue propre via wp_enqueue_scripts, config injectée avec wp_add_inline_script, et script pré-paint dans wp_head. À coller dans functions.php (thème enfant) ou dans un mu-plugin.
Options de configuration (window.ACSBLT_A11Y)
| Clé | Défaut | Description |
|---|---|---|
| fab | true | Affiche le bouton flottant automatique |
| position | 'right' | 'right' ou 'left' |
| label | 'Accessibilité' | Libellé du bouton flottant |
| accent | #006828 | Couleur d'accent (équivaut à --a11y-accent) |
| reportEmail | [email protected] | Adresse du lien « Signaler un problème » |
API JS publique
AcsbltA11y.open(); // ouvre le modal
AcsbltA11y.close();
AcsbltA11y.toggle();
AcsbltA11y.reset(); // réinitialise les préférences
AcsbltA11y.getState(); // copie de l'état courantÉtat des actions (ce qui s'applique réellement)
Toutes les actions persistent la préférence ; voici ce qu'elles produisent visuellement :
- Profils (vision réduite, dyslexie, TDAH, animations, daltonisme, senior) : ✅ effet CSS immédiat.
- Texte : taille du texte ✅ (agit désormais seule, pas seulement avec un profil), interligne ✅, espacement ✅, police lisible ✅, alignement à gauche ✅, largeur max ✅ (cible
main/.entry-content/article). - Couleurs & contraste : contraste ✅, mode sombre ✅, niveaux de gris ✅, souligner les liens ✅, boutons en évidence ✅.
- Mouvement : réduire les animations ✅, désactiver les transitions ✅.
- Navigation : focus renforcé ✅, grandes cibles ✅, curseur agrandi ✅.
- Dépendants de votre application (la préférence est stockée mais l'effet exige du markup spécifique à votre site) :
showShortcuts(afficher les raccourcis clavier),fieldHints(aides sous les champs),errorSummary(résumé des erreurs en haut).- Lisez ces drapeaux dans votre code via
getState()(vanilla) ouloadA11y()(React) pour les implémenter selon votre UI.
Le mode sombre utilise
filter: invert()sur<html>(approche simple, imparfaite sur images/embeds — celles-ci sont ré-inversées). Pour un vrai dark-mode à base de tokens, prévoir un chantier dédié.
Notes
- Les deux versions partagent la même clé
acsblt_a11y_v1et les mêmes classesa11y-*sur<html>: un utilisateur garde ses préférences s'il passe d'un site React à un site WordPress sur la même origine. Pour partager entre sous-domaines, voir « Partager les préférences entre sous-domaines » ci-dessous (cookie de domaine parent). - Les données (profils, réglages, CSS runtime) sont dupliquées dans chaque version pour rester totalement autonomes. En cas de modification, répercutez-la dans
react/engine.tsetvanilla/acsblt-accessibility.js+.css. - Certains effets reposent sur des sélecteurs génériques (
main,button,a,[aria-invalid]). AdaptezA11Y_CSS/ le.cssà vos classes spécifiques si besoin (ex..entry-contentpour WordPress est déjà inclus).
Publier une nouvelle version (mainteneurs)
Le script npm run release enchaîne tout : bump de version, commit + tag, push
dev, fast-forward de main, puis npm publish.
export NPM_TOKEN=npm_xxx # token « Automation » (contourne le 2FA) — voir .env.example
npm run release -- patch # 1.0.1 -> 1.0.2
npm run release -- minor # nouvelle fonctionnalité rétro-compatible
npm run release -- major # changement cassantLe token est lu depuis NPM_TOKEN (ou un .env local non versionné) et n'est
jamais écrit dans un fichier suivi par git. Le script refuse de tourner si le
working tree n'est pas propre ou si tu n'es pas sur la branche dev.
Partager les préférences entre sous-domaines
Par défaut, les préférences sont stockées dans localStorage, cloisonné par
origine : cocreation.bj et app.cocreation.bj ne les partagent donc pas.
Pour les partager entre sous-domaines d'un même domaine parent, configurez un
cookieDomain : le module écrit alors un cookie sur ce domaine (lisible par tous
les sous-domaines), en plus du localStorage.
WordPress / vanilla — avant le chargement du script :
<script>window.ACSBLT_A11Y = { cookieDomain: '.cocreation.bj' };</script>Next.js / React — via la prop du modal :
<AccessibilityModal onClose={() => setOpen(false)} cookieDomain=".cocreation.bj" />(ou par programme : configureA11y({ cookieDomain: '.cocreation.bj' })).
'auto' : déduire le domaine tout seul
Au lieu d'écrire le domaine en dur, passez 'auto' : le module le déduit de
l'URL courante (app.cocreation.bj → .cocreation.bj, manage.intside.com →
.intside.com). Pratique pour installer le même code sur plusieurs sous-domaines
sans rien adapter.
<script>window.ACSBLT_A11Y = { cookieDomain: 'auto' };</script><AccessibilityModal onClose={() => setOpen(false)} cookieDomain="auto" />Résolution exacte (Public Suffix List). En mode 'auto', l'installeur embarque
un module de données PSL (acsblt-accessibility-psl.js côté WordPress, psl-data.ts
côté React) et le charge avant le script principal. La dérivation est alors
exacte : suffixes multi-niveaux (example.co.uk → .example.co.uk,
site.com.au → .site.com.au, co.jp, gov.za…), wildcards (*.ck) et exceptions
(!www.ck) gérés selon l'algorithme officiel de la Public Suffix List.
- Le fichier de données (~128 KB, ~37 KB gzip) n'est chargé que sur les sites en
mode
'auto'; les autres installations n'en paient pas le coût. - Régénérez-le quand la PSL évolue :
npm run build:psl(récupère la liste officielle et réécrit les fichiers de données).
Sans le module PSL (si vous activez
cookieDomain: 'auto'à la main sans chargerpsl-data), le module retombe sur une heuristique des ccTLD à deux niveaux (co,com,org,gov,ac… sous un TLD pays) — correcte pour l'immense majorité des cas, mais pas exhaustive.'auto'renvoie vide (localStorage seul) surlocalhostet les adresses IP.
- Le cookie est petit (< 1 KB),
Path=/,SameSite=Lax,Secureen HTTPS, expiration 1 an. Le point initial (.cocreation.bj) le rend valable pour tous les sous-domaines. - La synchro est effective au prochain chargement / navigation sur l'autre site (les cookies n'émettent pas d'événement entre onglets déjà ouverts).
- Ne fonctionne que pour un domaine parent commun. Pour deux domaines
totalement distincts, il faudrait un partage côté serveur ou une iframe +
postMessage.
⚠️ Ça ne marche que si les deux sites sont réellement sur le même domaine parent.
cocreation.bj+app.cocreation.bj✅ ;cocreation.bj+cocreation.com❌.
