@oksigenia/access-panel 0.4.3 → 0.5.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/dist/index.js CHANGED
@@ -1,13 +1,13 @@
1
1
  // src/translations.ts
2
2
  var DICT = {
3
- es: { title: "Accesibilidad", close: "Cerrar", txt: "Texto", size: "Tama\xF1o", lh: "Interlineado", align: "Alineaci\xF3n", font: "Legible", dyslexia: "Fuente Dislexia", vis: "Visual", contrast: "Contraste", gray: "Grises", hide: "Ocultar Img", links: "Links", ori: "Orientaci\xF3n", guide: "Gu\xEDa", mask: "M\xE1scara", targets: "\xC1reas Grandes", cursor: "Cursor Grande", pause: "Parar Animac.", reset: "Restablecer Todo", dev: "Desarrollado por", ls: "Espaciado", cb: "Daltonismo", focus: "Foco", presets: "Perfiles", pLow: "Baja Visi\xF3n", pDys: "Dislexia", pMot: "Motor", pCalm: "Sin Distrac." },
4
- en: { title: "Accessibility", close: "Close", txt: "Text", size: "Size", lh: "Line Height", align: "Align", font: "Readable Font", dyslexia: "Dyslexia Font", vis: "Visual", contrast: "Contrast", gray: "Grayscale", hide: "Hide Images", links: "Highlight Links", ori: "Orientation", guide: "Reading Guide", mask: "Reading Mask", targets: "Big Targets", cursor: "Big Cursor", pause: "Pause Anim.", reset: "Reset All", dev: "Developed by", ls: "Letter Spacing", cb: "Color Blind", focus: "Focus", presets: "Profiles", pLow: "Low Vision", pDys: "Dyslexia", pMot: "Motor", pCalm: "No Distract." },
5
- gn: { title: "Oikeha (Accesibilidad)", close: "Mboty", txt: "Mo\xF1e'\u1EBDr\xE3", size: "Tuichakue", lh: "Jei", align: "Mbojoja", font: "Letra Por\xE3", dyslexia: "Dislexia", vis: "Hechapy", contrast: "Sa'y", gray: "H\u0169 ha T\u0129", hide: "Moka\xF1y Ta'anga", links: "Joajuha", ori: "S\xE3mbyhy", guide: "S\xE3mbyhyha", mask: "Mbohov\xE1i", targets: "Tenda Guasu", cursor: "Cursor Guasu", pause: "Mboopyta", reset: "Mbojevy", dev: "Apojare", ls: "Rapykue", cb: "Sa'yvy", focus: "\xD1emoha", presets: "Tekor\xE3", pLow: "Hechapy Vai", pDys: "Dislexia", pMot: "Po-rehegua", pCalm: "Py\u02BCa Guapy" },
6
- fr: { title: "Accessibilit\xE9", close: "Fermer", txt: "Texte", size: "Taille", lh: "Interligne", align: "Alignement", font: "Police Lisible", dyslexia: "Police Dyslexie", vis: "Visuel", contrast: "Contraste", gray: "Niveaux Gris", hide: "Masquer Img", links: "Liens", ori: "Orientation", guide: "Guide Lecture", mask: "Masque Lecture", targets: "Grandes Cibles", cursor: "Grand Curseur", pause: "Pause Anim.", reset: "R\xE9initialiser", dev: "D\xE9velopp\xE9 par", ls: "Espacement", cb: "Daltonisme", focus: "Focus", presets: "Profils", pLow: "Basse Vision", pDys: "Dyslexie", pMot: "Moteur", pCalm: "Sans Distrac." },
7
- it: { title: "Accessibilit\xE0", close: "Chiudi", txt: "Testo", size: "Dimensione", lh: "Interlinea", align: "Allineamento", font: "Leggibile", dyslexia: "Font Dislessia", vis: "Visivo", contrast: "Contrasto", gray: "Scala Grig.", hide: "Nascondi Img", links: "Link", ori: "Orientamento", guide: "Guida", mask: "Maschera", targets: "Aree Grandi", cursor: "Cursore Grande", pause: "Pausa Anim.", reset: "Reimposta", dev: "Sviluppato da", ls: "Spaziatura", cb: "Daltonismo", focus: "Focus", presets: "Profili", pLow: "Ipovisione", pDys: "Dislessia", pMot: "Motorio", pCalm: "No Distraz." },
8
- de: { title: "Barrierefreiheit", close: "Schlie\xDFen", txt: "Text", size: "Gr\xF6\xDFe", lh: "Zeilenh\xF6he", align: "Ausrichtung", font: "Lesbar", dyslexia: "Dyslexie", vis: "Visuell", contrast: "Kontrast", gray: "Graustufen", hide: "Bilder Aus", links: "Links", ori: "Orientierung", guide: "Lesehilfe", mask: "Lesemaske", targets: "Gro\xDFe Ziele", cursor: "Gro\xDFer Cursor", pause: "Anim. Stopp", reset: "Zur\xFCcksetzen", dev: "Entwickelt von", ls: "Buchst.abst.", cb: "Farbblindheit", focus: "Fokus", presets: "Profile", pLow: "Sehschw\xE4che", pDys: "Dyslexie", pMot: "Motorik", pCalm: "Ohne Ablenk." },
9
- nl: { title: "Toegankelijkheid", close: "Sluiten", txt: "Tekst", size: "Grootte", lh: "Regelhoogte", align: "Uitlijning", font: "Leesbaar", dyslexia: "Dyslexie", vis: "Visueel", contrast: "Contrast", gray: "Grijstinten", hide: "Verberg Afb.", links: "Links", ori: "Ori\xEBntatie", guide: "Leesgids", mask: "Leesmasker", targets: "Grote Doelen", cursor: "Grote Cursor", pause: "Anim. Pauze", reset: "Resetten", dev: "Ontwikkeld door", ls: "Letterspatie", cb: "Kleurenblind", focus: "Focus", presets: "Profielen", pLow: "Slechtziend", pDys: "Dyslexie", pMot: "Motoriek", pCalm: "Geen Afleid." },
10
- sv: { title: "Tillg\xE4nglighet", close: "St\xE4ng", txt: "Text", size: "Storlek", lh: "Radh\xF6jd", align: "Justering", font: "L\xE4sbar", dyslexia: "Dyslexi", vis: "Visuell", contrast: "Kontrast", gray: "Gr\xE5skala", hide: "D\xF6lj Bilder", links: "L\xE4nkar", ori: "Orientering", guide: "L\xE4slinjal", mask: "L\xE4smask", targets: "Stora Ytor", cursor: "Stor Mark\xF6r", pause: "Pausa Anim.", reset: "\xC5terst\xE4ll", dev: "Utvecklad av", ls: "Bokstavsavst.", cb: "F\xE4rgblindhet", focus: "Fokus", presets: "Profiler", pLow: "Synneds\xE4tt.", pDys: "Dyslexi", pMot: "Motorik", pCalm: "Inga Distrak." }
3
+ es: { title: "Accesibilidad", close: "Cerrar", txt: "Texto", size: "Tama\xF1o", lh: "Interlineado", align: "Alineaci\xF3n", font: "Legible", dyslexia: "Fuente Dislexia", vis: "Visual", contrast: "Contraste", gray: "Grises", hide: "Ocultar Img", links: "Links", ori: "Orientaci\xF3n", guide: "Gu\xEDa", mask: "M\xE1scara", targets: "\xC1reas Grandes", cursor: "Cursor Grande", pause: "Parar Animac.", reset: "Restablecer Todo", dev: "Desarrollado por", ls: "Espaciado", cb: "Daltonismo", focus: "Foco", presets: "Perfiles", pLow: "Baja Visi\xF3n", pDys: "Dislexia", pMot: "Motor", pCalm: "Sin Distrac.", level: (l, n, m) => `${l}, nivel ${n} de ${m}` },
4
+ en: { title: "Accessibility", close: "Close", txt: "Text", size: "Size", lh: "Line Height", align: "Align", font: "Readable Font", dyslexia: "Dyslexia Font", vis: "Visual", contrast: "Contrast", gray: "Grayscale", hide: "Hide Images", links: "Highlight Links", ori: "Orientation", guide: "Reading Guide", mask: "Reading Mask", targets: "Big Targets", cursor: "Big Cursor", pause: "Pause Anim.", reset: "Reset All", dev: "Developed by", ls: "Letter Spacing", cb: "Color Blind", focus: "Focus", presets: "Profiles", pLow: "Low Vision", pDys: "Dyslexia", pMot: "Motor", pCalm: "No Distract.", level: (l, n, m) => `${l}, level ${n} of ${m}` },
5
+ gn: { title: "Oikeha (Accesibilidad)", close: "Mboty", txt: "Mo\xF1e'\u1EBDr\xE3", size: "Tuichakue", lh: "Jei", align: "Mbojoja", font: "Letra Por\xE3", dyslexia: "Dislexia", vis: "Hechapy", contrast: "Sa'y", gray: "H\u0169 ha T\u0129", hide: "Moka\xF1y Ta'anga", links: "Joajuha", ori: "S\xE3mbyhy", guide: "S\xE3mbyhyha", mask: "Mbohov\xE1i", targets: "Tenda Guasu", cursor: "Cursor Guasu", pause: "Mboopyta", reset: "Mbojevy", dev: "Apojare", ls: "Rapykue", cb: "Sa'yvy", focus: "\xD1emoha", presets: "Tekor\xE3", pLow: "Hechapy Vai", pDys: "Dislexia", pMot: "Po-rehegua", pCalm: "Py\u02BCa Guapy", level: (l, n, m) => `${l}, ${n}/${m}` },
6
+ fr: { title: "Accessibilit\xE9", close: "Fermer", txt: "Texte", size: "Taille", lh: "Interligne", align: "Alignement", font: "Police Lisible", dyslexia: "Police Dyslexie", vis: "Visuel", contrast: "Contraste", gray: "Niveaux Gris", hide: "Masquer Img", links: "Liens", ori: "Orientation", guide: "Guide Lecture", mask: "Masque Lecture", targets: "Grandes Cibles", cursor: "Grand Curseur", pause: "Pause Anim.", reset: "R\xE9initialiser", dev: "D\xE9velopp\xE9 par", ls: "Espacement", cb: "Daltonisme", focus: "Focus", presets: "Profils", pLow: "Basse Vision", pDys: "Dyslexie", pMot: "Moteur", pCalm: "Sans Distrac.", level: (l, n, m) => `${l}, niveau ${n} sur ${m}` },
7
+ it: { title: "Accessibilit\xE0", close: "Chiudi", txt: "Testo", size: "Dimensione", lh: "Interlinea", align: "Allineamento", font: "Leggibile", dyslexia: "Font Dislessia", vis: "Visivo", contrast: "Contrasto", gray: "Scala Grig.", hide: "Nascondi Img", links: "Link", ori: "Orientamento", guide: "Guida", mask: "Maschera", targets: "Aree Grandi", cursor: "Cursore Grande", pause: "Pausa Anim.", reset: "Reimposta", dev: "Sviluppato da", ls: "Spaziatura", cb: "Daltonismo", focus: "Focus", presets: "Profili", pLow: "Ipovisione", pDys: "Dislessia", pMot: "Motorio", pCalm: "No Distraz.", level: (l, n, m) => `${l}, livello ${n} di ${m}` },
8
+ de: { title: "Barrierefreiheit", close: "Schlie\xDFen", txt: "Text", size: "Gr\xF6\xDFe", lh: "Zeilenh\xF6he", align: "Ausrichtung", font: "Lesbar", dyslexia: "Dyslexie", vis: "Visuell", contrast: "Kontrast", gray: "Graustufen", hide: "Bilder Aus", links: "Links", ori: "Orientierung", guide: "Lesehilfe", mask: "Lesemaske", targets: "Gro\xDFe Ziele", cursor: "Gro\xDFer Cursor", pause: "Anim. Stopp", reset: "Zur\xFCcksetzen", dev: "Entwickelt von", ls: "Buchst.abst.", cb: "Farbblindheit", focus: "Fokus", presets: "Profile", pLow: "Sehschw\xE4che", pDys: "Dyslexie", pMot: "Motorik", pCalm: "Ohne Ablenk.", level: (l, n, m) => `${l}, Stufe ${n} von ${m}` },
9
+ nl: { title: "Toegankelijkheid", close: "Sluiten", txt: "Tekst", size: "Grootte", lh: "Regelhoogte", align: "Uitlijning", font: "Leesbaar", dyslexia: "Dyslexie", vis: "Visueel", contrast: "Contrast", gray: "Grijstinten", hide: "Verberg Afb.", links: "Links", ori: "Ori\xEBntatie", guide: "Leesgids", mask: "Leesmasker", targets: "Grote Doelen", cursor: "Grote Cursor", pause: "Anim. Pauze", reset: "Resetten", dev: "Ontwikkeld door", ls: "Letterspatie", cb: "Kleurenblind", focus: "Focus", presets: "Profielen", pLow: "Slechtziend", pDys: "Dyslexie", pMot: "Motoriek", pCalm: "Geen Afleid.", level: (l, n, m) => `${l}, niveau ${n} van ${m}` },
10
+ sv: { title: "Tillg\xE4nglighet", close: "St\xE4ng", txt: "Text", size: "Storlek", lh: "Radh\xF6jd", align: "Justering", font: "L\xE4sbar", dyslexia: "Dyslexi", vis: "Visuell", contrast: "Kontrast", gray: "Gr\xE5skala", hide: "D\xF6lj Bilder", links: "L\xE4nkar", ori: "Orientering", guide: "L\xE4slinjal", mask: "L\xE4smask", targets: "Stora Ytor", cursor: "Stor Mark\xF6r", pause: "Pausa Anim.", reset: "\xC5terst\xE4ll", dev: "Utvecklad av", ls: "Bokstavsavst.", cb: "F\xE4rgblindhet", focus: "Fokus", presets: "Profiler", pLow: "Synneds\xE4tt.", pDys: "Dyslexi", pMot: "Motorik", pCalm: "Inga Distrak.", level: (l, n, m) => `${l}, niv\xE5 ${n} av ${m}` }
11
11
  };
