@intside/accessibility 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +245 -0
- package/bin/cli.mjs +283 -0
- package/package.json +29 -0
- package/react/AccessibilityButton.tsx +51 -0
- package/react/AccessibilityFab.tsx +99 -0
- package/react/AccessibilityModal.tsx +328 -0
- package/react/engine.ts +319 -0
- package/react/icons.tsx +19 -0
- package/react/index.ts +32 -0
- package/react/package.json +17 -0
- package/vanilla/acsblt-accessibility-head.js +17 -0
- package/vanilla/acsblt-accessibility.css +159 -0
- package/vanilla/acsblt-accessibility.js +383 -0
- package/vanilla/wordpress-example.php +53 -0
|
@@ -0,0 +1,383 @@
|
|
|
1
|
+
/* ================================================================
|
|
2
|
+
ACSBLT · Accessibilité — version autonome (WordPress / vanilla JS)
|
|
3
|
+
Aucune dépendance (ni jQuery ni framework). UMD-friendly.
|
|
4
|
+
|
|
5
|
+
Auto-init au chargement :
|
|
6
|
+
- applique les préférences stockées sur <html>
|
|
7
|
+
- injecte un bouton flottant (sauf config contraire)
|
|
8
|
+
- branche tout élément [data-acsblt-a11y-open] pour ouvrir le modal
|
|
9
|
+
|
|
10
|
+
Configuration (optionnelle) avant le chargement du script :
|
|
11
|
+
<script>window.ACSBLT_A11Y = { fab: true, position: 'right', accent: '#006828' };</script>
|
|
12
|
+
|
|
13
|
+
API publique : window.AcsbltA11y = { open, close, toggle, apply, reset, getState }
|
|
14
|
+
================================================================ */
|
|
15
|
+
(function (window, document) {
|
|
16
|
+
'use strict';
|
|
17
|
+
|
|
18
|
+
var STORAGE_KEY = 'acsblt_a11y_v1';
|
|
19
|
+
var CFG = window.ACSBLT_A11Y || {};
|
|
20
|
+
if (typeof CFG.fab === 'undefined') CFG.fab = true; // bouton flottant auto
|
|
21
|
+
if (typeof CFG.position === 'undefined') CFG.position = 'right';
|
|
22
|
+
if (CFG.accent) {
|
|
23
|
+
document.documentElement.style.setProperty('--a11y-accent', CFG.accent);
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/* ---------- Données ---------- */
|
|
27
|
+
var ACC_STANDARDS = [
|
|
28
|
+
{ id: 'screen_reader', icon: 'eye', label: "Lecteur d'écran", headline: 'Pris en charge par défaut',
|
|
29
|
+
details: ['HTML sémantique et landmarks (header, nav, main, footer)', 'Attributs aria-label sur tous les éléments interactifs', 'Textes alternatifs sur toutes les images', 'Messages dynamiques annoncés via aria-live', 'Formulaires entièrement labellisés', 'Aucune information transmise par la couleur seule'],
|
|
30
|
+
cta: 'Voir les détails' },
|
|
31
|
+
{ id: 'keyboard', icon: 'keyboard', label: 'Navigation clavier', headline: 'Pris en charge par défaut',
|
|
32
|
+
details: ['Navigation complète au clavier (Tab, Shift+Tab, Entrée, Espace)', 'Focus visible à tout moment', 'Ordre de tabulation logique', 'Fermeture des modales avec Echap', 'Skip link vers le contenu principal', 'Aucun piège clavier'],
|
|
33
|
+
cta: 'Voir les raccourcis' }
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
var ACC_PROFILES = [
|
|
37
|
+
{ id: 'low_vision', icon: 'zoomIn', label: 'Vision réduite', desc: 'Texte agrandi, contraste renforcé et zones cliquables plus visibles.', improves: ['Taille du texte augmentée', 'Contraste renforcé', 'Focus renforcé', 'Liens soulignés', 'Boutons plus visibles'], cssClass: 'a11y-low-vision' },
|
|
38
|
+
{ id: 'dyslexia', icon: 'type', label: 'Dyslexie / lecture', desc: 'Police lisible, espacement augmenté et largeur de texte limitée.', improves: ['Police adaptée à la lecture', 'Espacement des lettres', 'Hauteur de ligne augmentée', 'Texte aligné à gauche', 'Paragraphes plus aérés'], cssClass: 'a11y-dyslexia' },
|
|
39
|
+
{ id: 'adhd', icon: 'focus', label: 'Concentration / TDAH', desc: 'Réduction des distractions, animations pausées et mode lecture.', improves: ['Animations réduites', 'Éléments décoratifs estompés', 'Mode lecture centré', 'Contenu principal mis en avant'], cssClass: 'a11y-adhd' },
|
|
40
|
+
{ id: 'motion', icon: 'pause', label: 'Sensibilité aux animations', desc: 'Suppression des animations et transitions non essentielles.', improves: ['Toutes les animations désactivées', 'Transitions supprimées', 'Respect strict de prefers-reduced-motion', 'Autoplay désactivé'], cssClass: 'a11y-motion' },
|
|
41
|
+
{ id: 'color', icon: 'palette', label: 'Daltonisme', desc: 'États visuels renforcés par icônes et texte, jamais par couleur seule.', improves: ['Liens toujours soulignés', 'États avec icônes + texte', 'Contraste renforcé', 'Aucune info transmise par couleur seule'], cssClass: 'a11y-color' },
|
|
42
|
+
{ id: 'senior', icon: 'comfort', label: 'Senior / confort visuel', desc: 'Texte légèrement agrandi, espacement augmenté et formulaires plus lisibles.', improves: ['Texte légèrement plus grand', 'Zones cliquables plus grandes', 'Contraste doux', 'Espacement augmenté', 'Formulaires plus aérés'], cssClass: 'a11y-senior' }
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
var ACC_SETTINGS = {
|
|
46
|
+
text: { label: 'Texte', icon: 'type', items: [
|
|
47
|
+
{ id: 'fontSize', label: 'Taille du texte', type: 'select', options: ['Normale', 'Grande', 'Très grande'], def: 'Normale' },
|
|
48
|
+
{ id: 'lineHeight', label: 'Interligne', type: 'toggle', def: false },
|
|
49
|
+
{ id: 'letterSpacing', label: 'Espacement des lettres', type: 'toggle', def: false },
|
|
50
|
+
{ id: 'readableFont', label: 'Police lisible', type: 'toggle', def: false },
|
|
51
|
+
{ id: 'leftAlign', label: 'Alignement à gauche', type: 'toggle', def: false },
|
|
52
|
+
{ id: 'maxWidth', label: 'Largeur maximale du contenu', type: 'toggle', def: false }
|
|
53
|
+
] },
|
|
54
|
+
colors: { label: 'Couleurs & contraste', icon: 'palette', items: [
|
|
55
|
+
{ id: 'highContrast', label: 'Contraste renforcé', type: 'toggle', def: false },
|
|
56
|
+
{ id: 'darkMode', label: 'Mode sombre', type: 'toggle', def: false },
|
|
57
|
+
{ id: 'grayscale', label: 'Niveaux de gris', type: 'toggle', def: false },
|
|
58
|
+
{ id: 'underlineLinks', label: 'Souligner les liens', type: 'toggle', def: false },
|
|
59
|
+
{ id: 'highlightButtons', label: 'Mettre en évidence les boutons', type: 'toggle', def: false }
|
|
60
|
+
] },
|
|
61
|
+
motion: { label: 'Mouvement', icon: 'pause', items: [
|
|
62
|
+
{ id: 'reduceMotion', label: 'Réduire les animations', type: 'toggle', def: false },
|
|
63
|
+
{ id: 'noTransitions', label: 'Désactiver les transitions', type: 'toggle', def: false }
|
|
64
|
+
] },
|
|
65
|
+
navigation: { label: 'Navigation', icon: 'keyboard', items: [
|
|
66
|
+
{ id: 'strongFocus', label: 'Focus renforcé', type: 'toggle', def: false },
|
|
67
|
+
{ id: 'bigTargets', label: 'Zones cliquables agrandies', type: 'toggle', def: false },
|
|
68
|
+
{ id: 'bigCursor', label: 'Curseur agrandi', type: 'toggle', def: false },
|
|
69
|
+
{ id: 'showShortcuts', label: 'Afficher les raccourcis clavier', type: 'toggle', def: false }
|
|
70
|
+
] },
|
|
71
|
+
forms: { label: 'Formulaires', icon: 'form', items: [
|
|
72
|
+
{ id: 'visibleErrors', label: "Messages d'erreur plus visibles", type: 'toggle', def: false },
|
|
73
|
+
{ id: 'fieldHints', label: 'Aides sous les champs', type: 'toggle', def: false },
|
|
74
|
+
{ id: 'errorSummary', label: 'Résumé des erreurs en haut', type: 'toggle', def: false }
|
|
75
|
+
] }
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
/* Mapping réglage -> classe sur <html> */
|
|
79
|
+
var SETTING_CLASS = {
|
|
80
|
+
highContrast: 'a11y-high-contrast', darkMode: 'a11y-dark', grayscale: 'a11y-grayscale',
|
|
81
|
+
underlineLinks: 'a11y-underline-links', highlightButtons: 'a11y-highlight-btns',
|
|
82
|
+
reduceMotion: 'a11y-reduce-motion', noTransitions: 'a11y-no-transitions',
|
|
83
|
+
strongFocus: 'a11y-strong-focus', bigTargets: 'a11y-big-targets', bigCursor: 'a11y-big-cursor',
|
|
84
|
+
visibleErrors: 'a11y-visible-errors', lineHeight: 'a11y-line-height', letterSpacing: 'a11y-letter-spacing',
|
|
85
|
+
readableFont: 'a11y-readable-font', leftAlign: 'a11y-left-align', maxWidth: 'a11y-max-width'
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
var ICONS = {
|
|
89
|
+
eye: '<path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7S1 12 1 12z"/><circle cx="12" cy="12" r="3"/>',
|
|
90
|
+
keyboard: '<rect x="2" y="6" width="20" height="12" rx="2"/><path d="M6 10h.01M10 10h.01M14 10h.01M18 10h.01M6 14h12"/>',
|
|
91
|
+
zoomIn: '<circle cx="11" cy="11" r="7"/><path d="m21 21-4.3-4.3M11 8v6M8 11h6"/>',
|
|
92
|
+
type: '<polyline points="4 7 4 4 20 4 20 7"/><line x1="9" y1="20" x2="15" y2="20"/><line x1="12" y1="4" x2="12" y2="20"/>',
|
|
93
|
+
focus: '<circle cx="12" cy="12" r="3"/><path d="M3 7V5a2 2 0 0 1 2-2h2M17 3h2a2 2 0 0 1 2 2v2M21 17v2a2 2 0 0 1-2 2h-2M7 21H5a2 2 0 0 1-2-2v-2"/>',
|
|
94
|
+
pause: '<rect x="6" y="4" width="4" height="16"/><rect x="14" y="4" width="4" height="16"/>',
|
|
95
|
+
palette: '<circle cx="12" cy="12" r="9"/><circle cx="12" cy="12" r="3"/><path d="M12 3v3M12 18v3M3 12h3M18 12h3"/>',
|
|
96
|
+
comfort: '<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/>',
|
|
97
|
+
form: '<rect x="4" y="3" width="16" height="18" rx="2"/><path d="M8 8h8M8 12h8M8 16h5"/>',
|
|
98
|
+
check: '<path d="M20 6 9 17l-5-5"/>',
|
|
99
|
+
shieldChk: '<path d="M12 3 4 6v6c0 5 3.5 8 8 11 4.5-3 8-6 8-11V6z"/><path d="m9 12 2 2 4-4"/>',
|
|
100
|
+
reset: '<path d="M3 12a9 9 0 1 0 3-6.7L3 9M3 3v6h6"/>',
|
|
101
|
+
x: '<path d="M18 6 6 18M6 6l12 12"/>',
|
|
102
|
+
announce: '<path d="M3 11l19-9-9 19-2-8-8-2z"/>',
|
|
103
|
+
a11y: '<circle cx="12" cy="5" r="1"/><path d="m9 20 3-6 3 6"/><path d="m6 8 6 2 6-2"/><path d="M12 10v4"/>'
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
function icon(name, size) {
|
|
107
|
+
size = size || 18;
|
|
108
|
+
return '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="width:' + size + 'px;height:' + size + 'px;display:inline-block;vertical-align:middle;flex-shrink:0">' + (ICONS[name] || '') + '</svg>';
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ---------- État / persistance ---------- */
|
|
112
|
+
function load() {
|
|
113
|
+
try { var s = window.localStorage.getItem(STORAGE_KEY); return s ? JSON.parse(s) : { profiles: {}, settings: {} }; }
|
|
114
|
+
catch (e) { return { profiles: {}, settings: {} }; }
|
|
115
|
+
}
|
|
116
|
+
function save(st) {
|
|
117
|
+
try { window.localStorage.setItem(STORAGE_KEY, JSON.stringify(st)); } catch (e) {}
|
|
118
|
+
}
|
|
119
|
+
function applyClasses(st) {
|
|
120
|
+
var el = document.documentElement, i;
|
|
121
|
+
for (i = 0; i < ACC_PROFILES.length; i++) {
|
|
122
|
+
el.classList.toggle(ACC_PROFILES[i].cssClass, !!st.profiles[ACC_PROFILES[i].id]);
|
|
123
|
+
}
|
|
124
|
+
for (var key in SETTING_CLASS) {
|
|
125
|
+
if (SETTING_CLASS.hasOwnProperty(key)) el.classList.toggle(SETTING_CLASS[key], !!st.settings[key]);
|
|
126
|
+
}
|
|
127
|
+
var scale = { Normale: '1', Grande: '1.15', 'Très grande': '1.3' };
|
|
128
|
+
el.style.setProperty('--a11y-font-scale', scale[st.settings.fontSize] || '1');
|
|
129
|
+
el.classList.toggle('a11y-font-scaled', !!st.settings.fontSize && st.settings.fontSize !== 'Normale');
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/* ---------- Helpers DOM ---------- */
|
|
133
|
+
function h(tag, attrs, html) {
|
|
134
|
+
var n = document.createElement(tag), k;
|
|
135
|
+
if (attrs) for (k in attrs) { if (attrs.hasOwnProperty(k)) n.setAttribute(k, attrs[k]); }
|
|
136
|
+
if (html != null) n.innerHTML = html;
|
|
137
|
+
return n;
|
|
138
|
+
}
|
|
139
|
+
function esc(s) {
|
|
140
|
+
return String(s).replace(/[&<>"']/g, function (c) {
|
|
141
|
+
return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c];
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/* ---------- Widget ---------- */
|
|
146
|
+
var state = load();
|
|
147
|
+
var overlay = null, root = null, tab = 0, expandedStd = null, liveRegion = null;
|
|
148
|
+
|
|
149
|
+
function announce(msg) { if (liveRegion) liveRegion.textContent = msg; }
|
|
150
|
+
|
|
151
|
+
function activeCount() {
|
|
152
|
+
var n = 0, k;
|
|
153
|
+
for (k in state.profiles) { if (state.profiles[k]) n++; }
|
|
154
|
+
for (k in state.settings) { if (state.settings[k] && state.settings[k] !== 'Normale') n++; }
|
|
155
|
+
return n;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function persist() { save(state); applyClasses(state); render(); }
|
|
159
|
+
|
|
160
|
+
function switchBtn(checked, onToggle, id) {
|
|
161
|
+
var b = h('button', { type: 'button', class: 'acsblta11y-switch', role: 'switch', 'aria-checked': checked ? 'true' : 'false' });
|
|
162
|
+
if (id) b.id = id;
|
|
163
|
+
b.addEventListener('click', onToggle);
|
|
164
|
+
return b;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function buildStandards() {
|
|
168
|
+
var wrap = h('div', { class: 'acsblta11y-list' });
|
|
169
|
+
ACC_STANDARDS.forEach(function (p) {
|
|
170
|
+
var card = h('div', { class: 'acsblta11y-card' });
|
|
171
|
+
var row = h('div', { class: 'acsblta11y-card-row' });
|
|
172
|
+
row.appendChild(h('div', { class: 'acsblta11y-card-ic' }, icon(p.icon, 20)));
|
|
173
|
+
var main = h('div', { class: 'acsblta11y-card-main' });
|
|
174
|
+
main.appendChild(h('div', { class: 'acsblta11y-card-label' }, esc(p.label)));
|
|
175
|
+
main.appendChild(h('div', { class: 'acsblta11y-std-headline' }, icon('shieldChk', 13) + ' ' + esc(p.headline)));
|
|
176
|
+
row.appendChild(main);
|
|
177
|
+
var cta = h('button', { type: 'button', class: 'acsblta11y-cta' },
|
|
178
|
+
'<span class="acsblta11y-lbl-full">' + esc(p.cta) + '</span><span class="acsblta11y-lbl-short">Voir</span>');
|
|
179
|
+
cta.addEventListener('click', function () { expandedStd = expandedStd === p.id ? null : p.id; render(); });
|
|
180
|
+
row.appendChild(cta);
|
|
181
|
+
card.appendChild(row);
|
|
182
|
+
if (expandedStd === p.id) {
|
|
183
|
+
var det = h('div', { class: 'acsblta11y-detail' });
|
|
184
|
+
var ul = h('ul', { class: 'acsblta11y-ul' });
|
|
185
|
+
p.details.forEach(function (d) { ul.appendChild(h('li', null, icon('check', 14) + '<span>' + esc(d) + '</span>')); });
|
|
186
|
+
det.appendChild(ul);
|
|
187
|
+
card.appendChild(det);
|
|
188
|
+
}
|
|
189
|
+
wrap.appendChild(card);
|
|
190
|
+
});
|
|
191
|
+
return wrap;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function buildProfiles() {
|
|
195
|
+
var wrap = h('div', { class: 'acsblta11y-list' });
|
|
196
|
+
ACC_PROFILES.forEach(function (p) {
|
|
197
|
+
var active = !!state.profiles[p.id];
|
|
198
|
+
var card = h('div', { class: 'acsblta11y-card' + (active ? ' is-active' : '') });
|
|
199
|
+
var row = h('div', { class: 'acsblta11y-card-row' });
|
|
200
|
+
row.appendChild(h('div', { class: 'acsblta11y-card-ic' }, icon(p.icon, 18)));
|
|
201
|
+
var main = h('div', { class: 'acsblta11y-card-main' });
|
|
202
|
+
main.appendChild(h('div', { class: 'acsblta11y-card-label' }, esc(p.label)));
|
|
203
|
+
main.appendChild(h('div', { class: 'acsblta11y-card-desc' }, esc(p.desc)));
|
|
204
|
+
row.appendChild(main);
|
|
205
|
+
row.appendChild(switchBtn(active, function () {
|
|
206
|
+
state.profiles[p.id] = !active;
|
|
207
|
+
announce((!active ? 'Profil activé : ' : 'Profil désactivé : ') + p.label);
|
|
208
|
+
persist();
|
|
209
|
+
}, 'prof_' + p.id));
|
|
210
|
+
card.appendChild(row);
|
|
211
|
+
if (active) {
|
|
212
|
+
var imp = h('div', { class: 'acsblta11y-improve' });
|
|
213
|
+
var btn = h('button', { type: 'button', class: 'acsblta11y-improve-btn', 'aria-expanded': p._open ? 'true' : 'false' },
|
|
214
|
+
(p._open ? 'Masquer ' : 'Ce profil améliore… ') + (p._open ? '▲' : '▼'));
|
|
215
|
+
btn.addEventListener('click', function () { p._open = !p._open; render(); });
|
|
216
|
+
imp.appendChild(btn);
|
|
217
|
+
if (p._open) {
|
|
218
|
+
var ul = h('ul', { class: 'acsblta11y-ul' });
|
|
219
|
+
p.improves.forEach(function (d) { ul.appendChild(h('li', null, icon('check', 13) + '<span>' + esc(d) + '</span>')); });
|
|
220
|
+
imp.appendChild(ul);
|
|
221
|
+
}
|
|
222
|
+
card.appendChild(imp);
|
|
223
|
+
}
|
|
224
|
+
wrap.appendChild(card);
|
|
225
|
+
});
|
|
226
|
+
return wrap;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function buildSettings() {
|
|
230
|
+
var frag = document.createDocumentFragment();
|
|
231
|
+
frag.appendChild(h('p', { class: 'acsblta11y-intro' }, 'Ajustez chaque paramètre individuellement. Vos choix manuels ont priorité sur les profils.'));
|
|
232
|
+
Object.keys(ACC_SETTINGS).forEach(function (catKey) {
|
|
233
|
+
var cat = ACC_SETTINGS[catKey];
|
|
234
|
+
var group = h('div', { class: 'acsblta11y-group' });
|
|
235
|
+
group.appendChild(h('div', { class: 'acsblta11y-group-head' }, icon(cat.icon, 16) + '<span>' + esc(cat.label) + '</span>'));
|
|
236
|
+
var box = h('div', { class: 'acsblta11y-group-box' });
|
|
237
|
+
cat.items.forEach(function (item) {
|
|
238
|
+
var rowEl = h('div', { class: 'acsblta11y-srow' });
|
|
239
|
+
var sid = 's_' + item.id;
|
|
240
|
+
rowEl.appendChild(h('label', { for: sid }, esc(item.label)));
|
|
241
|
+
if (item.type === 'toggle') {
|
|
242
|
+
rowEl.appendChild(switchBtn(!!state.settings[item.id], function () {
|
|
243
|
+
state.settings[item.id] = !state.settings[item.id];
|
|
244
|
+
announce('Réglage mis à jour : ' + item.label);
|
|
245
|
+
persist();
|
|
246
|
+
}, sid));
|
|
247
|
+
} else {
|
|
248
|
+
var sel = h('select', { class: 'acsblta11y-select', id: sid });
|
|
249
|
+
item.options.forEach(function (o) {
|
|
250
|
+
var opt = h('option', null, esc(o));
|
|
251
|
+
opt.value = o;
|
|
252
|
+
if ((state.settings[item.id] || item.def) === o) opt.selected = true;
|
|
253
|
+
sel.appendChild(opt);
|
|
254
|
+
});
|
|
255
|
+
sel.addEventListener('change', function () {
|
|
256
|
+
state.settings[item.id] = sel.value;
|
|
257
|
+
announce('Réglage mis à jour : ' + item.label);
|
|
258
|
+
persist();
|
|
259
|
+
});
|
|
260
|
+
rowEl.appendChild(sel);
|
|
261
|
+
}
|
|
262
|
+
box.appendChild(rowEl);
|
|
263
|
+
});
|
|
264
|
+
group.appendChild(box);
|
|
265
|
+
frag.appendChild(group);
|
|
266
|
+
});
|
|
267
|
+
return frag;
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function render() {
|
|
271
|
+
if (!overlay) return;
|
|
272
|
+
var body = root.querySelector('.acsblta11y-body');
|
|
273
|
+
body.innerHTML = '';
|
|
274
|
+
if (tab === 0) {
|
|
275
|
+
body.appendChild(h('p', { class: 'acsblta11y-intro' }, "Certains profils sont actifs par défaut dans le code — ils ne sont pas des options, mais des standards d'accessibilité intégrés nativement."));
|
|
276
|
+
body.appendChild(h('div', { class: 'acsblta11y-kicker' }, 'STANDARDS DE BASE · TOUJOURS ACTIFS'));
|
|
277
|
+
body.appendChild(buildStandards());
|
|
278
|
+
body.appendChild(h('div', { class: 'acsblta11y-kicker' }, 'PRÉFÉRENCES VISUELLES & COGNITIVES'));
|
|
279
|
+
body.appendChild(buildProfiles());
|
|
280
|
+
} else {
|
|
281
|
+
body.appendChild(buildSettings());
|
|
282
|
+
}
|
|
283
|
+
// tabs + count + reset state
|
|
284
|
+
var tabs = root.querySelectorAll('.acsblta11y-tab');
|
|
285
|
+
for (var i = 0; i < tabs.length; i++) tabs[i].setAttribute('aria-selected', i === tab ? 'true' : 'false');
|
|
286
|
+
var cnt = activeCount();
|
|
287
|
+
var countEl = root.querySelector('.acsblta11y-count');
|
|
288
|
+
countEl.textContent = cnt > 0 ? (cnt + ' préférence' + (cnt > 1 ? 's' : '') + ' active' + (cnt > 1 ? 's' : '')) : '';
|
|
289
|
+
countEl.classList.toggle('acsblta11y-hidden', cnt === 0);
|
|
290
|
+
root.querySelector('.acsblta11y-reset').classList.toggle('acsblta11y-hidden', cnt === 0);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
function build() {
|
|
294
|
+
overlay = h('div', { class: 'acsblta11y acsblta11y-overlay', role: 'dialog', 'aria-modal': 'true', 'aria-label': "Module d'accessibilité" });
|
|
295
|
+
liveRegion = h('div', { class: 'acsblta11y-sr', 'aria-live': 'polite', 'aria-atomic': 'true' });
|
|
296
|
+
overlay.appendChild(liveRegion);
|
|
297
|
+
|
|
298
|
+
root = h('div', { class: 'acsblta11y-panel' });
|
|
299
|
+
root.innerHTML =
|
|
300
|
+
'<div class="acsblta11y-head">' +
|
|
301
|
+
'<div class="acsblta11y-head-l">' +
|
|
302
|
+
'<div class="acsblta11y-badge-icon">' + icon('shieldChk', 18) + '</div>' +
|
|
303
|
+
'<div><h2 class="acsblta11y-title">Accessibilité numérique</h2><span class="acsblta11y-count"></span></div>' +
|
|
304
|
+
'</div>' +
|
|
305
|
+
'<div class="acsblta11y-head-r">' +
|
|
306
|
+
'<button type="button" class="acsblta11y-reset" title="Réinitialiser" aria-label="Réinitialiser">' + icon('reset', 14) + '<span class="acsblta11y-reset-lbl"> Réinitialiser</span></button>' +
|
|
307
|
+
'<button type="button" class="acsblta11y-close" aria-label="Fermer">' + icon('x', 16) + '</button>' +
|
|
308
|
+
'</div>' +
|
|
309
|
+
'</div>' +
|
|
310
|
+
'<div class="acsblta11y-tabs">' +
|
|
311
|
+
'<button type="button" class="acsblta11y-tab" role="tab">Profils d\'assistance</button>' +
|
|
312
|
+
'<button type="button" class="acsblta11y-tab" role="tab">Réglages personnalisés</button>' +
|
|
313
|
+
'</div>' +
|
|
314
|
+
'<div class="acsblta11y-body" role="tabpanel"></div>' +
|
|
315
|
+
'<div class="acsblta11y-foot">' +
|
|
316
|
+
'<span>' + icon('shieldChk', 14) + ' Conformité RGAA 4.1 · Partiellement conforme</span>' +
|
|
317
|
+
'<a href="mailto:' + (CFG.reportEmail || 'accessibilite@example.org') + '">' + icon('announce', 14) + ' Signaler un problème</a>' +
|
|
318
|
+
'</div>';
|
|
319
|
+
overlay.appendChild(root);
|
|
320
|
+
|
|
321
|
+
var tabs = root.querySelectorAll('.acsblta11y-tab');
|
|
322
|
+
tabs[0].addEventListener('click', function () { tab = 0; render(); });
|
|
323
|
+
tabs[1].addEventListener('click', function () { tab = 1; render(); });
|
|
324
|
+
root.querySelector('.acsblta11y-close').addEventListener('click', close);
|
|
325
|
+
root.querySelector('.acsblta11y-reset').addEventListener('click', function () {
|
|
326
|
+
state = { profiles: {}, settings: {} };
|
|
327
|
+
announce('Préférences réinitialisées. Les standards de base restent actifs.');
|
|
328
|
+
persist();
|
|
329
|
+
});
|
|
330
|
+
overlay.addEventListener('click', function (e) { if (e.target === overlay) close(); });
|
|
331
|
+
document.body.appendChild(overlay);
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function onKey(e) { if (e.key === 'Escape' || e.keyCode === 27) close(); }
|
|
335
|
+
|
|
336
|
+
function open() {
|
|
337
|
+
if (!overlay) build();
|
|
338
|
+
overlay.classList.remove('acsblta11y-hidden');
|
|
339
|
+
document.addEventListener('keydown', onKey);
|
|
340
|
+
render();
|
|
341
|
+
var c = root.querySelector('.acsblta11y-close');
|
|
342
|
+
if (c) setTimeout(function () { c.focus(); }, 50);
|
|
343
|
+
}
|
|
344
|
+
function close() {
|
|
345
|
+
if (overlay) overlay.classList.add('acsblta11y-hidden');
|
|
346
|
+
document.removeEventListener('keydown', onKey);
|
|
347
|
+
}
|
|
348
|
+
function toggle() { (overlay && !overlay.classList.contains('acsblta11y-hidden')) ? close() : open(); }
|
|
349
|
+
function reset() { state = { profiles: {}, settings: {} }; persist(); }
|
|
350
|
+
|
|
351
|
+
/* ---------- Bouton flottant + déclencheurs ---------- */
|
|
352
|
+
function mountFab() {
|
|
353
|
+
if (!CFG.fab) return;
|
|
354
|
+
var fab = h('button', { type: 'button', class: 'acsblta11y acsblta11y-fab' + (CFG.position === 'left' ? ' is-left' : ''), 'aria-haspopup': 'dialog', 'aria-label': (CFG.label || 'Accessibilité') },
|
|
355
|
+
icon('a11y', 32));
|
|
356
|
+
fab.addEventListener('click', open);
|
|
357
|
+
document.body.appendChild(fab);
|
|
358
|
+
}
|
|
359
|
+
function bindTriggers() {
|
|
360
|
+
var els = document.querySelectorAll('[data-acsblt-a11y-open]');
|
|
361
|
+
for (var i = 0; i < els.length; i++) {
|
|
362
|
+
els[i].addEventListener('click', function (e) { e.preventDefault(); open(); });
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
/* ---------- Init ---------- */
|
|
367
|
+
function init() {
|
|
368
|
+
applyClasses(state); // ré-applique (utile sans script pré-paint) + harmonise
|
|
369
|
+
mountFab();
|
|
370
|
+
bindTriggers();
|
|
371
|
+
}
|
|
372
|
+
if (document.readyState === 'loading') {
|
|
373
|
+
document.addEventListener('DOMContentLoaded', init);
|
|
374
|
+
} else {
|
|
375
|
+
init();
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
window.AcsbltA11y = {
|
|
379
|
+
open: open, close: close, toggle: toggle, reset: reset,
|
|
380
|
+
apply: function () { applyClasses(state); },
|
|
381
|
+
getState: function () { return JSON.parse(JSON.stringify(state)); }
|
|
382
|
+
};
|
|
383
|
+
})(window, document);
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<?php
|
|
2
|
+
/**
|
|
3
|
+
* Exemple d'intégration WordPress du module d'accessibilité ACSBLT.
|
|
4
|
+
*
|
|
5
|
+
* Deux options :
|
|
6
|
+
* A) Coller ce code dans le functions.php du thème (enfant de préférence).
|
|
7
|
+
* B) En faire un mu-plugin : copier ce fichier dans wp-content/mu-plugins/.
|
|
8
|
+
*
|
|
9
|
+
* Copiez les 3 fichiers du dossier vanilla dans votre thème, ex. :
|
|
10
|
+
* wp-content/themes/mon-theme/assets/a11y/acsblt-accessibility.css
|
|
11
|
+
* wp-content/themes/mon-theme/assets/a11y/acsblt-accessibility.js
|
|
12
|
+
* wp-content/themes/mon-theme/assets/a11y/acsblt-accessibility-head.js
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
add_action( 'wp_enqueue_scripts', function () {
|
|
16
|
+
$base = get_stylesheet_directory_uri() . '/assets/a11y';
|
|
17
|
+
$ver = '1.0.0';
|
|
18
|
+
|
|
19
|
+
// CSS (effets runtime + widget)
|
|
20
|
+
wp_enqueue_style( 'acsblt-a11y', $base . '/acsblt-accessibility.css', array(), $ver );
|
|
21
|
+
|
|
22
|
+
// Config passée au script principal (optionnelle)
|
|
23
|
+
wp_register_script( 'acsblt-a11y', $base . '/acsblt-accessibility.js', array(), $ver, true );
|
|
24
|
+
wp_add_inline_script(
|
|
25
|
+
'acsblt-a11y',
|
|
26
|
+
'window.ACSBLT_A11Y = ' . wp_json_encode( array(
|
|
27
|
+
'fab' => true, // bouton flottant auto (false pour le masquer)
|
|
28
|
+
'position' => 'right', // 'right' | 'left'
|
|
29
|
+
'label' => 'Accessibilité',
|
|
30
|
+
'accent' => '#006828', // couleur d'accent
|
|
31
|
+
'reportEmail' => 'accessibilite@example.org',
|
|
32
|
+
) ) . ';',
|
|
33
|
+
'before'
|
|
34
|
+
);
|
|
35
|
+
wp_enqueue_script( 'acsblt-a11y' );
|
|
36
|
+
} );
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Script pré-paint dans <head> (évite le flash). Inline = aucune requête réseau.
|
|
40
|
+
*/
|
|
41
|
+
add_action( 'wp_head', function () {
|
|
42
|
+
$path = get_stylesheet_directory() . '/assets/a11y/acsblt-accessibility-head.js';
|
|
43
|
+
if ( is_readable( $path ) ) {
|
|
44
|
+
echo "<script>\n" . file_get_contents( $path ) . "\n</script>\n";
|
|
45
|
+
}
|
|
46
|
+
}, 1 );
|
|
47
|
+
|
|
48
|
+
/*
|
|
49
|
+
* Déclencheur personnalisé (au lieu / en plus du bouton flottant) :
|
|
50
|
+
* ajoutez l'attribut data-acsblt-a11y-open sur n'importe quel lien/bouton, ex.
|
|
51
|
+
* dans un menu : <a href="#" data-acsblt-a11y-open>Accessibilité</a>
|
|
52
|
+
* ou en JS : AcsbltA11y.open();
|
|
53
|
+
*/
|