accessify-widget 0.1.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.
Files changed (54) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +76 -0
  3. package/dist/accessify.min.js +2 -0
  4. package/dist/accessify.min.js.map +1 -0
  5. package/dist/accessify.mjs +6 -0
  6. package/dist/accessify.mjs.map +1 -0
  7. package/dist/alt-text-CLxbmwG6.js +567 -0
  8. package/dist/alt-text-CLxbmwG6.js.map +1 -0
  9. package/dist/animation-stop-DXebPS8D.js +88 -0
  10. package/dist/animation-stop-DXebPS8D.js.map +1 -0
  11. package/dist/auto-scan-pg-09o7A.js +885 -0
  12. package/dist/auto-scan-pg-09o7A.js.map +1 -0
  13. package/dist/big-cursor-B2UKu9dQ.js +88 -0
  14. package/dist/big-cursor-B2UKu9dQ.js.map +1 -0
  15. package/dist/color-blind-0LFng55r.js +108 -0
  16. package/dist/color-blind-0LFng55r.js.map +1 -0
  17. package/dist/contrast-DCkE0NXZ.js +64 -0
  18. package/dist/contrast-DCkE0NXZ.js.map +1 -0
  19. package/dist/dyslexia-font-wONgIy2T.js +77 -0
  20. package/dist/dyslexia-font-wONgIy2T.js.map +1 -0
  21. package/dist/focus-highlight-CjERyyUF.js +93 -0
  22. package/dist/focus-highlight-CjERyyUF.js.map +1 -0
  23. package/dist/hide-images-DJwmsV2C.js +39 -0
  24. package/dist/hide-images-DJwmsV2C.js.map +1 -0
  25. package/dist/index-CUQfpnwR.js +6520 -0
  26. package/dist/index-CUQfpnwR.js.map +1 -0
  27. package/dist/keyboard-nav-BdPyLaZt.js +312 -0
  28. package/dist/keyboard-nav-BdPyLaZt.js.map +1 -0
  29. package/dist/line-height-BT98qgEF.js +54 -0
  30. package/dist/line-height-BT98qgEF.js.map +1 -0
  31. package/dist/link-highlight-DBGm067Y.js +87 -0
  32. package/dist/link-highlight-DBGm067Y.js.map +1 -0
  33. package/dist/loader.min.js +1 -0
  34. package/dist/page-structure-2X8mOSpC.js +166 -0
  35. package/dist/page-structure-2X8mOSpC.js.map +1 -0
  36. package/dist/reading-guide-VT8NciIL.js +122 -0
  37. package/dist/reading-guide-VT8NciIL.js.map +1 -0
  38. package/dist/reading-mask-BABChuCz.js +76 -0
  39. package/dist/reading-mask-BABChuCz.js.map +1 -0
  40. package/dist/saturation-D8ZXpWAN.js +59 -0
  41. package/dist/saturation-D8ZXpWAN.js.map +1 -0
  42. package/dist/spacing-DENai3JU.js +106 -0
  43. package/dist/spacing-DENai3JU.js.map +1 -0
  44. package/dist/text-align-BDRPqPvl.js +51 -0
  45. package/dist/text-align-BDRPqPvl.js.map +1 -0
  46. package/dist/text-simplify-CELklw5A.js +223 -0
  47. package/dist/text-simplify-CELklw5A.js.map +1 -0
  48. package/dist/text-size-B-uv436p.js +69 -0
  49. package/dist/text-size-B-uv436p.js.map +1 -0
  50. package/dist/tts-02b9iV0h.js +505 -0
  51. package/dist/tts-02b9iV0h.js.map +1 -0
  52. package/dist/widget.js +2 -0
  53. package/dist/widget.js.map +1 -0
  54. package/package.json +61 -0