12
12
  function getTranslation(locale) {
13
13
  const lc = locale.toLowerCase();
@@ -37,75 +37,173 @@ var ICON_COLORBLIND = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12
37
37
  var ICON_FOCUS = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 7V3h4v2H5v2H3zm14-4h4v4h-2V5h-2V3zm4 14v4h-4v-2h2v-2h2zM7 21H3v-4h2v2h2v2z"/></svg>';
38
38
  var ICON_MASK = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M2 4h20v6H2zm0 10h20v6H2zm2-9v4h16V5zm0 10v4h16v-4z" opacity=".55"/><rect x="2" y="10" width="20" height="4"/></svg>';
39
39
  var ICON_TARGETS = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M3 3h4v2H5v2H3zm14 0h4v4h-2V5h-2zm0 18v-2h2v-2h2v4zM3 21v-4h2v2h2v2zM12 8a4 4 0 100 8 4 4 0 000-8zm0 2a2 2 0 110 4 2 2 0 010-4z"/></svg>';
40
+ var UNIVERSAL_GLYPH = '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z"/></svg>';
40
41
  var TRIGGER_ICONS = {
41
42
  vitruvian: '<svg viewBox="0 0 122.88 122.88" fill="currentColor"><path d="M61.44,0A61.46,61.46,0,1,1,18,18,61.21,61.21,0,0,1,61.44,0Zm-.39,74.18L52.1,98.91a4.94,4.94,0,0,1-2.58,2.83A5,5,0,0,1,42.7,95.5l6.24-17.28a26.3,26.3,0,0,0,1.17-4,40.64,40.64,0,0,0,.54-4.18c.24-2.53.41-5.27.54-7.9s.22-5.18.29-7.29c.09-2.63-.62-2.8-2.73-3.3l-.44-.1-18-3.39A5,5,0,0,1,27.08,46a5,5,0,0,1,5.05-7.74l19.34,3.63c.77.07,1.52.16,2.31.25a57.64,57.64,0,0,0,7.18.53A81.13,81.13,0,0,0,69.9,42c.9-.1,1.75-.21,2.6-.29l18.25-3.42A5,5,0,0,1,94.5,39a5,5,0,0,1,1.3,7,5,5,0,0,1-3.21,2.09L75.15,51.37c-.58.13-1.1.22-1.56.29-1.82.31-2.72.47-2.61,3.06.08,1.89.31,4.15.61,6.51.35,2.77.81,5.71,1.29,8.4.31,1.77.6,3.19,1,4.55s.79,2.75,1.39,4.42l6.11,16.9a5,5,0,0,1-6.82,6.24,4.94,4.94,0,0,1-2.58-2.83L63,74.23,62,72.4l-1,1.78Zm.39-53.52a8.83,8.83,0,1,1-6.24,2.59,8.79,8.79,0,0,1,6.24-2.59Zm36.35,4.43a51.42,51.42,0,1,0,15,36.35,51.27,51.27,0,0,0-15-36.35Z"/></svg>',
42
43
  wheelchair: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M19 13v-2c-1.54.02-3.09-.75-4.07-1.83l-1.29-1.43c-.17-.19-.38-.34-.61-.45-.01 0-.01-.01-.02-.01H13c-.35-.2-.75-.3-1.19-.26C10.76 7.11 10 8.04 10 9.09V15c0 1.1.9 2 2 2h5v5h2v-5.5c0-1.1-.9-2-2-2h-3v-3.45c1.29 1.07 3.25 1.94 5 1.95zm-6.17 5c-.41 1.16-1.52 2-2.83 2-1.66 0-3-1.34-3-3 0-1.31.84-2.41 2-2.83V12.1c-2.28.46-4 2.48-4 4.9 0 2.76 2.24 5 5 5 2.42 0 4.44-1.72 4.9-4h-2.07zM12 6c1.1 0 2-.9 2-2s-.9-2-2-2-2 .9-2 2 .9 2 2 2z"/></svg>',
43
44
  eye: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 4.5C7 4.5 2.73 7.61 1 12c1.73 4.39 6 7.5 11 7.5s9.27-3.11 11-7.5c-1.73-4.39-6-7.5-11-7.5zM12 17c-2.76 0-5-2.24-5-5s2.24-5 5-5 5 2.24 5 5-2.24 5-5 5zm0-8c-1.66 0-3 1.34-3 3s1.34 3 3 3 3-1.34 3-3-1.34-3-3-3z"/></svg>',
44
- universal: '<svg viewBox="0 0 24 24" fill="currentColor"><path d="M12 2c1.1 0 2 .9 2 2s-.9 2-2 2-2-.9-2-2 .9-2 2-2zm9 7h-6v13h-2v-6h-2v6H9V9H3V7h18v2z"/></svg>'
45
+ universal: UNIVERSAL_GLYPH,
46
+ porthole: UNIVERSAL_GLYPH
45
47
  };
46
48
  var ICON_CLOSE = '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>';
47
49
  var COLORBLIND_FILTERS_SVG = '<svg xmlns="http://www.w3.org/2000/svg" style="position:absolute;width:0;height:0;overflow:hidden;" aria-hidden="true"><defs><filter id="oks-filter-protanopia"><feColorMatrix type="matrix" values="0.567 0.433 0 0 0 0.558 0.442 0 0 0 0 0.242 0.758 0 0 0 0 0 1 0"/></filter><filter id="oks-filter-deuteranopia"><feColorMatrix type="matrix" values="0.625 0.375 0 0 0 0.7 0.3 0 0 0 0 0.3 0.7 0 0 0 0 0 1 0"/></filter><filter id="oks-filter-tritanopia"><feColorMatrix type="matrix" values="0.95 0.05 0 0 0 0 0.433 0.567 0 0 0 0.475 0.525 0 0 0 0 0 1 0"/></filter></defs></svg>';
48
50
 
51
+ // src/controls.ts
52
+ var ALL_CONTROLS = [
53
+ "text-size",
54
+ "line-height",
55
+ "text-align",
56
+ "readable-font",
57
+ "dyslexia-font",
58
+ "letter-spacing",
59
+ "contrast",
60
+ "grayscale",
61
+ "hide-images",
62
+ "highlight-links",
63
+ "colorblind",
64
+ "reading-guide",
65
+ "reading-mask",
66
+ "big-cursor",
67
+ "big-targets",
68
+ "pause-anim",
69
+ "focus"
70
+ ];
71
+ var CONTROL_STATE_KEY = {
72
+ "text-size": "zoom",
73
+ "line-height": "lh",
74
+ "text-align": "align",
75
+ "readable-font": "font",
76
+ "dyslexia-font": "dyslexia",
77
+ "letter-spacing": "ls",
78
+ "contrast": "contrast",
79
+ "grayscale": "grayOverlay",
80
+ "hide-images": "hideImages",
81
+ "highlight-links": "highlightLinks",
82
+ "colorblind": "colorblind",
83
+ "reading-guide": "readingGuide",
84
+ "reading-mask": "readingMask",
85
+ "big-cursor": "bigCursor",
86
+ "big-targets": "bigTargets",
87
+ "pause-anim": "pauseAnim",
88
+ "focus": "focusOutline"
89
+ };
90
+ var STATE_KEY_CONTROL = Object.fromEntries(
91
+ Object.entries(CONTROL_STATE_KEY).map(([control, key]) => [key, control])
92
+ );
93
+ var PRESETS = {
94
+ lowvision: { zoom: 2, contrast: true, highlightLinks: true, bigCursor: true, focusOutline: true },
95
+ dyslexia: { dyslexia: true, lh: 2, ls: 2, readingGuide: true },
96
+ motor: { bigCursor: true, bigTargets: true, focusOutline: true },
97
+ calm: { hideImages: true, pauseAnim: true }
98
+ };
99
+ var PRESET_IDS = ["lowvision", "dyslexia", "motor", "calm"];
100
+ function presetControlIds(id) {
101
+ return Object.keys(PRESETS[id]).map((k) => STATE_KEY_CONTROL[k]);
102
+ }
103
+ function resolveEnabledControls(controlsAttr, excludeAttr) {
104
+ const valid = new Set(ALL_CONTROLS);
105
+ const parse = (raw) => (raw ?? "").split(",").map((s) => s.trim().toLowerCase()).filter((s) => valid.has(s));
106
+ const whitelist = controlsAttr != null ? parse(controlsAttr) : null;
107
+ let enabled = whitelist && whitelist.length > 0 ? new Set(whitelist) : new Set(ALL_CONTROLS);
108
+ if (excludeAttr != null) {
109
+ for (const id of parse(excludeAttr)) enabled.delete(id);
110
+ }
111
+ return enabled;
112
+ }
113
+ function filterPresetForEnabled(id, enabled) {
114
+ const out = {};
115
+ for (const [k, v] of Object.entries(PRESETS[id])) {
116
+ if (enabled.has(STATE_KEY_CONTROL[k])) out[k] = v;
117
+ }
118
+ return out;
119
+ }
120
+ function presetIsAvailable(id, enabled) {
121
+ return presetControlIds(id).filter((c) => enabled.has(c)).length >= 2;
122
+ }
123
+
49
124
  // src/render.ts
125
+ var PRESET_ICON = {
126
+ lowvision: ICON_CONTRAST,
127
+ dyslexia: ICON_DYSLEXIA,
128
+ motor: ICON_CURSOR,
129
+ calm: ICON_HIDE
130
+ };
50
131
  function buildPanelHtml(opts) {
51
132
  const { t, triggerIcon } = opts;
133
+ const enabled = opts.enabled ?? new Set(ALL_CONTROLS);
134
+ const showTrigger = opts.showTrigger ?? true;
135
+ const showPresets = opts.showPresets ?? true;
52
136
  const trig = TRIGGER_ICONS[triggerIcon];
53
- const grid = (rows) => `<div class="oks-access-grid">${rows.join("")}</div>`;
54
- const multi = (action, prefix, levels, label, icon, full = false) => {
137
+ const multi = (id, prefix, levels, label, icon, full = false) => {
138
+ if (!enabled.has(id)) return "";
139
+ const action = id === "colorblind" ? "colorblind" : "multi";
55
140
  const fullClass = full ? " full-width" : "";
56
141
  const dots = Array.from({ length: levels }, () => "<span></span>").join("");
57
- return `<button class="oks-access-opt multi-step${fullClass}" data-action="${action}" data-prefix="${prefix}" data-levels="${levels}" aria-pressed="false" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span><div class="oks-levels">${dots}</div></button>`;
142
+ return `<button class="oks-access-opt multi-step${fullClass}" data-control="${id}" data-action="${action}" data-prefix="${prefix}" data-levels="${levels}" aria-pressed="false" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span><div class="oks-levels">${dots}</div></button>`;
143
+ };
144
+ const toggle = (id, klass, label, icon) => {
145
+ if (!enabled.has(id)) return "";
146
+ return `<button class="oks-access-opt" data-control="${id}" data-action="toggle" data-class="${klass}" aria-pressed="false" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span></button>`;
147
+ };
148
+ const overlay = (id, target, label, icon) => {
149
+ if (!enabled.has(id)) return "";
150
+ return `<button class="oks-access-opt" data-control="${id}" data-action="overlay" data-target="${target}" aria-pressed="false" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span></button>`;
151
+ };
152
+ const simple = (id, action, label, icon) => {
153
+ if (!enabled.has(id)) return "";
154
+ return `<button class="oks-access-opt" data-control="${id}" data-action="${action}" aria-pressed="false" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span></button>`;
58
155
  };
59
- const toggle = (klass, label, icon) => `<button class="oks-access-opt" data-action="toggle" data-class="${klass}" aria-pressed="false" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span></button>`;
60
- const overlay = (id, label, icon) => `<button class="oks-access-opt" data-action="overlay" data-target="${id}" aria-pressed="false" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span></button>`;
61
- const guide = (label, icon) => `<button class="oks-access-opt" data-action="guide" aria-pressed="false" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span></button>`;
62
- const mask = (label, icon) => `<button class="oks-access-opt" data-action="mask" aria-pressed="false" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span></button>`;
63
- const preset = (id, label, icon) => `<button class="oks-preset" data-action="preset" data-preset="${id}" type="button"><span class="oks-icon">${icon}</span><span class="oks-label">${label}</span></button>`;
64
- return `
156
+ const preset = (id, label) => `<button class="oks-preset" data-action="preset" data-preset="${id}" type="button"><span class="oks-icon">${PRESET_ICON[id]}</span><span class="oks-label">${label}</span></button>`;
157
+ const section = (title, rows) => {
158
+ const kept = rows.filter(Boolean);
159
+ if (kept.length === 0) return "";
160
+ return `<h4 class="oks-access-title">${escapeHtml(title)}</h4><div class="oks-access-grid">${kept.join("")}</div>`;
161
+ };
162
+ const presetLabels = {
163
+ lowvision: t.pLow,
164
+ dyslexia: t.pDys,
165
+ motor: t.pMot,
166
+ calm: t.pCalm
167
+ };
168
+ const presetButtons = showPresets ? PRESET_IDS.filter((id) => presetIsAvailable(id, enabled)).map((id) => preset(id, presetLabels[id])) : [];
169
+ const presetsBlock = presetButtons.length ? `<h4 class="oks-access-title">${escapeHtml(t.presets)}</h4><div class="oks-access-presets">${presetButtons.join("")}</div>` : "";
170
+ const triggerBlock = showTrigger ? `
65
171
  <div class="oks-access-wrapper" id="oks-wrapper" data-position="${opts.position}">
66
- <button class="oks-access-btn" id="oks-trigger" aria-label="${escapeAttr(t.title)}" aria-expanded="false" aria-controls="oks-panel" type="button">
172
+ <button class="oks-access-btn" id="oks-trigger" part="trigger" data-trigger-icon="${triggerIcon}" aria-label="${escapeAttr(t.title)}" aria-expanded="false" aria-controls="oks-panel" type="button">
67
173
  ${trig}
68
174
  </button>
69
175
  <span class="oks-active-badge"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="4"><polyline points="20 6 9 17 4 12"/></svg></span>
70
- </div>
176
+ </div>` : "";
177
+ return `${triggerBlock}
71
178
  <div class="oks-access-panel" id="oks-panel" role="dialog" aria-modal="true" aria-labelledby="oks-panel-title" inert>
72
179
  <div class="oks-access-header">
73
180
  <h3 id="oks-panel-title">${escapeHtml(t.title)}</h3>
74
181
  <button class="oks-access-close" id="oks-close" aria-label="${escapeAttr(t.close)}" type="button">${ICON_CLOSE}</button>
75
182
  </div>
76
183
  <div class="oks-access-content">
77
- <h4 class="oks-access-title">${escapeHtml(t.presets)}</h4>
78
- <div class="oks-access-presets">
79
- ${preset("lowvision", t.pLow, ICON_CONTRAST)}
80
- ${preset("dyslexia", t.pDys, ICON_DYSLEXIA)}
81
- ${preset("motor", t.pMot, ICON_CURSOR)}
82
- ${preset("calm", t.pCalm, ICON_HIDE)}
83
- </div>
84
- <h4 class="oks-access-title">${escapeHtml(t.txt)}</h4>
85
- ${grid([
86
- multi("multi", "oks-zoom", 4, t.size, ICON_TXT),
87
- multi("multi", "oks-lh", 3, t.lh, ICON_LH),
88
- multi("multi", "oks-align", 3, t.align, ICON_ALIGN),
89
- toggle("oks-a11y-font", t.font, ICON_FONT),
90
- toggle("oks-dyslexia", t.dyslexia, ICON_DYSLEXIA),
91
- multi("multi", "oks-ls", 3, t.ls, ICON_LS)
184
+ ${presetsBlock}
185
+ ${section(t.txt, [
186
+ multi("text-size", "oks-zoom", 4, t.size, ICON_TXT),
187
+ multi("line-height", "oks-lh", 3, t.lh, ICON_LH),
188
+ multi("text-align", "oks-align", 3, t.align, ICON_ALIGN),
189
+ toggle("readable-font", "oks-a11y-font", t.font, ICON_FONT),
190
+ toggle("dyslexia-font", "oks-dyslexia", t.dyslexia, ICON_DYSLEXIA),
191
+ multi("letter-spacing", "oks-ls", 3, t.ls, ICON_LS)
92
192
  ])}
93
- <h4 class="oks-access-title">${escapeHtml(t.vis)}</h4>
94
- ${grid([
95
- toggle("oks-a11y-contrast", t.contrast, ICON_CONTRAST),
96
- overlay("oks-overlay-gray", t.gray, ICON_GRAY),
97
- toggle("oks-a11y-hide", t.hide, ICON_HIDE),
98
- toggle("oks-a11y-links", t.links, ICON_LINK),
193
+ ${section(t.vis, [
194
+ toggle("contrast", "oks-a11y-contrast", t.contrast, ICON_CONTRAST),
195
+ overlay("grayscale", "oks-overlay-gray", t.gray, ICON_GRAY),
196
+ toggle("hide-images", "oks-a11y-hide", t.hide, ICON_HIDE),
197
+ toggle("highlight-links", "oks-a11y-links", t.links, ICON_LINK),
99
198
  multi("colorblind", "oks-colorblind", 3, t.cb, ICON_COLORBLIND, true)
100
199
  ])}
101
- <h4 class="oks-access-title">${escapeHtml(t.ori)}</h4>
102
- ${grid([
103
- guide(t.guide, ICON_GUIDE),
104
- mask(t.mask, ICON_MASK),
105
- toggle("oks-big-cursor", t.cursor, ICON_CURSOR),
106
- toggle("oks-a11y-bigtargets", t.targets, ICON_TARGETS),
107
- toggle("oks-a11y-pause", t.pause, ICON_PAUSE),
108
- toggle("oks-a11y-focus", t.focus, ICON_FOCUS)
200
+ ${section(t.ori, [
201
+ simple("reading-guide", "guide", t.guide, ICON_GUIDE),
202
+ simple("reading-mask", "mask", t.mask, ICON_MASK),
203
+ toggle("big-cursor", "oks-big-cursor", t.cursor, ICON_CURSOR),
204
+ toggle("big-targets", "oks-a11y-bigtargets", t.targets, ICON_TARGETS),
205
+ toggle("pause-anim", "oks-a11y-pause", t.pause, ICON_PAUSE),
206
+ toggle("focus", "oks-a11y-focus", t.focus, ICON_FOCUS)
109
207
  ])}
110
208
  </div>
111
209
  <div class="oks-access-footer">
@@ -130,14 +228,20 @@ function positionRules(position) {
130
228
  switch (position) {
131
229
  case "top-left":
132
230
  return { wrap: "top: 20px; left: 20px;", panel: "top: 80px; left: 20px;" };
231
+ case "top-center":
232
+ return { wrap: "top: 20px; left: 50%; transform: translateX(-50%);", panel: "top: 80px; left: 50%; transform: translateX(-50%);" };
133
233
  case "top-right":
134
234
  return { wrap: "top: 20px; right: 20px;", panel: "top: 80px; right: 20px;" };
135
235
  case "mid-left":
136
236
  return { wrap: "top: 50%; left: 20px; transform: translateY(-50%);", panel: "top: 50%; left: 90px; transform: translateY(-50%);" };
237
+ case "mid-center":
238
+ return { wrap: "top: 50%; left: 50%; transform: translate(-50%, -50%);", panel: "top: 50%; left: 50%; transform: translate(-50%, -50%);" };
137
239
  case "mid-right":
138
240
  return { wrap: "top: 50%; right: 20px; transform: translateY(-50%);", panel: "top: 50%; right: 90px; transform: translateY(-50%);" };
139
241
  case "bottom-left":
140
242
  return { wrap: "bottom: 20px; left: 20px;", panel: "bottom: 100px; left: 20px;" };
243
+ case "bottom-center":
244
+ return { wrap: "bottom: 20px; left: 50%; transform: translateX(-50%);", panel: "bottom: 100px; left: 50%; transform: translateX(-50%);" };
141
245
  case "bottom-right":
142
246
  return { wrap: "bottom: 20px; right: 20px;", panel: "bottom: 100px; right: 20px;" };
143
247
  }
@@ -231,37 +335,45 @@ var TOGGLE_KEYS = {
231
335
  "oks-a11y-focus": "focusOutline",
232
336
  "oks-a11y-bigtargets": "bigTargets"
233
337
  };
234
- var PRESETS = {
235
- lowvision: { zoom: 2, contrast: true, highlightLinks: true, bigCursor: true, focusOutline: true },
236
- dyslexia: { dyslexia: true, lh: 2, ls: 2, readingGuide: true },
237
- motor: { bigCursor: true, bigTargets: true, focusOutline: true },
238
- calm: { hideImages: true, pauseAnim: true }
239
- };
338
+ function noopController() {
339
+ return Object.assign(() => {
340
+ }, { open() {
341
+ }, close() {
342
+ }, toggle() {
343
+ } });
344
+ }
345
+ function deepActiveElement() {
346
+ let a = document.activeElement;
347
+ while (a?.shadowRoot?.activeElement) a = a.shadowRoot.activeElement;
348
+ return a;
349
+ }
240
350
  function bindPanelBehavior(root, opts = {}) {
241
351
  const storageKey = opts.storageKey ?? "oksiacSettings";
352
+ const t = getTranslation(opts.locale ?? "en");
353
+ const enabled = opts.enabled ?? new Set(ALL_CONTROLS);
354
+ const nudgeMax = Math.max(0, opts.nudgeMax ?? 0);
242
355
  const trigger = root.getElementById("oks-trigger");
243
356
  const panel = root.getElementById("oks-panel");
244
357
  const closeBtn = root.getElementById("oks-close");
245
358
  const resetBtn = root.getElementById("oks-reset");
246
359
  const wrapper = root.getElementById("oks-wrapper");
247
360
  const opts$ = Array.from(root.querySelectorAll(".oks-access-opt, .oks-preset"));
248
- if (!trigger || !panel || !closeBtn || !resetBtn || !wrapper) {
249
- return () => {
250
- };
361
+ if (!panel || !closeBtn || !resetBtn) {
362
+ return noopController();
251
363
  }
252
364
  let state = loadState(storageKey);
253
365
  function applyState() {
254
366
  const body = document.body;
255
- const root2 = document.documentElement;
367
+ const rootEl = document.documentElement;
256
368
  for (const cls of Array.from(body.classList)) {
257
369
  if (cls.startsWith("oks-")) body.classList.remove(cls);
258
370
  }
259
- [1, 2, 3].forEach((l) => root2.classList.remove(`oks-colorblind-${l}`));
371
+ [1, 2, 3].forEach((l) => rootEl.classList.remove(`oks-colorblind-${l}`));
260
372
  if (state.zoom > 0) body.classList.add(`oks-zoom-${state.zoom}`);
261
373
  if (state.lh > 0) body.classList.add(`oks-lh-${state.lh}`);
262
374
  if (state.align > 0) body.classList.add(`oks-align-${state.align}`);
263
375
  if (state.ls > 0) body.classList.add(`oks-ls-${state.ls}`);
264
- if (state.colorblind > 0) root2.classList.add(`oks-colorblind-${state.colorblind}`);
376
+ if (state.colorblind > 0) rootEl.classList.add(`oks-colorblind-${state.colorblind}`);
265
377
  if (state.font) body.classList.add("oks-a11y-font");
266
378
  if (state.dyslexia) body.classList.add("oks-dyslexia");
267
379
  if (state.contrast) body.classList.add("oks-a11y-contrast");
@@ -289,6 +401,10 @@ function bindPanelBehavior(root, opts = {}) {
289
401
  btn.setAttribute("data-level", String(lvl));
290
402
  btn.classList.toggle("is-active", lvl > 0);
291
403
  btn.setAttribute("aria-pressed", lvl > 0 ? "true" : "false");
404
+ const max = parseInt(btn.getAttribute("data-levels") ?? "0", 10);
405
+ const lbl = btn.querySelector(".oks-label")?.textContent?.trim() ?? "";
406
+ if (lvl > 0 && max > 0) btn.setAttribute("aria-label", t.level(lbl, lvl, max));
407
+ else btn.removeAttribute("aria-label");
292
408
  } else if (action === "toggle") {
293
409
  const klass = btn.getAttribute("data-class") ?? "";
294
410
  const key = TOGGLE_KEYS[klass];
@@ -334,9 +450,8 @@ function bindPanelBehavior(root, opts = {}) {
334
450
  } else if (action === "mask") {
335
451
  state.readingMask = !state.readingMask;
336
452
  } else if (action === "preset") {
337
- const id = btn.getAttribute("data-preset") ?? "";
338
- const preset = PRESETS[id];
339
- if (preset) Object.assign(state, preset);
453
+ const id = btn.getAttribute("data-preset");
454
+ if (id && PRESETS[id]) Object.assign(state, filterPresetForEnabled(id, enabled));
340
455
  btn.classList.add("is-flashing");
341
456
  setTimeout(() => btn.classList.remove("is-flashing"), 250);
342
457
  }
@@ -348,28 +463,50 @@ function bindPanelBehavior(root, opts = {}) {
348
463
  applyState();
349
464
  saveState(storageKey, state);
350
465
  };
466
+ let opener = null;
467
+ let ignoreDocClose = false;
351
468
  const openPanel = () => {
469
+ if (panel.classList.contains("is-open")) return;
470
+ opener = deepActiveElement();
352
471
  panel.classList.add("is-open");
353
472
  panel.removeAttribute("inert");
354
- trigger.setAttribute("aria-expanded", "true");
473
+ trigger?.setAttribute("aria-expanded", "true");
474
+ ignoreDocClose = true;
475
+ setTimeout(() => {
476
+ ignoreDocClose = false;
477
+ }, 0);
355
478
  const first = panel.querySelector("button:not([disabled])");
356
479
  first?.focus();
357
480
  };
358
481
  const closePanel = () => {
482
+ if (!panel.classList.contains("is-open")) return;
359
483
  panel.classList.remove("is-open");
360
484
  panel.setAttribute("inert", "");
361
- trigger.setAttribute("aria-expanded", "false");
362
- trigger.focus();
485
+ trigger?.setAttribute("aria-expanded", "false");
486
+ if (trigger) trigger.focus();
487
+ else if (opener?.isConnected) opener.focus();
488
+ opener = null;
363
489
  };
364
- const onTriggerClick = (e) => {
365
- e.stopPropagation();
490
+ const togglePanel = () => {
366
491
  if (panel.classList.contains("is-open")) closePanel();
367
492
  else openPanel();
368
493
  };
494
+ let suppressClick = false;
495
+ const onTriggerClick = (e) => {
496
+ e.stopPropagation();
497
+ if (suppressClick) {
498
+ suppressClick = false;
499
+ return;
500
+ }
501
+ togglePanel();
502
+ };
369
503
  const onDocClick = (e) => {
370
504
  if (!panel.classList.contains("is-open")) return;
505
+ if (ignoreDocClose) return;
371
506
  const path = e.composedPath();
372
- if (path.includes(panel) || path.includes(trigger) || path.includes(wrapper)) return;
507
+ if (path.includes(panel)) return;
508
+ if (trigger && path.includes(trigger)) return;
509
+ if (wrapper && path.includes(wrapper)) return;
373
510
  closePanel();
374
511
  };
375
512
  const onKeyDown = (e) => {
@@ -396,6 +533,88 @@ function bindPanelBehavior(root, opts = {}) {
396
533
  }
397
534
  }
398
535
  };
536
+ const NUDGE_KEY = `${storageKey}::pos`;
537
+ const clampMax = (v) => Math.max(-nudgeMax, Math.min(nudgeMax, v));
538
+ let nudge = { x: 0, y: 0 };
539
+ const loadNudge = () => {
540
+ if (nudgeMax <= 0) return;
541
+ try {
542
+ const raw = localStorage.getItem(NUDGE_KEY);
543
+ if (!raw) return;
544
+ const p = JSON.parse(raw);
545
+ if (typeof p?.x === "number" && typeof p?.y === "number") {
546
+ nudge = { x: clampMax(p.x), y: clampMax(p.y) };
547
+ }
548
+ } catch {
549
+ }
550
+ };
551
+ const saveNudge = () => {
552
+ try {
553
+ if (nudge.x === 0 && nudge.y === 0) localStorage.removeItem(NUDGE_KEY);
554
+ else localStorage.setItem(NUDGE_KEY, JSON.stringify(nudge));
555
+ } catch {
556
+ }
557
+ };
558
+ const applyNudge = () => {
559
+ if (wrapper) wrapper.style.translate = `${nudge.x}px ${nudge.y}px`;
560
+ };
561
+ const clampOffset = (x, y) => {
562
+ x = clampMax(x);
563
+ y = clampMax(y);
564
+ if (wrapper) {
565
+ const r = wrapper.getBoundingClientRect();
566
+ const pad = 4;
567
+ const baseLeft = r.left - nudge.x;
568
+ const baseTop = r.top - nudge.y;
569
+ const vw = window.innerWidth || 0;
570
+ const vh = window.innerHeight || 0;
571
+ if (r.width > 0 && vw > 0) x = Math.max(pad - baseLeft, Math.min(vw - pad - r.width - baseLeft, x));
572
+ if (r.height > 0 && vh > 0) y = Math.max(pad - baseTop, Math.min(vh - pad - r.height - baseTop, y));
573
+ }
574
+ return { x, y };
575
+ };
576
+ let dragStart = null;
577
+ let didDrag = false;
578
+ const DRAG_THRESHOLD = 4;
579
+ const onPointerDown = (e) => {
580
+ if (nudgeMax <= 0 || e.button !== 0) return;
581
+ dragStart = { px: e.clientX, py: e.clientY, ox: nudge.x, oy: nudge.y };
582
+ didDrag = false;
583
+ trigger?.setPointerCapture?.(e.pointerId);
584
+ };
585
+ const onPointerMove = (e) => {
586
+ if (!dragStart) return;
587
+ const dx = e.clientX - dragStart.px;
588
+ const dy = e.clientY - dragStart.py;
589
+ if (!didDrag && Math.hypot(dx, dy) < DRAG_THRESHOLD) return;
590
+ didDrag = true;
591
+ nudge = clampOffset(dragStart.ox + dx, dragStart.oy + dy);
592
+ applyNudge();
593
+ e.preventDefault();
594
+ };
595
+ const onPointerUp = () => {
596
+ if (!dragStart) return;
597
+ dragStart = null;
598
+ if (didDrag) {
599
+ saveNudge();
600
+ suppressClick = true;
601
+ }
602
+ };
603
+ const onTriggerKey = (e) => {
604
+ if (nudgeMax <= 0) return;
605
+ const step = e.shiftKey ? 1 : 10;
606
+ let dx = 0;
607
+ let dy = 0;
608
+ if (e.key === "ArrowLeft") dx = -step;
609
+ else if (e.key === "ArrowRight") dx = step;
610
+ else if (e.key === "ArrowUp") dy = -step;
611
+ else if (e.key === "ArrowDown") dy = step;
612
+ else return;
613
+ e.preventDefault();
614
+ nudge = clampOffset(nudge.x + dx, nudge.y + dy);
615
+ applyNudge();
616
+ saveNudge();
617
+ };
399
618
  const onMove = (e) => {
400
619
  if (!state.readingGuide && !state.readingMask) return;
401
620
  const y = e.touches?.[0]?.clientY ?? e.clientY;
@@ -409,7 +628,7 @@ function bindPanelBehavior(root, opts = {}) {
409
628
  if (mask) mask.style.setProperty("--oks-mask-y", `${y}px`);
410
629
  }
411
630
  };
412
- trigger.addEventListener("click", onTriggerClick);
631
+ trigger?.addEventListener("click", onTriggerClick);
413
632
  closeBtn.addEventListener("click", closePanel);
414
633
  resetBtn.addEventListener("click", onReset);
415
634
  for (const btn of opts$) btn.addEventListener("click", onOptClick);
@@ -417,9 +636,19 @@ function bindPanelBehavior(root, opts = {}) {
417
636
  document.addEventListener("keydown", onKeyDown);
418
637
  document.addEventListener("mousemove", onMove);
419
638
  document.addEventListener("touchmove", onMove, { passive: true });
639
+ if (nudgeMax > 0 && trigger) {
640
+ loadNudge();
641
+ applyNudge();
642
+ trigger.style.cursor = "grab";
643
+ trigger.style.touchAction = "none";
644
+ trigger.addEventListener("pointerdown", onPointerDown);
645
+ document.addEventListener("pointermove", onPointerMove);
646
+ document.addEventListener("pointerup", onPointerUp);
647
+ trigger.addEventListener("keydown", onTriggerKey);
648
+ }
420
649
  applyState();
421
- return () => {
422
- trigger.removeEventListener("click", onTriggerClick);
650
+ const dispose = () => {
651
+ trigger?.removeEventListener("click", onTriggerClick);
423
652
  closeBtn.removeEventListener("click", closePanel);
424
653
  resetBtn.removeEventListener("click", onReset);
425
654
  for (const btn of opts$) btn.removeEventListener("click", onOptClick);
@@ -427,7 +656,12 @@ function bindPanelBehavior(root, opts = {}) {
427
656
  document.removeEventListener("keydown", onKeyDown);
428
657
  document.removeEventListener("mousemove", onMove);
429
658
  document.removeEventListener("touchmove", onMove);
659
+ trigger?.removeEventListener("pointerdown", onPointerDown);
660
+ document.removeEventListener("pointermove", onPointerMove);
661
+ document.removeEventListener("pointerup", onPointerUp);
662
+ trigger?.removeEventListener("keydown", onTriggerKey);
430
663
  };
664
+ return Object.assign(dispose, { open: openPanel, close: closePanel, toggle: togglePanel });
431
665
  }
432
666
  function ensureOverlay() {
433
667
  let el = document.getElementById("oks-overlay-gray");
@@ -482,6 +716,12 @@ var PANEL_CSS = `
482
716
  width: 60%;
483
717
  height: 60%;
484
718
  }
719
+ /* Porthole preset: a brushed-metal ring framing the standard glyph. The glyph
720
+ itself is untouched (discoverability), only the button gets the frame.
721
+ Hosts wanting a bespoke frame can target ::part(trigger) from light DOM. */
722
+ .oks-access-btn[data-trigger-icon="porthole"] {
723
+ box-shadow: 0 0 0 4px #b0bec5, 0 0 0 7px #546e7a, 0 6px 18px rgba(0,0,0,0.3);
724
+ }
485
725
  .oks-active-badge {
486
726
  position: absolute;
487
727
  top: -2px;
@@ -650,6 +890,12 @@ var PANEL_CSS = `
650
890
  .oks-access-branding { margin-top: 12px; font-size: 12px; color: #000; font-weight: 700; }
651
891
  .oks-access-branding a { color: #000; text-decoration: none; border-bottom: 1px dotted #000; }
652
892
 
893
+ @media (prefers-reduced-motion: reduce) {
894
+ .oks-access-btn, .oks-access-panel, .oks-access-close,
895
+ .oks-preset, .oks-access-opt { transition: none; }
896
+ .oks-access-btn:hover { transform: scale(1); }
897
+ .oks-preset.is-flashing { transform: none; }
898
+ }
653
899
  @media (max-width: 768px) {
654
900
  .oks-access-panel {
655
901
  width: 100%; height: 100%; max-height: 100%;
@@ -827,6 +1073,6 @@ body.oks-a11y-bigtargets summary:not(oksigenia-access-panel):not(oksigenia-acces
827
1073
  }
828
1074
  `;
829
1075
 
830
- export { COLORBLIND_FILTERS_SVG, DEFAULT_STATE, EFFECT_CSS, PANEL_CSS, bindPanelBehavior, buildPanelHtml, getTranslation, isStateEmpty, loadState, positionCss, saveState, supportedLocales };
1076
+ export { ALL_CONTROLS, COLORBLIND_FILTERS_SVG, DEFAULT_STATE, EFFECT_CSS, PANEL_CSS, PRESETS, PRESET_IDS, bindPanelBehavior, buildPanelHtml, getTranslation, isStateEmpty, loadState, positionCss, resolveEnabledControls, saveState, supportedLocales };
831
1077
  //# sourceMappingURL=index.js.map
832
1078
  //# sourceMappingURL=index.js.map