@contractspec/lib.accessibility 3.7.16 → 3.7.17
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/AccessibilityPanel.js +1 -362
- package/dist/AccessibilityProvider.js +1 -173
- package/dist/browser/AccessibilityPanel.js +1 -362
- package/dist/browser/AccessibilityProvider.js +1 -173
- package/dist/browser/index.css +1 -60
- package/dist/browser/index.js +1 -14
- package/dist/browser/next-route-announcer.js +1 -23
- package/dist/browser/preferences.js +1 -132
- package/dist/index.css +1 -60
- package/dist/index.js +1 -14
- package/dist/next-route-announcer.js +1 -23
- package/dist/preferences.js +1 -132
- package/package.json +8 -8
|
@@ -1,363 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
5
|
-
"use client";
|
|
6
|
-
var DEFAULT_PREFERENCES = {
|
|
7
|
-
textSize: "m",
|
|
8
|
-
textSpacing: "normal",
|
|
9
|
-
lineHeight: "normal",
|
|
10
|
-
underlineLinks: false,
|
|
11
|
-
focusRing: "normal",
|
|
12
|
-
reduceMotion: "system",
|
|
13
|
-
highContrast: false,
|
|
14
|
-
dyslexiaFont: false
|
|
15
|
-
};
|
|
16
|
-
var STORAGE_KEY = "a11y:preferences:v1";
|
|
17
|
-
function getStoredPreferences() {
|
|
18
|
-
try {
|
|
19
|
-
const raw = typeof window !== "undefined" ? window.localStorage.getItem(STORAGE_KEY) : null;
|
|
20
|
-
if (!raw)
|
|
21
|
-
return null;
|
|
22
|
-
const parsed = JSON.parse(raw);
|
|
23
|
-
return { ...DEFAULT_PREFERENCES, ...parsed };
|
|
24
|
-
} catch {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function savePreferences(prefs) {
|
|
29
|
-
try {
|
|
30
|
-
if (typeof window !== "undefined") {
|
|
31
|
-
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));
|
|
32
|
-
}
|
|
33
|
-
} catch {}
|
|
34
|
-
}
|
|
35
|
-
function applyCssVariables(prefs) {
|
|
36
|
-
if (typeof document === "undefined")
|
|
37
|
-
return;
|
|
38
|
-
const root = document.documentElement;
|
|
39
|
-
const body = document.body;
|
|
40
|
-
const sizeMap = {
|
|
41
|
-
s: "0.95",
|
|
42
|
-
m: "1",
|
|
43
|
-
l: "1.1",
|
|
44
|
-
xl: "1.25"
|
|
45
|
-
};
|
|
46
|
-
root.style.setProperty("--a11y-text-scale", sizeMap[prefs.textSize]);
|
|
47
|
-
const targetMin = prefs.textSize === "l" || prefs.textSize === "xl" ? "44px" : "0px";
|
|
48
|
-
root.style.setProperty("--a11y-target-min", targetMin);
|
|
49
|
-
root.style.setProperty("--a11y-letter-spacing", prefs.textSpacing === "increased" ? "0.12em" : "normal");
|
|
50
|
-
root.style.setProperty("--a11y-word-spacing", prefs.textSpacing === "increased" ? "0.16em" : "normal");
|
|
51
|
-
root.style.setProperty("--a11y-line-height", prefs.lineHeight === "increased" ? "1.6" : "1.5");
|
|
52
|
-
root.style.setProperty("--a11y-underline-links", prefs.underlineLinks ? "underline" : "revert");
|
|
53
|
-
root.style.setProperty("--a11y-focus-ring-width", prefs.focusRing === "thick" ? "4px" : "2px");
|
|
54
|
-
root.style.setProperty("--a11y-reduce-motion", prefs.reduceMotion);
|
|
55
|
-
root.style.setProperty("--a11y-contrast-mode", prefs.highContrast ? "high" : "normal");
|
|
56
|
-
if (prefs.highContrast) {
|
|
57
|
-
root.setAttribute("data-contrast", "high");
|
|
58
|
-
} else {
|
|
59
|
-
root.removeAttribute("data-contrast");
|
|
60
|
-
}
|
|
61
|
-
root.style.setProperty("--a11y-font-family-alt-enabled", prefs.dyslexiaFont ? "1" : "0");
|
|
62
|
-
if (body) {
|
|
63
|
-
if (prefs.textSpacing === "increased") {
|
|
64
|
-
body.style.letterSpacing = "0.12em";
|
|
65
|
-
body.style.wordSpacing = "0.16em";
|
|
66
|
-
} else {
|
|
67
|
-
body.style.letterSpacing = "";
|
|
68
|
-
body.style.wordSpacing = "";
|
|
69
|
-
}
|
|
70
|
-
body.style.lineHeight = prefs.lineHeight === "increased" ? "1.6" : "";
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
var PreferencesContext = React.createContext(null);
|
|
74
|
-
function useA11YPreferences() {
|
|
75
|
-
const ctx = React.useContext(PreferencesContext);
|
|
76
|
-
if (!ctx)
|
|
77
|
-
throw new Error("useA11YPreferences must be used within A11YPreferencesProvider");
|
|
78
|
-
return ctx;
|
|
79
|
-
}
|
|
80
|
-
function A11YPreferencesProvider({
|
|
81
|
-
children
|
|
82
|
-
}) {
|
|
83
|
-
const [preferences, setPreferencesState] = React.useState(DEFAULT_PREFERENCES);
|
|
84
|
-
const [hydrated, setHydrated] = React.useState(false);
|
|
85
|
-
React.useEffect(() => {
|
|
86
|
-
const stored = getStoredPreferences();
|
|
87
|
-
const next = stored ?? DEFAULT_PREFERENCES;
|
|
88
|
-
setPreferencesState(next);
|
|
89
|
-
setHydrated(true);
|
|
90
|
-
applyCssVariables(next);
|
|
91
|
-
}, []);
|
|
92
|
-
const setPreferences = React.useCallback((updater) => {
|
|
93
|
-
setPreferencesState((prev) => {
|
|
94
|
-
const next = typeof updater === "function" ? updater(prev) : { ...prev, ...updater };
|
|
95
|
-
savePreferences(next);
|
|
96
|
-
applyCssVariables(next);
|
|
97
|
-
try {
|
|
98
|
-
if (typeof window !== "undefined") {
|
|
99
|
-
const changed = {};
|
|
100
|
-
for (const key of Object.keys(next)) {
|
|
101
|
-
if (next[key] !== prev[key]) {
|
|
102
|
-
changed[key] = next[key];
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
window.dispatchEvent(new CustomEvent("a11y:pref_changed", {
|
|
106
|
-
detail: {
|
|
107
|
-
previous: prev,
|
|
108
|
-
current: next,
|
|
109
|
-
changed
|
|
110
|
-
}
|
|
111
|
-
}));
|
|
112
|
-
}
|
|
113
|
-
} catch {}
|
|
114
|
-
return next;
|
|
115
|
-
});
|
|
116
|
-
}, []);
|
|
117
|
-
const value = React.useMemo(() => ({ preferences, setPreferences }), [preferences, setPreferences]);
|
|
118
|
-
return /* @__PURE__ */ jsxDEV(PreferencesContext, {
|
|
119
|
-
value,
|
|
120
|
-
children: [
|
|
121
|
-
children,
|
|
122
|
-
!hydrated ? null : null
|
|
123
|
-
]
|
|
124
|
-
}, undefined, true, undefined, this);
|
|
125
|
-
}
|
|
126
|
-
function a11yRootClassName() {
|
|
127
|
-
return "a11y-root";
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// src/AccessibilityPanel.tsx
|
|
131
|
-
import { Button } from "@contractspec/lib.design-system";
|
|
132
|
-
import {
|
|
133
|
-
Dialog,
|
|
134
|
-
DialogClose,
|
|
135
|
-
DialogContent,
|
|
136
|
-
DialogOverlay,
|
|
137
|
-
DialogPortal,
|
|
138
|
-
DialogTitle,
|
|
139
|
-
DialogTrigger
|
|
140
|
-
} from "@contractspec/lib.ui-kit-web/ui/dialog";
|
|
141
|
-
import { Label } from "@contractspec/lib.ui-kit-web/ui/label";
|
|
142
|
-
import {
|
|
143
|
-
Select,
|
|
144
|
-
SelectContent,
|
|
145
|
-
SelectItem,
|
|
146
|
-
SelectTrigger,
|
|
147
|
-
SelectValue
|
|
148
|
-
} from "@contractspec/lib.ui-kit-web/ui/select";
|
|
149
|
-
import { Switch } from "@contractspec/lib.ui-kit-web/ui/switch";
|
|
150
|
-
import { cn } from "@contractspec/lib.ui-kit-web/ui/utils";
|
|
151
|
-
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
152
|
-
"use client";
|
|
153
|
-
function AccessibilityPanel({ className }) {
|
|
154
|
-
const { preferences, setPreferences } = useA11YPreferences();
|
|
155
|
-
return /* @__PURE__ */ jsxDEV2(Dialog, {
|
|
156
|
-
children: [
|
|
157
|
-
/* @__PURE__ */ jsxDEV2(DialogTrigger, {
|
|
158
|
-
asChild: true,
|
|
159
|
-
children: /* @__PURE__ */ jsxDEV2(Button, {
|
|
160
|
-
variant: "outline",
|
|
161
|
-
"aria-haspopup": "dialog",
|
|
162
|
-
children: "Accessibility"
|
|
163
|
-
}, undefined, false, undefined, this)
|
|
164
|
-
}, undefined, false, undefined, this),
|
|
165
|
-
/* @__PURE__ */ jsxDEV2(DialogPortal, {
|
|
166
|
-
children: [
|
|
167
|
-
/* @__PURE__ */ jsxDEV2(DialogOverlay, {
|
|
168
|
-
className: "fixed inset-0 z-50 bg-black/40"
|
|
169
|
-
}, undefined, false, undefined, this),
|
|
170
|
-
/* @__PURE__ */ jsxDEV2(DialogContent, {
|
|
171
|
-
className: cn("max-w-md bg-background p-6 shadow-lg outline-hidden focus-visible:ring-2 focus-visible:ring-ring", className),
|
|
172
|
-
"aria-describedby": "a11y-panel-desc",
|
|
173
|
-
children: [
|
|
174
|
-
/* @__PURE__ */ jsxDEV2(DialogTitle, {
|
|
175
|
-
className: "font-semibold text-lg",
|
|
176
|
-
children: "Accessibility settings"
|
|
177
|
-
}, undefined, false, undefined, this),
|
|
178
|
-
/* @__PURE__ */ jsxDEV2("p", {
|
|
179
|
-
id: "a11y-panel-desc",
|
|
180
|
-
className: "text-muted-foreground text-sm",
|
|
181
|
-
children: "Adjust text size, spacing, focus and motion to suit your needs."
|
|
182
|
-
}, undefined, false, undefined, this),
|
|
183
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
184
|
-
className: "mt-6 space-y-6",
|
|
185
|
-
children: [
|
|
186
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
187
|
-
className: "space-y-2",
|
|
188
|
-
children: [
|
|
189
|
-
/* @__PURE__ */ jsxDEV2(Label, {
|
|
190
|
-
children: "Text size"
|
|
191
|
-
}, undefined, false, undefined, this),
|
|
192
|
-
/* @__PURE__ */ jsxDEV2(Select, {
|
|
193
|
-
value: preferences.textSize,
|
|
194
|
-
onValueChange: (v) => setPreferences({ textSize: v }),
|
|
195
|
-
children: [
|
|
196
|
-
/* @__PURE__ */ jsxDEV2(SelectTrigger, {
|
|
197
|
-
"aria-label": "Text size",
|
|
198
|
-
children: /* @__PURE__ */ jsxDEV2(SelectValue, {}, undefined, false, undefined, this)
|
|
199
|
-
}, undefined, false, undefined, this),
|
|
200
|
-
/* @__PURE__ */ jsxDEV2(SelectContent, {
|
|
201
|
-
children: [
|
|
202
|
-
/* @__PURE__ */ jsxDEV2(SelectItem, {
|
|
203
|
-
value: "s",
|
|
204
|
-
children: "Small"
|
|
205
|
-
}, undefined, false, undefined, this),
|
|
206
|
-
/* @__PURE__ */ jsxDEV2(SelectItem, {
|
|
207
|
-
value: "m",
|
|
208
|
-
children: "Medium"
|
|
209
|
-
}, undefined, false, undefined, this),
|
|
210
|
-
/* @__PURE__ */ jsxDEV2(SelectItem, {
|
|
211
|
-
value: "l",
|
|
212
|
-
children: "Large"
|
|
213
|
-
}, undefined, false, undefined, this),
|
|
214
|
-
/* @__PURE__ */ jsxDEV2(SelectItem, {
|
|
215
|
-
value: "xl",
|
|
216
|
-
children: "Extra Large"
|
|
217
|
-
}, undefined, false, undefined, this)
|
|
218
|
-
]
|
|
219
|
-
}, undefined, true, undefined, this)
|
|
220
|
-
]
|
|
221
|
-
}, undefined, true, undefined, this)
|
|
222
|
-
]
|
|
223
|
-
}, undefined, true, undefined, this),
|
|
224
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
225
|
-
className: "flex items-center justify-between",
|
|
226
|
-
children: [
|
|
227
|
-
/* @__PURE__ */ jsxDEV2(Label, {
|
|
228
|
-
htmlFor: "text-spacing",
|
|
229
|
-
children: "Increase text spacing (WCAG 1.4.12)"
|
|
230
|
-
}, undefined, false, undefined, this),
|
|
231
|
-
/* @__PURE__ */ jsxDEV2(Switch, {
|
|
232
|
-
id: "text-spacing",
|
|
233
|
-
checked: preferences.textSpacing === "increased",
|
|
234
|
-
onCheckedChange: (c) => setPreferences({ textSpacing: c ? "increased" : "normal" })
|
|
235
|
-
}, undefined, false, undefined, this)
|
|
236
|
-
]
|
|
237
|
-
}, undefined, true, undefined, this),
|
|
238
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
239
|
-
className: "flex items-center justify-between",
|
|
240
|
-
children: [
|
|
241
|
-
/* @__PURE__ */ jsxDEV2(Label, {
|
|
242
|
-
htmlFor: "line-height",
|
|
243
|
-
children: "Increase line height"
|
|
244
|
-
}, undefined, false, undefined, this),
|
|
245
|
-
/* @__PURE__ */ jsxDEV2(Switch, {
|
|
246
|
-
id: "line-height",
|
|
247
|
-
checked: preferences.lineHeight === "increased",
|
|
248
|
-
onCheckedChange: (c) => setPreferences({ lineHeight: c ? "increased" : "normal" })
|
|
249
|
-
}, undefined, false, undefined, this)
|
|
250
|
-
]
|
|
251
|
-
}, undefined, true, undefined, this),
|
|
252
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
253
|
-
className: "flex items-center justify-between",
|
|
254
|
-
children: [
|
|
255
|
-
/* @__PURE__ */ jsxDEV2(Label, {
|
|
256
|
-
htmlFor: "underline-links",
|
|
257
|
-
children: "Always underline links"
|
|
258
|
-
}, undefined, false, undefined, this),
|
|
259
|
-
/* @__PURE__ */ jsxDEV2(Switch, {
|
|
260
|
-
id: "underline-links",
|
|
261
|
-
checked: preferences.underlineLinks,
|
|
262
|
-
onCheckedChange: (c) => setPreferences({ underlineLinks: c })
|
|
263
|
-
}, undefined, false, undefined, this)
|
|
264
|
-
]
|
|
265
|
-
}, undefined, true, undefined, this),
|
|
266
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
267
|
-
className: "flex items-center justify-between",
|
|
268
|
-
children: [
|
|
269
|
-
/* @__PURE__ */ jsxDEV2(Label, {
|
|
270
|
-
htmlFor: "focus-ring",
|
|
271
|
-
children: "Thick focus ring"
|
|
272
|
-
}, undefined, false, undefined, this),
|
|
273
|
-
/* @__PURE__ */ jsxDEV2(Switch, {
|
|
274
|
-
id: "focus-ring",
|
|
275
|
-
checked: preferences.focusRing === "thick",
|
|
276
|
-
onCheckedChange: (c) => setPreferences({ focusRing: c ? "thick" : "normal" })
|
|
277
|
-
}, undefined, false, undefined, this)
|
|
278
|
-
]
|
|
279
|
-
}, undefined, true, undefined, this),
|
|
280
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
281
|
-
className: "space-y-2",
|
|
282
|
-
children: [
|
|
283
|
-
/* @__PURE__ */ jsxDEV2(Label, {
|
|
284
|
-
children: "Motion"
|
|
285
|
-
}, undefined, false, undefined, this),
|
|
286
|
-
/* @__PURE__ */ jsxDEV2(Select, {
|
|
287
|
-
value: preferences.reduceMotion,
|
|
288
|
-
onValueChange: (v) => setPreferences({ reduceMotion: v }),
|
|
289
|
-
children: [
|
|
290
|
-
/* @__PURE__ */ jsxDEV2(SelectTrigger, {
|
|
291
|
-
"aria-label": "Motion preferences",
|
|
292
|
-
children: /* @__PURE__ */ jsxDEV2(SelectValue, {}, undefined, false, undefined, this)
|
|
293
|
-
}, undefined, false, undefined, this),
|
|
294
|
-
/* @__PURE__ */ jsxDEV2(SelectContent, {
|
|
295
|
-
children: [
|
|
296
|
-
/* @__PURE__ */ jsxDEV2(SelectItem, {
|
|
297
|
-
value: "system",
|
|
298
|
-
children: "Follow system"
|
|
299
|
-
}, undefined, false, undefined, this),
|
|
300
|
-
/* @__PURE__ */ jsxDEV2(SelectItem, {
|
|
301
|
-
value: "reduce",
|
|
302
|
-
children: "Reduce motion"
|
|
303
|
-
}, undefined, false, undefined, this),
|
|
304
|
-
/* @__PURE__ */ jsxDEV2(SelectItem, {
|
|
305
|
-
value: "no-preference",
|
|
306
|
-
children: "Allow motion"
|
|
307
|
-
}, undefined, false, undefined, this)
|
|
308
|
-
]
|
|
309
|
-
}, undefined, true, undefined, this)
|
|
310
|
-
]
|
|
311
|
-
}, undefined, true, undefined, this)
|
|
312
|
-
]
|
|
313
|
-
}, undefined, true, undefined, this),
|
|
314
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
315
|
-
className: "flex items-center justify-between",
|
|
316
|
-
children: [
|
|
317
|
-
/* @__PURE__ */ jsxDEV2(Label, {
|
|
318
|
-
htmlFor: "contrast",
|
|
319
|
-
children: "High contrast"
|
|
320
|
-
}, undefined, false, undefined, this),
|
|
321
|
-
/* @__PURE__ */ jsxDEV2(Switch, {
|
|
322
|
-
id: "contrast",
|
|
323
|
-
checked: preferences.highContrast,
|
|
324
|
-
onCheckedChange: (c) => setPreferences({ highContrast: c })
|
|
325
|
-
}, undefined, false, undefined, this)
|
|
326
|
-
]
|
|
327
|
-
}, undefined, true, undefined, this),
|
|
328
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
329
|
-
className: "flex items-center justify-between",
|
|
330
|
-
children: [
|
|
331
|
-
/* @__PURE__ */ jsxDEV2(Label, {
|
|
332
|
-
htmlFor: "dyslexia-font",
|
|
333
|
-
children: "Dyslexia-friendly font"
|
|
334
|
-
}, undefined, false, undefined, this),
|
|
335
|
-
/* @__PURE__ */ jsxDEV2(Switch, {
|
|
336
|
-
id: "dyslexia-font",
|
|
337
|
-
checked: preferences.dyslexiaFont,
|
|
338
|
-
onCheckedChange: (c) => setPreferences({ dyslexiaFont: c })
|
|
339
|
-
}, undefined, false, undefined, this)
|
|
340
|
-
]
|
|
341
|
-
}, undefined, true, undefined, this)
|
|
342
|
-
]
|
|
343
|
-
}, undefined, true, undefined, this),
|
|
344
|
-
/* @__PURE__ */ jsxDEV2("div", {
|
|
345
|
-
className: "mt-8 flex justify-end gap-2",
|
|
346
|
-
children: /* @__PURE__ */ jsxDEV2(DialogClose, {
|
|
347
|
-
asChild: true,
|
|
348
|
-
children: /* @__PURE__ */ jsxDEV2(Button, {
|
|
349
|
-
variant: "secondary",
|
|
350
|
-
children: "Close"
|
|
351
|
-
}, undefined, false, undefined, this)
|
|
352
|
-
}, undefined, false, undefined, this)
|
|
353
|
-
}, undefined, false, undefined, this)
|
|
354
|
-
]
|
|
355
|
-
}, undefined, true, undefined, this)
|
|
356
|
-
]
|
|
357
|
-
}, undefined, true, undefined, this)
|
|
358
|
-
]
|
|
359
|
-
}, undefined, true, undefined, this);
|
|
360
|
-
}
|
|
361
|
-
export {
|
|
362
|
-
AccessibilityPanel
|
|
363
|
-
};
|
|
2
|
+
import G from"react";import{jsxs as v}from"react/jsx-runtime";var Y={textSize:"m",textSpacing:"normal",lineHeight:"normal",underlineLinks:!1,focusRing:"normal",reduceMotion:"system",highContrast:!1,dyslexiaFont:!1},A="a11y:preferences:v1";function h(){try{let J=typeof window<"u"?window.localStorage.getItem(A):null;if(!J)return null;let Q=JSON.parse(J);return{...Y,...Q}}catch{return null}}function m(J){try{if(typeof window<"u")window.localStorage.setItem(A,JSON.stringify(J))}catch{}}function k(J){if(typeof document>"u")return;let{documentElement:Q,body:X}=document,W={s:"0.95",m:"1",l:"1.1",xl:"1.25"};Q.style.setProperty("--a11y-text-scale",W[J.textSize]);let U=J.textSize==="l"||J.textSize==="xl"?"44px":"0px";if(Q.style.setProperty("--a11y-target-min",U),Q.style.setProperty("--a11y-letter-spacing",J.textSpacing==="increased"?"0.12em":"normal"),Q.style.setProperty("--a11y-word-spacing",J.textSpacing==="increased"?"0.16em":"normal"),Q.style.setProperty("--a11y-line-height",J.lineHeight==="increased"?"1.6":"1.5"),Q.style.setProperty("--a11y-underline-links",J.underlineLinks?"underline":"revert"),Q.style.setProperty("--a11y-focus-ring-width",J.focusRing==="thick"?"4px":"2px"),Q.style.setProperty("--a11y-reduce-motion",J.reduceMotion),Q.style.setProperty("--a11y-contrast-mode",J.highContrast?"high":"normal"),J.highContrast)Q.setAttribute("data-contrast","high");else Q.removeAttribute("data-contrast");if(Q.style.setProperty("--a11y-font-family-alt-enabled",J.dyslexiaFont?"1":"0"),X){if(J.textSpacing==="increased")X.style.letterSpacing="0.12em",X.style.wordSpacing="0.16em";else X.style.letterSpacing="",X.style.wordSpacing="";X.style.lineHeight=J.lineHeight==="increased"?"1.6":""}}var F=G.createContext(null);function N(){let J=G.useContext(F);if(!J)throw Error("useA11YPreferences must be used within A11YPreferencesProvider");return J}function i({children:J}){let[Q,X]=G.useState(Y),[W,U]=G.useState(!1);G.useEffect(()=>{let z=h()??Y;X(z),U(!0),k(z)},[]);let V=G.useCallback((M)=>{X((z)=>{let B=typeof M==="function"?M(z):{...z,...M};m(B),k(B);try{if(typeof window<"u"){let _={};for(let O of Object.keys(B))if(B[O]!==z[O])_[O]=B[O];window.dispatchEvent(new CustomEvent("a11y:pref_changed",{detail:{previous:z,current:B,changed:_}}))}}catch{}return B})},[]),L=G.useMemo(()=>({preferences:Q,setPreferences:V}),[Q,V]);return v(F,{value:L,children:[J,!W?null:null]})}function l(){return"a11y-root"}import{Button as I}from"@contractspec/lib.design-system";import{Dialog as y,DialogClose as R,DialogContent as g,DialogOverlay as b,DialogPortal as u,DialogTitle as E,DialogTrigger as P}from"@contractspec/lib.ui-kit-web/ui/dialog";import{Label as $}from"@contractspec/lib.ui-kit-web/ui/label";import{Select as w,SelectContent as T,SelectItem as H,SelectTrigger as D,SelectValue as C}from"@contractspec/lib.ui-kit-web/ui/select";import{Switch as K}from"@contractspec/lib.ui-kit-web/ui/switch";import{cn as d}from"@contractspec/lib.ui-kit-web/ui/utils";import{jsx as q,jsxs as Z}from"react/jsx-runtime";function r({className:J}){let{preferences:Q,setPreferences:X}=N();return Z(y,{children:[q(P,{asChild:!0,children:q(I,{variant:"outline","aria-haspopup":"dialog",children:"Accessibility"})}),Z(u,{children:[q(b,{className:"fixed inset-0 z-50 bg-black/40"}),Z(g,{className:d("max-w-md bg-background p-6 shadow-lg outline-hidden focus-visible:ring-2 focus-visible:ring-ring",J),"aria-describedby":"a11y-panel-desc",children:[q(E,{className:"font-semibold text-lg",children:"Accessibility settings"}),q("p",{id:"a11y-panel-desc",className:"text-muted-foreground text-sm",children:"Adjust text size, spacing, focus and motion to suit your needs."}),Z("div",{className:"mt-6 space-y-6",children:[Z("div",{className:"space-y-2",children:[q($,{children:"Text size"}),Z(w,{value:Q.textSize,onValueChange:(W)=>X({textSize:W}),children:[q(D,{"aria-label":"Text size",children:q(C,{})}),Z(T,{children:[q(H,{value:"s",children:"Small"}),q(H,{value:"m",children:"Medium"}),q(H,{value:"l",children:"Large"}),q(H,{value:"xl",children:"Extra Large"})]})]})]}),Z("div",{className:"flex items-center justify-between",children:[q($,{htmlFor:"text-spacing",children:"Increase text spacing (WCAG 1.4.12)"}),q(K,{id:"text-spacing",checked:Q.textSpacing==="increased",onCheckedChange:(W)=>X({textSpacing:W?"increased":"normal"})})]}),Z("div",{className:"flex items-center justify-between",children:[q($,{htmlFor:"line-height",children:"Increase line height"}),q(K,{id:"line-height",checked:Q.lineHeight==="increased",onCheckedChange:(W)=>X({lineHeight:W?"increased":"normal"})})]}),Z("div",{className:"flex items-center justify-between",children:[q($,{htmlFor:"underline-links",children:"Always underline links"}),q(K,{id:"underline-links",checked:Q.underlineLinks,onCheckedChange:(W)=>X({underlineLinks:W})})]}),Z("div",{className:"flex items-center justify-between",children:[q($,{htmlFor:"focus-ring",children:"Thick focus ring"}),q(K,{id:"focus-ring",checked:Q.focusRing==="thick",onCheckedChange:(W)=>X({focusRing:W?"thick":"normal"})})]}),Z("div",{className:"space-y-2",children:[q($,{children:"Motion"}),Z(w,{value:Q.reduceMotion,onValueChange:(W)=>X({reduceMotion:W}),children:[q(D,{"aria-label":"Motion preferences",children:q(C,{})}),Z(T,{children:[q(H,{value:"system",children:"Follow system"}),q(H,{value:"reduce",children:"Reduce motion"}),q(H,{value:"no-preference",children:"Allow motion"})]})]})]}),Z("div",{className:"flex items-center justify-between",children:[q($,{htmlFor:"contrast",children:"High contrast"}),q(K,{id:"contrast",checked:Q.highContrast,onCheckedChange:(W)=>X({highContrast:W})})]}),Z("div",{className:"flex items-center justify-between",children:[q($,{htmlFor:"dyslexia-font",children:"Dyslexia-friendly font"}),q(K,{id:"dyslexia-font",checked:Q.dyslexiaFont,onCheckedChange:(W)=>X({dyslexiaFont:W})})]})]}),q("div",{className:"mt-8 flex justify-end gap-2",children:q(R,{asChild:!0,children:q(I,{variant:"secondary",children:"Close"})})})]})]})]})}export{r as AccessibilityPanel};
|
|
@@ -1,174 +1,2 @@
|
|
|
1
1
|
// @bun
|
|
2
|
-
|
|
3
|
-
import React from "react";
|
|
4
|
-
import { jsxDEV } from "react/jsx-dev-runtime";
|
|
5
|
-
"use client";
|
|
6
|
-
var DEFAULT_PREFERENCES = {
|
|
7
|
-
textSize: "m",
|
|
8
|
-
textSpacing: "normal",
|
|
9
|
-
lineHeight: "normal",
|
|
10
|
-
underlineLinks: false,
|
|
11
|
-
focusRing: "normal",
|
|
12
|
-
reduceMotion: "system",
|
|
13
|
-
highContrast: false,
|
|
14
|
-
dyslexiaFont: false
|
|
15
|
-
};
|
|
16
|
-
var STORAGE_KEY = "a11y:preferences:v1";
|
|
17
|
-
function getStoredPreferences() {
|
|
18
|
-
try {
|
|
19
|
-
const raw = typeof window !== "undefined" ? window.localStorage.getItem(STORAGE_KEY) : null;
|
|
20
|
-
if (!raw)
|
|
21
|
-
return null;
|
|
22
|
-
const parsed = JSON.parse(raw);
|
|
23
|
-
return { ...DEFAULT_PREFERENCES, ...parsed };
|
|
24
|
-
} catch {
|
|
25
|
-
return null;
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
function savePreferences(prefs) {
|
|
29
|
-
try {
|
|
30
|
-
if (typeof window !== "undefined") {
|
|
31
|
-
window.localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));
|
|
32
|
-
}
|
|
33
|
-
} catch {}
|
|
34
|
-
}
|
|
35
|
-
function applyCssVariables(prefs) {
|
|
36
|
-
if (typeof document === "undefined")
|
|
37
|
-
return;
|
|
38
|
-
const root = document.documentElement;
|
|
39
|
-
const body = document.body;
|
|
40
|
-
const sizeMap = {
|
|
41
|
-
s: "0.95",
|
|
42
|
-
m: "1",
|
|
43
|
-
l: "1.1",
|
|
44
|
-
xl: "1.25"
|
|
45
|
-
};
|
|
46
|
-
root.style.setProperty("--a11y-text-scale", sizeMap[prefs.textSize]);
|
|
47
|
-
const targetMin = prefs.textSize === "l" || prefs.textSize === "xl" ? "44px" : "0px";
|
|
48
|
-
root.style.setProperty("--a11y-target-min", targetMin);
|
|
49
|
-
root.style.setProperty("--a11y-letter-spacing", prefs.textSpacing === "increased" ? "0.12em" : "normal");
|
|
50
|
-
root.style.setProperty("--a11y-word-spacing", prefs.textSpacing === "increased" ? "0.16em" : "normal");
|
|
51
|
-
root.style.setProperty("--a11y-line-height", prefs.lineHeight === "increased" ? "1.6" : "1.5");
|
|
52
|
-
root.style.setProperty("--a11y-underline-links", prefs.underlineLinks ? "underline" : "revert");
|
|
53
|
-
root.style.setProperty("--a11y-focus-ring-width", prefs.focusRing === "thick" ? "4px" : "2px");
|
|
54
|
-
root.style.setProperty("--a11y-reduce-motion", prefs.reduceMotion);
|
|
55
|
-
root.style.setProperty("--a11y-contrast-mode", prefs.highContrast ? "high" : "normal");
|
|
56
|
-
if (prefs.highContrast) {
|
|
57
|
-
root.setAttribute("data-contrast", "high");
|
|
58
|
-
} else {
|
|
59
|
-
root.removeAttribute("data-contrast");
|
|
60
|
-
}
|
|
61
|
-
root.style.setProperty("--a11y-font-family-alt-enabled", prefs.dyslexiaFont ? "1" : "0");
|
|
62
|
-
if (body) {
|
|
63
|
-
if (prefs.textSpacing === "increased") {
|
|
64
|
-
body.style.letterSpacing = "0.12em";
|
|
65
|
-
body.style.wordSpacing = "0.16em";
|
|
66
|
-
} else {
|
|
67
|
-
body.style.letterSpacing = "";
|
|
68
|
-
body.style.wordSpacing = "";
|
|
69
|
-
}
|
|
70
|
-
body.style.lineHeight = prefs.lineHeight === "increased" ? "1.6" : "";
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
var PreferencesContext = React.createContext(null);
|
|
74
|
-
function useA11YPreferences() {
|
|
75
|
-
const ctx = React.useContext(PreferencesContext);
|
|
76
|
-
if (!ctx)
|
|
77
|
-
throw new Error("useA11YPreferences must be used within A11YPreferencesProvider");
|
|
78
|
-
return ctx;
|
|
79
|
-
}
|
|
80
|
-
function A11YPreferencesProvider({
|
|
81
|
-
children
|
|
82
|
-
}) {
|
|
83
|
-
const [preferences, setPreferencesState] = React.useState(DEFAULT_PREFERENCES);
|
|
84
|
-
const [hydrated, setHydrated] = React.useState(false);
|
|
85
|
-
React.useEffect(() => {
|
|
86
|
-
const stored = getStoredPreferences();
|
|
87
|
-
const next = stored ?? DEFAULT_PREFERENCES;
|
|
88
|
-
setPreferencesState(next);
|
|
89
|
-
setHydrated(true);
|
|
90
|
-
applyCssVariables(next);
|
|
91
|
-
}, []);
|
|
92
|
-
const setPreferences = React.useCallback((updater) => {
|
|
93
|
-
setPreferencesState((prev) => {
|
|
94
|
-
const next = typeof updater === "function" ? updater(prev) : { ...prev, ...updater };
|
|
95
|
-
savePreferences(next);
|
|
96
|
-
applyCssVariables(next);
|
|
97
|
-
try {
|
|
98
|
-
if (typeof window !== "undefined") {
|
|
99
|
-
const changed = {};
|
|
100
|
-
for (const key of Object.keys(next)) {
|
|
101
|
-
if (next[key] !== prev[key]) {
|
|
102
|
-
changed[key] = next[key];
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
window.dispatchEvent(new CustomEvent("a11y:pref_changed", {
|
|
106
|
-
detail: {
|
|
107
|
-
previous: prev,
|
|
108
|
-
current: next,
|
|
109
|
-
changed
|
|
110
|
-
}
|
|
111
|
-
}));
|
|
112
|
-
}
|
|
113
|
-
} catch {}
|
|
114
|
-
return next;
|
|
115
|
-
});
|
|
116
|
-
}, []);
|
|
117
|
-
const value = React.useMemo(() => ({ preferences, setPreferences }), [preferences, setPreferences]);
|
|
118
|
-
return /* @__PURE__ */ jsxDEV(PreferencesContext, {
|
|
119
|
-
value,
|
|
120
|
-
children: [
|
|
121
|
-
children,
|
|
122
|
-
!hydrated ? null : null
|
|
123
|
-
]
|
|
124
|
-
}, undefined, true, undefined, this);
|
|
125
|
-
}
|
|
126
|
-
function a11yRootClassName() {
|
|
127
|
-
return "a11y-root";
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// src/next-route-announcer.tsx
|
|
131
|
-
import { usePathname } from "next/navigation";
|
|
132
|
-
import * as React2 from "react";
|
|
133
|
-
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
134
|
-
"use client";
|
|
135
|
-
function NextRouteAnnouncer() {
|
|
136
|
-
const pathname = usePathname();
|
|
137
|
-
const [message, setMessage] = React2.useState("");
|
|
138
|
-
React2.useEffect(() => {
|
|
139
|
-
if (typeof document !== "undefined") {
|
|
140
|
-
setMessage(document.title || pathname || "");
|
|
141
|
-
}
|
|
142
|
-
}, [pathname]);
|
|
143
|
-
return /* @__PURE__ */ jsxDEV2("div", {
|
|
144
|
-
"aria-live": "polite",
|
|
145
|
-
"aria-atomic": "true",
|
|
146
|
-
className: "sr-only",
|
|
147
|
-
children: message
|
|
148
|
-
}, undefined, false, undefined, this);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// src/AccessibilityProvider.tsx
|
|
152
|
-
import { SRLiveRegionProvider } from "@contractspec/lib.ui-kit-web/ui/live-region";
|
|
153
|
-
import { SkipLink } from "@contractspec/lib.ui-kit-web/ui/skip-link";
|
|
154
|
-
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
155
|
-
"use client";
|
|
156
|
-
function AccessibilityProvider({
|
|
157
|
-
children,
|
|
158
|
-
skipTargetId = "main"
|
|
159
|
-
}) {
|
|
160
|
-
return /* @__PURE__ */ jsxDEV3(A11YPreferencesProvider, {
|
|
161
|
-
children: /* @__PURE__ */ jsxDEV3(SRLiveRegionProvider, {
|
|
162
|
-
children: [
|
|
163
|
-
/* @__PURE__ */ jsxDEV3(SkipLink, {
|
|
164
|
-
targetId: skipTargetId
|
|
165
|
-
}, undefined, false, undefined, this),
|
|
166
|
-
/* @__PURE__ */ jsxDEV3(NextRouteAnnouncer, {}, undefined, false, undefined, this),
|
|
167
|
-
children
|
|
168
|
-
]
|
|
169
|
-
}, undefined, true, undefined, this)
|
|
170
|
-
}, undefined, false, undefined, this);
|
|
171
|
-
}
|
|
172
|
-
export {
|
|
173
|
-
AccessibilityProvider
|
|
174
|
-
};
|
|
2
|
+
import X from"react";import{jsxs as F}from"react/jsx-runtime";var I={textSize:"m",textSpacing:"normal",lineHeight:"normal",underlineLinks:!1,focusRing:"normal",reduceMotion:"system",highContrast:!1,dyslexiaFont:!1},M="a11y:preferences:v1";function N(){try{let q=typeof window<"u"?window.localStorage.getItem(M):null;if(!q)return null;let B=JSON.parse(q);return{...I,...B}}catch{return null}}function _(q){try{if(typeof window<"u")window.localStorage.setItem(M,JSON.stringify(q))}catch{}}function D(q){if(typeof document>"u")return;let{documentElement:B,body:J}=document,G={s:"0.95",m:"1",l:"1.1",xl:"1.25"};B.style.setProperty("--a11y-text-scale",G[q.textSize]);let H=q.textSize==="l"||q.textSize==="xl"?"44px":"0px";if(B.style.setProperty("--a11y-target-min",H),B.style.setProperty("--a11y-letter-spacing",q.textSpacing==="increased"?"0.12em":"normal"),B.style.setProperty("--a11y-word-spacing",q.textSpacing==="increased"?"0.16em":"normal"),B.style.setProperty("--a11y-line-height",q.lineHeight==="increased"?"1.6":"1.5"),B.style.setProperty("--a11y-underline-links",q.underlineLinks?"underline":"revert"),B.style.setProperty("--a11y-focus-ring-width",q.focusRing==="thick"?"4px":"2px"),B.style.setProperty("--a11y-reduce-motion",q.reduceMotion),B.style.setProperty("--a11y-contrast-mode",q.highContrast?"high":"normal"),q.highContrast)B.setAttribute("data-contrast","high");else B.removeAttribute("data-contrast");if(B.style.setProperty("--a11y-font-family-alt-enabled",q.dyslexiaFont?"1":"0"),J){if(q.textSpacing==="increased")J.style.letterSpacing="0.12em",J.style.wordSpacing="0.16em";else J.style.letterSpacing="",J.style.wordSpacing="";J.style.lineHeight=q.lineHeight==="increased"?"1.6":""}}var U=X.createContext(null);function g(){let q=X.useContext(U);if(!q)throw Error("useA11YPreferences must be used within A11YPreferencesProvider");return q}function Y({children:q}){let[B,J]=X.useState(I),[G,H]=X.useState(!1);X.useEffect(()=>{let Q=N()??I;J(Q),H(!0),D(Q)},[]);let O=X.useCallback((Z)=>{J((Q)=>{let W=typeof Z==="function"?Z(Q):{...Q,...Z};_(W),D(W);try{if(typeof window<"u"){let V={};for(let $ of Object.keys(W))if(W[$]!==Q[$])V[$]=W[$];window.dispatchEvent(new CustomEvent("a11y:pref_changed",{detail:{previous:Q,current:W,changed:V}}))}}catch{}return W})},[]),L=X.useMemo(()=>({preferences:B,setPreferences:O}),[B,O]);return F(U,{value:L,children:[q,!G?null:null]})}function m(){return"a11y-root"}import{usePathname as A}from"next/navigation";import*as z from"react";import{jsx as C}from"react/jsx-runtime";function w(){let q=A(),[B,J]=z.useState("");return z.useEffect(()=>{if(typeof document<"u")J(document.title||q||"")},[q]),C("div",{"aria-live":"polite","aria-atomic":"true",className:"sr-only",children:B})}import{SRLiveRegionProvider as T}from"@contractspec/lib.ui-kit-web/ui/live-region";import{SkipLink as j}from"@contractspec/lib.ui-kit-web/ui/skip-link";import{jsx as K,jsxs as b}from"react/jsx-runtime";function x({children:q,skipTargetId:B="main"}){return K(Y,{children:b(T,{children:[K(j,{targetId:B}),K(w,{}),q]})})}export{x as AccessibilityProvider};
|