@@ -0,0 +1,166 @@
1
+ function createPageStructureModule() {
2
+ let enabled = false;
3
+ let panelEl = null;
4
+ const PANEL_ID = "accessify-page-structure";
5
+ function collectHeadings() {
6
+ const results = [];
7
+ const headings = document.querySelectorAll("h1, h2, h3, h4, h5, h6");
8
+ headings.forEach((el) => {
9
+ if (el.closest("#accessify-root")) return;
10
+ const text = el.textContent?.trim() || "";
11
+ if (text) results.push({ tag: el.tagName.toLowerCase(), text, el });
12
+ });
13
+ return results;
14
+ }
15
+ function collectLandmarks() {
16
+ const results = [];
17
+ const selectors = [
18
+ "main",
19
+ "nav",
20
+ "aside",
21
+ "header",
22
+ "footer",
23
+ "section[aria-label]",
24
+ "section[aria-labelledby]",
25
+ '[role="main"]',
26
+ '[role="navigation"]',
27
+ '[role="complementary"]',
28
+ '[role="banner"]',
29
+ '[role="contentinfo"]',
30
+ '[role="search"]'
31
+ ];
32
+ const els = document.querySelectorAll(selectors.join(","));
33
+ els.forEach((el) => {
34
+ if (el.closest("#accessify-root")) return;
35
+ const role = el.getAttribute("role") || el.tagName.toLowerCase();
36
+ const label = el.getAttribute("aria-label") || el.getAttribute("aria-labelledby") || role;
37
+ results.push({ role, label, el });
38
+ });
39
+ return results;
40
+ }
41
+ function buildPanel() {
42
+ panelEl = document.createElement("div");
43
+ panelEl.id = PANEL_ID;
44
+ panelEl.setAttribute("role", "dialog");
45
+ panelEl.setAttribute("aria-label", "Page Structure");
46
+ const headings = collectHeadings();
47
+ const landmarks = collectLandmarks();
48
+ const indent = {
49
+ h1: "0",
50
+ h2: "12px",
51
+ h3: "24px",
52
+ h4: "36px",
53
+ h5: "48px",
54
+ h6: "60px"
55
+ };
56
+ let html = `
57
+ <style>
58
+ #${PANEL_ID} {
59
+ position: fixed; top: 50%; left: 50%;
60
+ transform: translate(-50%, -50%);
61
+ z-index: 999998;
62
+ background: #fff; color: #1a1a1a;
63
+ border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.2);
64
+ padding: 24px; width: 400px; max-height: 80vh;
65
+ overflow-y: auto; font-family: system-ui, sans-serif;
66
+ font-size: 14px; line-height: 1.5;
67
+ }
68
+ #${PANEL_ID} h3 { margin: 0 0 12px; font-size: 16px; }
69
+ #${PANEL_ID} .ps-section { margin-bottom: 16px; }
70
+ #${PANEL_ID} .ps-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #888; margin-bottom: 6px; }
71
+ #${PANEL_ID} .ps-item {
72
+ display: block; width: 100%; text-align: left;
73
+ padding: 6px 8px; border: none; background: none;
74
+ cursor: pointer; border-radius: 6px; font-size: 13px;
75
+ color: #1a1a1a; font-family: inherit;
76
+ }
77
+ #${PANEL_ID} .ps-item:hover { background: #f0f0f2; }
78
+ #${PANEL_ID} .ps-item:focus-visible { outline: 2px solid #0055CC; outline-offset: 1px; }
79
+ #${PANEL_ID} .ps-tag { color: #888; font-size: 11px; margin-right: 6px; }
80
+ #${PANEL_ID} .ps-close {
81
+ position: absolute; top: 12px; right: 12px;
82
+ background: none; border: none; cursor: pointer;
83
+ font-size: 18px; color: #888; padding: 4px 8px; border-radius: 4px;
84
+ }
85
+ #${PANEL_ID} .ps-close:hover { background: #f0f0f2; }
86
+ @media (prefers-color-scheme: dark) {
87
+ #${PANEL_ID} { background: #1a1a1a; color: #f0f0f0; }
88
+ #${PANEL_ID} .ps-item { color: #f0f0f0; }
89
+ #${PANEL_ID} .ps-item:hover { background: #2a2a2e; }
90
+ #${PANEL_ID} .ps-close { color: #aaa; }
91
+ #${PANEL_ID} .ps-close:hover { background: #2a2a2e; }
92
+ }
93
+ </style>
94
+ <button class="ps-close" aria-label="Close">&times;</button>
95
+ <h3>Page Structure</h3>
96
+ `;
97
+ if (headings.length > 0) {
98
+ html += '<div class="ps-section"><div class="ps-label">Headings</div>';
99
+ headings.forEach((h, i) => {
100
+ html += `<button class="ps-item" data-type="heading" data-index="${i}" style="padding-left:${indent[h.tag] || "0"}">
101
+ <span class="ps-tag">${h.tag}</span>${escapeHtml(h.text.slice(0, 80))}
102
+ </button>`;
103
+ });
104
+ html += "</div>";
105
+ }
106
+ if (landmarks.length > 0) {
107
+ html += '<div class="ps-section"><div class="ps-label">Landmarks</div>';
108
+ landmarks.forEach((lm, i) => {
109
+ html += `<button class="ps-item" data-type="landmark" data-index="${i}">
110
+ <span class="ps-tag">${escapeHtml(lm.role)}</span>${escapeHtml(lm.label.slice(0, 60))}
111
+ </button>`;
112
+ });
113
+ html += "</div>";
114
+ }
115
+ if (headings.length === 0 && landmarks.length === 0) {
116
+ html += '<p style="color:#888">No headings or landmarks found on this page.</p>';
117
+ }
118
+ panelEl.innerHTML = html;
119
+ panelEl.addEventListener("click", (e) => {
120
+ const btn = e.target.closest(".ps-item");
121
+ if (btn) {
122
+ const type = btn.dataset.type;
123
+ const idx = parseInt(btn.dataset.index || "0", 10);
124
+ let target;
125
+ if (type === "heading") target = headings[idx]?.el;
126
+ else if (type === "landmark") target = landmarks[idx]?.el;
127
+ if (target) {
128
+ target.scrollIntoView({ behavior: "smooth", block: "center" });
129
+ target.focus({ preventScroll: true });
130
+ }
131
+ }
132
+ if (e.target.closest(".ps-close")) {
133
+ deactivate();
134
+ }
135
+ });
136
+ document.body.appendChild(panelEl);
137
+ const firstBtn = panelEl.querySelector(".ps-item, .ps-close");
138
+ firstBtn?.focus();
139
+ }
140
+ function escapeHtml(str) {
141
+ return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
142
+ }
143
+ function activate() {
144
+ enabled = true;
145
+ buildPanel();
146
+ }
147
+ function deactivate() {
148
+ enabled = false;
149
+ panelEl?.remove();
150
+ panelEl = null;
151
+ }
152
+ return {
153
+ id: "page-structure",
154
+ name: () => "Page Structure",
155
+ description: "View headings and landmarks for quick navigation",
156
+ icon: "page-structure",
157
+ category: "motor",
158
+ activate,
159
+ deactivate,
160
+ getState: () => ({ id: "page-structure", enabled })
161
+ };
162
+ }
163
+ export {
164
+ createPageStructureModule as default
165
+ };
166
+ //# sourceMappingURL=page-structure-2X8mOSpC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"page-structure-2X8mOSpC.js","sources":["../src/features/page-structure.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\nexport default function createPageStructureModule(): FeatureModule {\n let enabled = false;\n let panelEl: HTMLElement | null = null;\n const PANEL_ID = 'accessify-page-structure';\n\n function collectHeadings(): Array<{ tag: string; text: string; el: HTMLElement }> {\n const results: Array<{ tag: string; text: string; el: HTMLElement }> = [];\n const headings = document.querySelectorAll<HTMLElement>('h1, h2, h3, h4, h5, h6');\n headings.forEach((el) => {\n if (el.closest('#accessify-root')) return;\n const text = el.textContent?.trim() || '';\n if (text) results.push({ tag: el.tagName.toLowerCase(), text, el });\n });\n return results;\n }\n\n function collectLandmarks(): Array<{ role: string; label: string; el: HTMLElement }> {\n const results: Array<{ role: string; label: string; el: HTMLElement }> = [];\n const selectors = [\n 'main', 'nav', 'aside', 'header', 'footer', 'section[aria-label]',\n 'section[aria-labelledby]', '[role=\"main\"]', '[role=\"navigation\"]',\n '[role=\"complementary\"]', '[role=\"banner\"]', '[role=\"contentinfo\"]',\n '[role=\"search\"]',\n ];\n const els = document.querySelectorAll<HTMLElement>(selectors.join(','));\n els.forEach((el) => {\n if (el.closest('#accessify-root')) return;\n const role = el.getAttribute('role') || el.tagName.toLowerCase();\n const label = el.getAttribute('aria-label') || el.getAttribute('aria-labelledby') || role;\n results.push({ role, label, el });\n });\n return results;\n }\n\n function buildPanel() {\n panelEl = document.createElement('div');\n panelEl.id = PANEL_ID;\n panelEl.setAttribute('role', 'dialog');\n panelEl.setAttribute('aria-label', 'Page Structure');\n\n const headings = collectHeadings();\n const landmarks = collectLandmarks();\n\n const indent: Record<string, string> = {\n h1: '0', h2: '12px', h3: '24px', h4: '36px', h5: '48px', h6: '60px',\n };\n\n let html = `\n <style>\n #${PANEL_ID} {\n position: fixed; top: 50%; left: 50%;\n transform: translate(-50%, -50%);\n z-index: 999998;\n background: #fff; color: #1a1a1a;\n border-radius: 12px; box-shadow: 0 8px 32px rgba(0,0,0,0.2);\n padding: 24px; width: 400px; max-height: 80vh;\n overflow-y: auto; font-family: system-ui, sans-serif;\n font-size: 14px; line-height: 1.5;\n }\n #${PANEL_ID} h3 { margin: 0 0 12px; font-size: 16px; }\n #${PANEL_ID} .ps-section { margin-bottom: 16px; }\n #${PANEL_ID} .ps-label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.5px; color: #888; margin-bottom: 6px; }\n #${PANEL_ID} .ps-item {\n display: block; width: 100%; text-align: left;\n padding: 6px 8px; border: none; background: none;\n cursor: pointer; border-radius: 6px; font-size: 13px;\n color: #1a1a1a; font-family: inherit;\n }\n #${PANEL_ID} .ps-item:hover { background: #f0f0f2; }\n #${PANEL_ID} .ps-item:focus-visible { outline: 2px solid #0055CC; outline-offset: 1px; }\n #${PANEL_ID} .ps-tag { color: #888; font-size: 11px; margin-right: 6px; }\n #${PANEL_ID} .ps-close {\n position: absolute; top: 12px; right: 12px;\n background: none; border: none; cursor: pointer;\n font-size: 18px; color: #888; padding: 4px 8px; border-radius: 4px;\n }\n #${PANEL_ID} .ps-close:hover { background: #f0f0f2; }\n @media (prefers-color-scheme: dark) {\n #${PANEL_ID} { background: #1a1a1a; color: #f0f0f0; }\n #${PANEL_ID} .ps-item { color: #f0f0f0; }\n #${PANEL_ID} .ps-item:hover { background: #2a2a2e; }\n #${PANEL_ID} .ps-close { color: #aaa; }\n #${PANEL_ID} .ps-close:hover { background: #2a2a2e; }\n }\n </style>\n <button class=\"ps-close\" aria-label=\"Close\">&times;</button>\n <h3>Page Structure</h3>\n `;\n\n if (headings.length > 0) {\n html += '<div class=\"ps-section\"><div class=\"ps-label\">Headings</div>';\n headings.forEach((h, i) => {\n html += `<button class=\"ps-item\" data-type=\"heading\" data-index=\"${i}\" style=\"padding-left:${indent[h.tag] || '0'}\">\n <span class=\"ps-tag\">${h.tag}</span>${escapeHtml(h.text.slice(0, 80))}\n </button>`;\n });\n html += '</div>';\n }\n\n if (landmarks.length > 0) {\n html += '<div class=\"ps-section\"><div class=\"ps-label\">Landmarks</div>';\n landmarks.forEach((lm, i) => {\n html += `<button class=\"ps-item\" data-type=\"landmark\" data-index=\"${i}\">\n <span class=\"ps-tag\">${escapeHtml(lm.role)}</span>${escapeHtml(lm.label.slice(0, 60))}\n </button>`;\n });\n html += '</div>';\n }\n\n if (headings.length === 0 && landmarks.length === 0) {\n html += '<p style=\"color:#888\">No headings or landmarks found on this page.</p>';\n }\n\n panelEl.innerHTML = html;\n\n // Event delegation\n panelEl.addEventListener('click', (e) => {\n const btn = (e.target as HTMLElement).closest<HTMLElement>('.ps-item');\n if (btn) {\n const type = btn.dataset.type;\n const idx = parseInt(btn.dataset.index || '0', 10);\n let target: HTMLElement | undefined;\n if (type === 'heading') target = headings[idx]?.el;\n else if (type === 'landmark') target = landmarks[idx]?.el;\n if (target) {\n target.scrollIntoView({ behavior: 'smooth', block: 'center' });\n target.focus({ preventScroll: true });\n }\n }\n if ((e.target as HTMLElement).closest('.ps-close')) {\n deactivate();\n }\n });\n\n document.body.appendChild(panelEl);\n const firstBtn = panelEl.querySelector<HTMLElement>('.ps-item, .ps-close');\n firstBtn?.focus();\n }\n\n function escapeHtml(str: string): string {\n return str.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');\n }\n\n function activate() {\n enabled = true;\n buildPanel();\n }\n\n function deactivate() {\n enabled = false;\n panelEl?.remove();\n panelEl = null;\n }\n\n return {\n id: 'page-structure',\n name: () => 'Page Structure',\n description: 'View headings and landmarks for quick navigation',\n icon: 'page-structure',\n category: 'motor',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'page-structure', enabled }),\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,122 @@
1
+ const DEFAULT_OPTIONS$1 = {
2
+ height: 8,
3
+ opacity: 0.3
4
+ };
5
+ function createReadingGuideModule() {
6
+ let enabled = false;
7
+ let guideEl = null;
8
+ let currentY = 0;
9
+ let options = { ...DEFAULT_OPTIONS$1 };
10
+ const STORAGE_KEY = "accessify-reading-guide";
11
+ const GUIDE_ID = "accessify-reading-guide";
12
+ function handleMouseMove(e) {
13
+ currentY = e.clientY;
14
+ updateGuidePosition();
15
+ }
16
+ function handleTouchMove(e) {
17
+ if (e.touches.length > 0) {
18
+ currentY = e.touches[0].clientY;
19
+ updateGuidePosition();
20
+ }
21
+ }
22
+ function handleKeyDown(e) {
23
+ if (!enabled || !guideEl) return;
24
+ const STEP = 10;
25
+ if (e.key === "ArrowUp") {
26
+ e.preventDefault();
27
+ currentY = Math.max(0, currentY - STEP);
28
+ updateGuidePosition();
29
+ } else if (e.key === "ArrowDown") {
30
+ e.preventDefault();
31
+ currentY = Math.min(window.innerHeight, currentY + STEP);
32
+ updateGuidePosition();
33
+ }
34
+ }
35
+ function updateGuidePosition() {
36
+ if (!guideEl) return;
37
+ const halfHeight = options.height / 2;
38
+ guideEl.style.top = `${currentY - halfHeight}px`;
39
+ }
40
+ function createGuideElement() {
41
+ const el = document.createElement("div");
42
+ el.id = GUIDE_ID;
43
+ el.setAttribute("role", "presentation");
44
+ el.setAttribute("aria-hidden", "true");
45
+ Object.assign(el.style, {
46
+ position: "fixed",
47
+ left: "0",
48
+ width: "100%",
49
+ height: `${options.height}px`,
50
+ backgroundColor: `rgba(255, 255, 0, ${options.opacity})`,
51
+ pointerEvents: "none",
52
+ zIndex: "2147483646",
53
+ top: "50%",
54
+ transition: "top 0.05s linear",
55
+ boxShadow: "0 0 4px rgba(0,0,0,0.2)"
56
+ });
57
+ return el;
58
+ }
59
+ function activate() {
60
+ enabled = true;
61
+ const saved = localStorage.getItem(STORAGE_KEY);
62
+ if (saved) {
63
+ try {
64
+ const parsed = JSON.parse(saved);
65
+ options = {
66
+ height: Math.min(60, Math.max(2, parsed.height ?? DEFAULT_OPTIONS$1.height)),
67
+ opacity: Math.min(1, Math.max(0.1, parsed.opacity ?? DEFAULT_OPTIONS$1.opacity))
68
+ };
69
+ } catch {
70
+ options = { ...DEFAULT_OPTIONS$1 };
71
+ }
72
+ }
73
+ guideEl = createGuideElement();
74
+ document.documentElement.appendChild(guideEl);
75
+ currentY = window.innerHeight / 2;
76
+ updateGuidePosition();
77
+ document.addEventListener("mousemove", handleMouseMove, { passive: true });
78
+ document.addEventListener("touchmove", handleTouchMove, { passive: true });
79
+ document.addEventListener("keydown", handleKeyDown);
80
+ }
81
+ function deactivate() {
82
+ enabled = false;
83
+ document.removeEventListener("mousemove", handleMouseMove);
84
+ document.removeEventListener("touchmove", handleTouchMove);
85
+ document.removeEventListener("keydown", handleKeyDown);
86
+ if (guideEl) {
87
+ guideEl.remove();
88
+ guideEl = null;
89
+ }
90
+ localStorage.removeItem(STORAGE_KEY);
91
+ }
92
+ return {
93
+ id: "reading-guide",
94
+ name: () => "Reading Guide",
95
+ description: "Horizontal ruler that follows your cursor for easier reading",
96
+ icon: "reading-guide",
97
+ category: "cognitive",
98
+ activate,
99
+ deactivate,
100
+ getState: () => ({
101
+ id: "reading-guide",
102
+ enabled,
103
+ value: { ...options }
104
+ }),
105
+ setState: (newOptions) => {
106
+ options = {
107
+ height: Math.min(60, Math.max(2, newOptions.height ?? options.height)),
108
+ opacity: Math.min(1, Math.max(0.1, newOptions.opacity ?? options.opacity))
109
+ };
110
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(options));
111
+ if (guideEl) {
112
+ guideEl.style.height = `${options.height}px`;
113
+ guideEl.style.backgroundColor = `rgba(255, 255, 0, ${options.opacity})`;
114
+ updateGuidePosition();
115
+ }
116
+ }
117
+ };
118
+ }
119
+ export {
120
+ createReadingGuideModule as default
121
+ };
122
+ //# sourceMappingURL=reading-guide-VT8NciIL.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reading-guide-VT8NciIL.js","sources":["../src/features/reading-guide.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\ninterface ReadingGuideOptions {\n height: number;\n opacity: number;\n}\n\nconst DEFAULT_OPTIONS: ReadingGuideOptions = {\n height: 8,\n opacity: 0.3,\n};\n\nexport default function createReadingGuideModule(): FeatureModule {\n let enabled = false;\n let guideEl: HTMLDivElement | null = null;\n let currentY = 0;\n let options: ReadingGuideOptions = { ...DEFAULT_OPTIONS };\n const STORAGE_KEY = 'accessify-reading-guide';\n const GUIDE_ID = 'accessify-reading-guide';\n\n function handleMouseMove(e: MouseEvent) {\n currentY = e.clientY;\n updateGuidePosition();\n }\n\n function handleTouchMove(e: TouchEvent) {\n if (e.touches.length > 0) {\n currentY = e.touches[0].clientY;\n updateGuidePosition();\n }\n }\n\n function handleKeyDown(e: KeyboardEvent) {\n if (!enabled || !guideEl) return;\n const STEP = 10;\n if (e.key === 'ArrowUp') {\n e.preventDefault();\n currentY = Math.max(0, currentY - STEP);\n updateGuidePosition();\n } else if (e.key === 'ArrowDown') {\n e.preventDefault();\n currentY = Math.min(window.innerHeight, currentY + STEP);\n updateGuidePosition();\n }\n }\n\n function updateGuidePosition() {\n if (!guideEl) return;\n const halfHeight = options.height / 2;\n guideEl.style.top = `${currentY - halfHeight}px`;\n }\n\n function createGuideElement(): HTMLDivElement {\n const el = document.createElement('div');\n el.id = GUIDE_ID;\n el.setAttribute('role', 'presentation');\n el.setAttribute('aria-hidden', 'true');\n Object.assign(el.style, {\n position: 'fixed',\n left: '0',\n width: '100%',\n height: `${options.height}px`,\n backgroundColor: `rgba(255, 255, 0, ${options.opacity})`,\n pointerEvents: 'none',\n zIndex: '2147483646',\n top: '50%',\n transition: 'top 0.05s linear',\n boxShadow: '0 0 4px rgba(0,0,0,0.2)',\n });\n return el;\n }\n\n function activate() {\n enabled = true;\n const saved = localStorage.getItem(STORAGE_KEY);\n if (saved) {\n try {\n const parsed = JSON.parse(saved);\n options = {\n height: Math.min(60, Math.max(2, parsed.height ?? DEFAULT_OPTIONS.height)),\n opacity: Math.min(1, Math.max(0.1, parsed.opacity ?? DEFAULT_OPTIONS.opacity)),\n };\n } catch {\n options = { ...DEFAULT_OPTIONS };\n }\n }\n\n guideEl = createGuideElement();\n document.documentElement.appendChild(guideEl);\n\n currentY = window.innerHeight / 2;\n updateGuidePosition();\n\n document.addEventListener('mousemove', handleMouseMove, { passive: true });\n document.addEventListener('touchmove', handleTouchMove, { passive: true });\n document.addEventListener('keydown', handleKeyDown);\n }\n\n function deactivate() {\n enabled = false;\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('touchmove', handleTouchMove);\n document.removeEventListener('keydown', handleKeyDown);\n\n if (guideEl) {\n guideEl.remove();\n guideEl = null;\n }\n localStorage.removeItem(STORAGE_KEY);\n }\n\n return {\n id: 'reading-guide',\n name: () => 'Reading Guide',\n description: 'Horizontal ruler that follows your cursor for easier reading',\n icon: 'reading-guide',\n category: 'cognitive',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'reading-guide',\n enabled,\n value: { ...options },\n }),\n setState: (newOptions: Partial<ReadingGuideOptions>) => {\n options = {\n height: Math.min(60, Math.max(2, newOptions.height ?? options.height)),\n opacity: Math.min(1, Math.max(0.1, newOptions.opacity ?? options.opacity)),\n };\n localStorage.setItem(STORAGE_KEY, JSON.stringify(options));\n\n if (guideEl) {\n guideEl.style.height = `${options.height}px`;\n guideEl.style.backgroundColor = `rgba(255, 255, 0, ${options.opacity})`;\n updateGuidePosition();\n }\n },\n };\n}\n"],"names":["DEFAULT_OPTIONS"],"mappings":"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"}
@@ -0,0 +1,76 @@
1
+ function createReadingMaskModule() {
2
+ let enabled = false;
3
+ let maskTop = null;
4
+ let maskBottom = null;
5
+ let viewportHeight = 120;
6
+ function createOverlays() {
7
+ maskTop = document.createElement("div");
8
+ maskBottom = document.createElement("div");
9
+ const baseStyle = `
10
+ position: fixed; left: 0; right: 0; z-index: 999990;
11
+ background: rgba(0, 0, 0, 0.65); pointer-events: none;
12
+ transition: top 80ms ease, bottom 80ms ease, height 80ms ease;
13
+ `;
14
+ maskTop.setAttribute("style", baseStyle + "top: 0;");
15
+ maskTop.setAttribute("aria-hidden", "true");
16
+ maskTop.id = "accessify-mask-top";
17
+ maskBottom.setAttribute("style", baseStyle + "bottom: 0;");
18
+ maskBottom.setAttribute("aria-hidden", "true");
19
+ maskBottom.id = "accessify-mask-bottom";
20
+ document.body.appendChild(maskTop);
21
+ document.body.appendChild(maskBottom);
22
+ }
23
+ function handleMouseMove(e) {
24
+ if (!maskTop || !maskBottom) return;
25
+ const y = e.clientY;
26
+ const half = viewportHeight / 2;
27
+ const topH = Math.max(0, y - half);
28
+ const bottomH = Math.max(0, window.innerHeight - y - half);
29
+ maskTop.style.height = topH + "px";
30
+ maskBottom.style.height = bottomH + "px";
31
+ }
32
+ function handleKeyDown(e) {
33
+ if (!maskTop || !maskBottom) return;
34
+ if (e.key === "ArrowUp" || e.key === "ArrowDown") {
35
+ const active = document.activeElement;
36
+ if (!active) return;
37
+ const rect = active.getBoundingClientRect();
38
+ const y = rect.top + rect.height / 2;
39
+ const half = viewportHeight / 2;
40
+ maskTop.style.height = Math.max(0, y - half) + "px";
41
+ maskBottom.style.height = Math.max(0, window.innerHeight - y - half) + "px";
42
+ }
43
+ }
44
+ function activate() {
45
+ enabled = true;
46
+ createOverlays();
47
+ document.addEventListener("mousemove", handleMouseMove);
48
+ document.addEventListener("keydown", handleKeyDown);
49
+ }
50
+ function deactivate() {
51
+ enabled = false;
52
+ document.removeEventListener("mousemove", handleMouseMove);
53
+ document.removeEventListener("keydown", handleKeyDown);
54
+ maskTop?.remove();
55
+ maskBottom?.remove();
56
+ maskTop = null;
57
+ maskBottom = null;
58
+ }
59
+ return {
60
+ id: "reading-mask",
61
+ name: () => "Reading Mask",
62
+ description: "Focus window that dims surrounding content",
63
+ icon: "reading-mask",
64
+ category: "motor",
65
+ activate,
66
+ deactivate,
67
+ getState: () => ({ id: "reading-mask", enabled, value: viewportHeight }),
68
+ setState: (height) => {
69
+ viewportHeight = Math.min(400, Math.max(60, height));
70
+ }
71
+ };
72
+ }
73
+ export {
74
+ createReadingMaskModule as default
75
+ };
76
+ //# sourceMappingURL=reading-mask-BABChuCz.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reading-mask-BABChuCz.js","sources":["../src/features/reading-mask.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\nexport default function createReadingMaskModule(): FeatureModule {\n let enabled = false;\n let maskTop: HTMLElement | null = null;\n let maskBottom: HTMLElement | null = null;\n let viewportHeight = 120;\n\n const STORAGE_KEY = 'accessify-reading-mask';\n\n function createOverlays() {\n maskTop = document.createElement('div');\n maskBottom = document.createElement('div');\n\n const baseStyle = `\n position: fixed; left: 0; right: 0; z-index: 999990;\n background: rgba(0, 0, 0, 0.65); pointer-events: none;\n transition: top 80ms ease, bottom 80ms ease, height 80ms ease;\n `;\n maskTop.setAttribute('style', baseStyle + 'top: 0;');\n maskTop.setAttribute('aria-hidden', 'true');\n maskTop.id = 'accessify-mask-top';\n maskBottom.setAttribute('style', baseStyle + 'bottom: 0;');\n maskBottom.setAttribute('aria-hidden', 'true');\n maskBottom.id = 'accessify-mask-bottom';\n\n document.body.appendChild(maskTop);\n document.body.appendChild(maskBottom);\n }\n\n function handleMouseMove(e: MouseEvent) {\n if (!maskTop || !maskBottom) return;\n const y = e.clientY;\n const half = viewportHeight / 2;\n const topH = Math.max(0, y - half);\n const bottomH = Math.max(0, window.innerHeight - y - half);\n maskTop.style.height = topH + 'px';\n maskBottom.style.height = bottomH + 'px';\n }\n\n function handleKeyDown(e: KeyboardEvent) {\n if (!maskTop || !maskBottom) return;\n if (e.key === 'ArrowUp' || e.key === 'ArrowDown') {\n const active = document.activeElement as HTMLElement | null;\n if (!active) return;\n const rect = active.getBoundingClientRect();\n const y = rect.top + rect.height / 2;\n const half = viewportHeight / 2;\n maskTop.style.height = Math.max(0, y - half) + 'px';\n maskBottom.style.height = Math.max(0, window.innerHeight - y - half) + 'px';\n }\n }\n\n function activate() {\n enabled = true;\n createOverlays();\n document.addEventListener('mousemove', handleMouseMove);\n document.addEventListener('keydown', handleKeyDown);\n }\n\n function deactivate() {\n enabled = false;\n document.removeEventListener('mousemove', handleMouseMove);\n document.removeEventListener('keydown', handleKeyDown);\n maskTop?.remove();\n maskBottom?.remove();\n maskTop = null;\n maskBottom = null;\n }\n\n return {\n id: 'reading-mask',\n name: () => 'Reading Mask',\n description: 'Focus window that dims surrounding content',\n icon: 'reading-mask',\n category: 'motor',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'reading-mask', enabled, value: viewportHeight }),\n setState: (height: number) => {\n viewportHeight = Math.min(400, Math.max(60, height));\n },\n };\n}\n"],"names":[],"mappings":"AAEA,SAAwB,0BAAyC;AAC/D,MAAI,UAAU;AACd,MAAI,UAA8B;AAClC,MAAI,aAAiC;AACrC,MAAI,iBAAiB;AAIrB,WAAS,iBAAiB;AACxB,cAAU,SAAS,cAAc,KAAK;AACtC,iBAAa,SAAS,cAAc,KAAK;AAEzC,UAAM,YAAY;AAAA;AAAA;AAAA;AAAA;AAKlB,YAAQ,aAAa,SAAS,YAAY,SAAS;AACnD,YAAQ,aAAa,eAAe,MAAM;AAC1C,YAAQ,KAAK;AACb,eAAW,aAAa,SAAS,YAAY,YAAY;AACzD,eAAW,aAAa,eAAe,MAAM;AAC7C,eAAW,KAAK;AAEhB,aAAS,KAAK,YAAY,OAAO;AACjC,aAAS,KAAK,YAAY,UAAU;AAAA,EACtC;AAEA,WAAS,gBAAgB,GAAe;AACtC,QAAI,CAAC,WAAW,CAAC,WAAY;AAC7B,UAAM,IAAI,EAAE;AACZ,UAAM,OAAO,iBAAiB;AAC9B,UAAM,OAAO,KAAK,IAAI,GAAG,IAAI,IAAI;AACjC,UAAM,UAAU,KAAK,IAAI,GAAG,OAAO,cAAc,IAAI,IAAI;AACzD,YAAQ,MAAM,SAAS,OAAO;AAC9B,eAAW,MAAM,SAAS,UAAU;AAAA,EACtC;AAEA,WAAS,cAAc,GAAkB;AACvC,QAAI,CAAC,WAAW,CAAC,WAAY;AAC7B,QAAI,EAAE,QAAQ,aAAa,EAAE,QAAQ,aAAa;AAChD,YAAM,SAAS,SAAS;AACxB,UAAI,CAAC,OAAQ;AACb,YAAM,OAAO,OAAO,sBAAA;AACpB,YAAM,IAAI,KAAK,MAAM,KAAK,SAAS;AACnC,YAAM,OAAO,iBAAiB;AAC9B,cAAQ,MAAM,SAAS,KAAK,IAAI,GAAG,IAAI,IAAI,IAAI;AAC/C,iBAAW,MAAM,SAAS,KAAK,IAAI,GAAG,OAAO,cAAc,IAAI,IAAI,IAAI;AAAA,IACzE;AAAA,EACF;AAEA,WAAS,WAAW;AAClB,cAAU;AACV,mBAAA;AACA,aAAS,iBAAiB,aAAa,eAAe;AACtD,aAAS,iBAAiB,WAAW,aAAa;AAAA,EACpD;AAEA,WAAS,aAAa;AACpB,cAAU;AACV,aAAS,oBAAoB,aAAa,eAAe;AACzD,aAAS,oBAAoB,WAAW,aAAa;AACrD,aAAS,OAAA;AACT,gBAAY,OAAA;AACZ,cAAU;AACV,iBAAa;AAAA,EACf;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM,MAAM;AAAA,IACZ,aAAa;AAAA,IACb,MAAM;AAAA,IACN,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU,OAAqB,EAAE,IAAI,gBAAgB,SAAS,OAAO;IACrE,UAAU,CAAC,WAAmB;AAC5B,uBAAiB,KAAK,IAAI,KAAK,KAAK,IAAI,IAAI,MAAM,CAAC;AAAA,IACrD;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,59 @@
1
+ function createSaturationModule() {
2
+ let currentMode = "off";
3
+ const STYLE_ID = "accessify-saturation";
4
+ const STORAGE_KEY = "accessify-saturation-mode";
5
+ function getStyles(mode) {
6
+ switch (mode) {
7
+ case "low":
8
+ return "html { filter: saturate(0.3) !important; }";
9
+ case "grayscale":
10
+ return "html { filter: grayscale(1) !important; }";
11
+ case "high":
12
+ return "html { filter: saturate(1.8) !important; }";
13
+ default:
14
+ return "";
15
+ }
16
+ }
17
+ function applyStyles(mode) {
18
+ let styleEl = document.getElementById(STYLE_ID);
19
+ if (mode === "off") {
20
+ styleEl?.remove();
21
+ return;
22
+ }
23
+ if (!styleEl) {
24
+ styleEl = document.createElement("style");
25
+ styleEl.id = STYLE_ID;
26
+ document.head.appendChild(styleEl);
27
+ }
28
+ styleEl.textContent = getStyles(mode);
29
+ }
30
+ function activate() {
31
+ const saved = localStorage.getItem(STORAGE_KEY);
32
+ currentMode = saved || "grayscale";
33
+ applyStyles(currentMode);
34
+ }
35
+ function deactivate() {
36
+ currentMode = "off";
37
+ applyStyles("off");
38
+ localStorage.removeItem(STORAGE_KEY);
39
+ }
40
+ return {
41
+ id: "saturation",
42
+ name: () => "Saturation",
43
+ description: "Adjust color saturation or grayscale",
44
+ icon: "saturation",
45
+ category: "visual",
46
+ activate,
47
+ deactivate,
48
+ getState: () => ({ id: "saturation", enabled: currentMode !== "off", value: currentMode }),
49
+ setState: (mode) => {
50
+ currentMode = mode;
51
+ applyStyles(mode);
52
+ localStorage.setItem(STORAGE_KEY, mode);
53
+ }
54
+ };
55
+ }
56
+ export {
57
+ createSaturationModule as default
58
+ };
59
+ //# sourceMappingURL=saturation-D8ZXpWAN.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"saturation-D8ZXpWAN.js","sources":["../src/features/saturation.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\ntype SaturationMode = 'off' | 'low' | 'grayscale' | 'high';\n\nexport default function createSaturationModule(): FeatureModule {\n let currentMode: SaturationMode = 'off';\n const STYLE_ID = 'accessify-saturation';\n const STORAGE_KEY = 'accessify-saturation-mode';\n\n function getStyles(mode: SaturationMode): string {\n switch (mode) {\n case 'low':\n return 'html { filter: saturate(0.3) !important; }';\n case 'grayscale':\n return 'html { filter: grayscale(1) !important; }';\n case 'high':\n return 'html { filter: saturate(1.8) !important; }';\n default:\n return '';\n }\n }\n\n function applyStyles(mode: SaturationMode) {\n let styleEl = document.getElementById(STYLE_ID);\n if (mode === 'off') {\n styleEl?.remove();\n return;\n }\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = STYLE_ID;\n document.head.appendChild(styleEl);\n }\n styleEl.textContent = getStyles(mode);\n }\n\n function activate() {\n const saved = localStorage.getItem(STORAGE_KEY) as SaturationMode;\n currentMode = saved || 'grayscale';\n applyStyles(currentMode);\n }\n\n function deactivate() {\n currentMode = 'off';\n applyStyles('off');\n localStorage.removeItem(STORAGE_KEY);\n }\n\n return {\n id: 'saturation',\n name: () => 'Saturation',\n description: 'Adjust color saturation or grayscale',\n icon: 'saturation',\n category: 'visual',\n activate,\n deactivate,\n getState: (): FeatureState => ({ id: 'saturation', enabled: currentMode !== 'off', value: currentMode }),\n setState: (mode: SaturationMode) => {\n currentMode = mode;\n applyStyles(mode);\n localStorage.setItem(STORAGE_KEY, mode);\n },\n };\n}\n"],"names":[],"mappings":"AAIA,SAAwB,yBAAwC;AAC9D,MAAI,cAA8B;AAClC,QAAM,WAAW;AACjB,QAAM,cAAc;AAEpB,WAAS,UAAU,MAA8B;AAC/C,YAAQ,MAAA;AAAA,MACN,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IAAA;AAAA,EAEb;AAEA,WAAS,YAAY,MAAsB;AACzC,QAAI,UAAU,SAAS,eAAe,QAAQ;AAC9C,QAAI,SAAS,OAAO;AAClB,eAAS,OAAA;AACT;AAAA,IACF;AACA,QAAI,CAAC,SAAS;AACZ,gBAAU,SAAS,cAAc,OAAO;AACxC,cAAQ,KAAK;AACb,eAAS,KAAK,YAAY,OAAO;AAAA,IACnC;AACA,YAAQ,cAAc,UAAU,IAAI;AAAA,EACtC;AAEA,WAAS,WAAW;AAClB,UAAM,QAAQ,aAAa,QAAQ,WAAW;AAC9C,kBAAc,SAAS;AACvB,gBAAY,WAAW;AAAA,EACzB;AAEA,WAAS,aAAa;AACpB,kBAAc;AACd,gBAAY,KAAK;AACjB,iBAAa,WAAW,WAAW;AAAA,EACrC;AAEA,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,MAAM,MAAM;AAAA,IACZ,aAAa;AAAA,IACb,MAAM;AAAA,IACN,UAAU;AAAA,IACV;AAAA,IACA;AAAA,IACA,UAAU,OAAqB,EAAE,IAAI,cAAc,SAAS,gBAAgB,OAAO,OAAO;IAC1F,UAAU,CAAC,SAAyB;AAClC,oBAAc;AACd,kBAAY,IAAI;AAChB,mBAAa,QAAQ,aAAa,IAAI;AAAA,IACxC;AAAA,EAAA;AAEJ;"}
@@ -0,0 +1,106 @@
1
+ const DEFAULT_VALUES = {
2
+ lineHeight: 1.5,
3
+ paragraphSpacing: 2,
4
+ letterSpacing: 0.12,
5
+ wordSpacing: 0.16
6
+ };
7
+ const MAX_VALUES = {
8
+ lineHeight: 2.5,
9
+ paragraphSpacing: 4,
10
+ letterSpacing: 0.24,
11
+ wordSpacing: 0.32
12
+ };
13
+ function createSpacingModule() {
14
+ let enabled = false;
15
+ let currentValues = { ...DEFAULT_VALUES };
16
+ const STYLE_ID = "accessify-spacing";
17
+ const STORAGE_KEY = "accessify-spacing";
18
+ function applySpacing(values) {
19
+ const root = document.documentElement;
20
+ root.style.setProperty("--accessify-line-height", String(values.lineHeight));
21
+ root.style.setProperty("--accessify-paragraph-spacing", `${values.paragraphSpacing}em`);
22
+ root.style.setProperty("--accessify-letter-spacing", `${values.letterSpacing}em`);
23
+ root.style.setProperty("--accessify-word-spacing", `${values.wordSpacing}em`);
24
+ let styleEl = document.getElementById(STYLE_ID);
25
+ if (!styleEl) {
26
+ styleEl = document.createElement("style");
27
+ styleEl.id = STYLE_ID;
28
+ document.head.appendChild(styleEl);
29
+ }
30
+ styleEl.textContent = `
31
+ /* accessify WCAG 1.4.12 text spacing adjustments */
32
+ * {
33
+ line-height: var(--accessify-line-height) !important;
34
+ letter-spacing: var(--accessify-letter-spacing) !important;
35
+ word-spacing: var(--accessify-word-spacing) !important;
36
+ }
37
+ p {
38
+ margin-bottom: var(--accessify-paragraph-spacing) !important;
39
+ }
40
+ p + p {
41
+ margin-top: var(--accessify-paragraph-spacing) !important;
42
+ }
43
+ `;
44
+ }
45
+ function removeSpacing() {
46
+ const root = document.documentElement;
47
+ root.style.removeProperty("--accessify-line-height");
48
+ root.style.removeProperty("--accessify-paragraph-spacing");
49
+ root.style.removeProperty("--accessify-letter-spacing");
50
+ root.style.removeProperty("--accessify-word-spacing");
51
+ const styleEl = document.getElementById(STYLE_ID);
52
+ styleEl?.remove();
53
+ }
54
+ function clampValues(values) {
55
+ return {
56
+ lineHeight: Math.min(MAX_VALUES.lineHeight, Math.max(DEFAULT_VALUES.lineHeight, values.lineHeight ?? currentValues.lineHeight)),
57
+ paragraphSpacing: Math.min(MAX_VALUES.paragraphSpacing, Math.max(DEFAULT_VALUES.paragraphSpacing, values.paragraphSpacing ?? currentValues.paragraphSpacing)),
58
+ letterSpacing: Math.min(MAX_VALUES.letterSpacing, Math.max(DEFAULT_VALUES.letterSpacing, values.letterSpacing ?? currentValues.letterSpacing)),
59
+ wordSpacing: Math.min(MAX_VALUES.wordSpacing, Math.max(DEFAULT_VALUES.wordSpacing, values.wordSpacing ?? currentValues.wordSpacing))
60
+ };
61
+ }
62
+ function activate() {
63
+ enabled = true;
64
+ const saved = localStorage.getItem(STORAGE_KEY);
65
+ if (saved) {
66
+ try {
67
+ currentValues = clampValues(JSON.parse(saved));
68
+ } catch {
69
+ currentValues = { ...DEFAULT_VALUES };
70
+ }
71
+ } else {
72
+ currentValues = { ...DEFAULT_VALUES };
73
+ }
74
+ applySpacing(currentValues);
75
+ }
76
+ function deactivate() {
77
+ enabled = false;
78
+ removeSpacing();
79
+ localStorage.removeItem(STORAGE_KEY);
80
+ currentValues = { ...DEFAULT_VALUES };
81
+ }
82
+ return {
83
+ id: "spacing",
84
+ name: () => "Text Spacing",
85
+ description: "Adjust line height, letter spacing, word spacing and paragraph spacing (WCAG 1.4.12)",
86
+ icon: "spacing",
87
+ category: "visual",
88
+ activate,
89
+ deactivate,
90
+ getState: () => ({
91
+ id: "spacing",
92
+ enabled,
93
+ value: { ...currentValues }
94
+ }),
95
+ setState: (values) => {
96
+ currentValues = clampValues(values);
97
+ applySpacing(currentValues);
98
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(currentValues));
99
+ enabled = true;
100
+ }
101
+ };
102
+ }
103
+ export {
104
+ createSpacingModule as default
105
+ };
106
+ //# sourceMappingURL=spacing-DENai3JU.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spacing-DENai3JU.js","sources":["../src/features/spacing.ts"],"sourcesContent":["import type { FeatureModule, FeatureState } from '../types';\n\ninterface SpacingValues {\n lineHeight: number;\n paragraphSpacing: number;\n letterSpacing: number;\n wordSpacing: number;\n}\n\nconst DEFAULT_VALUES: SpacingValues = {\n lineHeight: 1.5,\n paragraphSpacing: 2,\n letterSpacing: 0.12,\n wordSpacing: 0.16,\n};\n\nconst MAX_VALUES: SpacingValues = {\n lineHeight: 2.5,\n paragraphSpacing: 4,\n letterSpacing: 0.24,\n wordSpacing: 0.32,\n};\n\nexport default function createSpacingModule(): FeatureModule {\n let enabled = false;\n let currentValues: SpacingValues = { ...DEFAULT_VALUES };\n const STYLE_ID = 'accessify-spacing';\n const STORAGE_KEY = 'accessify-spacing';\n\n function applySpacing(values: SpacingValues) {\n const root = document.documentElement;\n root.style.setProperty('--accessify-line-height', String(values.lineHeight));\n root.style.setProperty('--accessify-paragraph-spacing', `${values.paragraphSpacing}em`);\n root.style.setProperty('--accessify-letter-spacing', `${values.letterSpacing}em`);\n root.style.setProperty('--accessify-word-spacing', `${values.wordSpacing}em`);\n\n let styleEl = document.getElementById(STYLE_ID);\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = STYLE_ID;\n document.head.appendChild(styleEl);\n }\n styleEl.textContent = `\n /* accessify WCAG 1.4.12 text spacing adjustments */\n * {\n line-height: var(--accessify-line-height) !important;\n letter-spacing: var(--accessify-letter-spacing) !important;\n word-spacing: var(--accessify-word-spacing) !important;\n }\n p {\n margin-bottom: var(--accessify-paragraph-spacing) !important;\n }\n p + p {\n margin-top: var(--accessify-paragraph-spacing) !important;\n }\n `;\n }\n\n function removeSpacing() {\n const root = document.documentElement;\n root.style.removeProperty('--accessify-line-height');\n root.style.removeProperty('--accessify-paragraph-spacing');\n root.style.removeProperty('--accessify-letter-spacing');\n root.style.removeProperty('--accessify-word-spacing');\n const styleEl = document.getElementById(STYLE_ID);\n styleEl?.remove();\n }\n\n function clampValues(values: Partial<SpacingValues>): SpacingValues {\n return {\n lineHeight: Math.min(MAX_VALUES.lineHeight, Math.max(DEFAULT_VALUES.lineHeight, values.lineHeight ?? currentValues.lineHeight)),\n paragraphSpacing: Math.min(MAX_VALUES.paragraphSpacing, Math.max(DEFAULT_VALUES.paragraphSpacing, values.paragraphSpacing ?? currentValues.paragraphSpacing)),\n letterSpacing: Math.min(MAX_VALUES.letterSpacing, Math.max(DEFAULT_VALUES.letterSpacing, values.letterSpacing ?? currentValues.letterSpacing)),\n wordSpacing: Math.min(MAX_VALUES.wordSpacing, Math.max(DEFAULT_VALUES.wordSpacing, values.wordSpacing ?? currentValues.wordSpacing)),\n };\n }\n\n function activate() {\n enabled = true;\n const saved = localStorage.getItem(STORAGE_KEY);\n if (saved) {\n try {\n currentValues = clampValues(JSON.parse(saved));\n } catch {\n currentValues = { ...DEFAULT_VALUES };\n }\n } else {\n currentValues = { ...DEFAULT_VALUES };\n }\n applySpacing(currentValues);\n }\n\n function deactivate() {\n enabled = false;\n removeSpacing();\n localStorage.removeItem(STORAGE_KEY);\n currentValues = { ...DEFAULT_VALUES };\n }\n\n return {\n id: 'spacing',\n name: () => 'Text Spacing',\n description: 'Adjust line height, letter spacing, word spacing and paragraph spacing (WCAG 1.4.12)',\n icon: 'spacing',\n category: 'visual',\n activate,\n deactivate,\n getState: (): FeatureState => ({\n id: 'spacing',\n enabled,\n value: { ...currentValues },\n }),\n setState: (values: Partial<SpacingValues>) => {\n currentValues = clampValues(values);\n applySpacing(currentValues);\n localStorage.setItem(STORAGE_KEY, JSON.stringify(currentValues));\n enabled = true;\n },\n };\n}\n"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ function createTextAlignModule() {
2
+ let currentMode = "off";
3
+ const STYLE_ID = "accessify-text-align";
4
+ const STORAGE_KEY = "accessify-text-align-mode";
5
+ function applyStyles(mode) {
6
+ let styleEl = document.getElementById(STYLE_ID);
7
+ if (mode === "off") {
8
+ styleEl?.remove();
9
+ return;
10
+ }
11
+ if (!styleEl) {
12
+ styleEl = document.createElement("style");
13
+ styleEl.id = STYLE_ID;
14
+ document.head.appendChild(styleEl);
15
+ }
16
+ styleEl.textContent = `
17
+ body, body * {
18
+ text-align: ${mode} !important;
19
+ }
20
+ `;
21
+ }
22
+ function activate() {
23
+ const saved = localStorage.getItem(STORAGE_KEY);
24
+ currentMode = saved || "left";
25
+ applyStyles(currentMode);
26
+ }
27
+ function deactivate() {
28
+ currentMode = "off";
29
+ applyStyles("off");
30
+ localStorage.removeItem(STORAGE_KEY);
31
+ }
32
+ return {
33
+ id: "text-align",
34
+ name: () => "Text Align",
35
+ description: "Change text alignment across the page",
36
+ icon: "text-align",
37
+ category: "visual",
38
+ activate,
39
+ deactivate,
40
+ getState: () => ({ id: "text-align", enabled: currentMode !== "off", value: currentMode }),
41
+ setState: (mode) => {
42
+ currentMode = mode;
43
+ applyStyles(mode);
44
+ localStorage.setItem(STORAGE_KEY, mode);
45
+ }
46
+ };
47
+ }
48
+ export {
49
+ createTextAlignModule as default
50
+ };
51
+ //# sourceMappingURL=text-align-BDRPqPvl.js.map