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