@lssm/lib.accessibility 1.7.4 → 1.9.1

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/README.md CHANGED
@@ -1,8 +1,12 @@
1
1
  # @lssm/lib.accessibility
2
2
 
3
- Stable exports of accessibility primitives for LSSM web apps.
3
+ Stable exports of accessibility primitives for LSSM web apps, ensuring WCAG compliance and inclusive design.
4
4
 
5
- - Install
5
+ ## Purpose
6
+
7
+ To provide a standardized set of accessibility components and hooks that handle focus management, screen reader announcements, and motion preferences across Next.js and React applications.
8
+
9
+ ## Installation
6
10
 
7
11
  ```bash
8
12
  npm install @lssm/lib.accessibility
@@ -10,14 +14,63 @@ npm install @lssm/lib.accessibility
10
14
  bun add @lssm/lib.accessibility
11
15
  ```
12
16
 
13
- - Import with tree-shaken entry points. The `dist/` bundle is platform-aware and assumes you already installed `@lssm/lib.design-system`, `@lssm/lib.ui-kit`, and `@lssm/lib.ui-kit-web`.
17
+ ## Key Concepts
18
+
19
+ - **Focus Management**: Automatically handle focus transitions on route changes.
20
+ - **Live Regions**: reliably announce dynamic content updates to screen readers.
21
+ - **Reduced Motion**: Respect user system preferences for animation.
22
+ - **Hidden Content**: Visually hide content while keeping it available for assistive technology.
23
+
24
+ ## Exports
25
+
26
+ - `SkipLink`: A link to skip navigation, visible on focus.
27
+ - `VisuallyHidden`: Component to hide content from sighted users but keep it for screen readers.
28
+ - `SRLiveRegionProvider`, `useSRLiveRegion`: Context and hook for managing live region announcements.
29
+ - `RouteAnnouncer`: Component that announces page title/path changes on navigation.
30
+ - `FocusOnRouteChange`: Component that resets focus to body or main content on navigation.
31
+ - `useReducedMotion`: Hook to detect if the user prefers reduced motion.
32
+
33
+ ## Usage
34
+
35
+ ### App Setup
36
+
37
+ Wrap your application (e.g., in `layout.tsx` or `_app.tsx`) with the necessary providers and components:
38
+
39
+ ```tsx
40
+ import {
41
+ SRLiveRegionProvider,
42
+ RouteAnnouncer,
43
+ SkipLink,
44
+ } from '@lssm/lib.accessibility';
45
+
46
+ export default function RootLayout({ children }) {
47
+ return (
48
+ <html lang="en">
49
+ <body>
50
+ <SRLiveRegionProvider>
51
+ <SkipLink />
52
+ <RouteAnnouncer />
53
+ <main id="main-content">{children}</main>
54
+ </SRLiveRegionProvider>
55
+ </body>
56
+ </html>
57
+ );
58
+ }
59
+ ```
60
+
61
+ ### Announcing Dynamic Changes
62
+
63
+ ```tsx
64
+ import { useSRLiveRegion } from '@lssm/lib.accessibility';
14
65
 
15
- - `SkipLink`
16
- - `VisuallyHidden`
17
- - `SRLiveRegionProvider`, `useSRLiveRegion`
18
- - `RouteAnnouncer`
19
- - `FocusOnRouteChange`
20
- - `useReducedMotion`
66
+ export function TodoList() {
67
+ const { announce } = useSRLiveRegion();
21
68
 
22
- These map directly to rules in `docs/accessibility_wcag_compliance_specs.md` (skip link, live regions, focus management, PRM).
69
+ const addTodo = () => {
70
+ // ... add logic
71
+ announce('Todo added successfully', 'polite');
72
+ };
23
73
 
74
+ return <button onClick={addTodo}>Add Todo</button>;
75
+ }
76
+ ```
@@ -1,11 +1,11 @@
1
- import * as react_jsx_runtime2 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/AccessibilityPanel.d.ts
4
4
  declare function AccessibilityPanel({
5
5
  className
6
6
  }: {
7
7
  className?: string;
8
- }): react_jsx_runtime2.JSX.Element;
8
+ }): react_jsx_runtime0.JSX.Element;
9
9
  //#endregion
10
10
  export { AccessibilityPanel };
11
11
  //# sourceMappingURL=AccessibilityPanel.d.ts.map
@@ -1,6 +1,5 @@
1
1
  'use client';
2
2
 
3
-
4
3
  import { useA11YPreferences } from "./preferences.js";
5
4
  import React from "react";
6
5
  import { Dialog, DialogClose, DialogContent, DialogOverlay, DialogPortal, DialogTitle, DialogTrigger } from "@lssm/lib.ui-kit-web/ui/dialog";
@@ -1 +1 @@
1
- {"version":3,"file":"AccessibilityPanel.js","names":[],"sources":["../src/AccessibilityPanel.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n} from '@lssm/lib.ui-kit-web/ui/dialog';\nimport { Button } from '@lssm/lib.design-system';\nimport { Switch } from '@lssm/lib.ui-kit-web/ui/switch';\nimport { Label } from '@lssm/lib.ui-kit-web/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@lssm/lib.ui-kit-web/ui/select';\nimport { useA11YPreferences } from './preferences';\nimport { cn } from '@lssm/lib.ui-kit-web/ui/utils';\n\nexport function AccessibilityPanel({ className }: { className?: string }) {\n const { preferences, setPreferences } = useA11YPreferences();\n\n return (\n <Dialog>\n <DialogTrigger asChild>\n <Button variant=\"outline\" aria-haspopup=\"dialog\">\n Accessibility\n </Button>\n </DialogTrigger>\n <DialogPortal>\n <DialogOverlay className=\"fixed inset-0 z-50 bg-black/40\" />\n <DialogContent\n className={cn(\n // 'fixed right-0 top-0 z-50 h-full w-full max-w-md bg-background p-6 shadow-lg outline-hidden focus-visible:ring-2 focus-visible:ring-ring',\n 'bg-background focus-visible:ring-ring max-w-md p-6 shadow-lg outline-hidden focus-visible:ring-2',\n className\n )}\n aria-describedby=\"a11y-panel-desc\"\n >\n <DialogTitle className=\"text-lg font-semibold\">\n Accessibility settings\n </DialogTitle>\n <p id=\"a11y-panel-desc\" className=\"text-muted-foreground text-sm\">\n Adjust text size, spacing, focus and motion to suit your needs.\n </p>\n\n <div className=\"mt-6 space-y-6\">\n <div className=\"space-y-2\">\n <Label>Text size</Label>\n <Select\n value={preferences.textSize}\n onValueChange={(v) => setPreferences({ textSize: v as any })}\n >\n <SelectTrigger aria-label=\"Text size\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"s\">Small</SelectItem>\n <SelectItem value=\"m\">Medium</SelectItem>\n <SelectItem value=\"l\">Large</SelectItem>\n <SelectItem value=\"xl\">Extra Large</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"text-spacing\">\n Increase text spacing (WCAG 1.4.12)\n </Label>\n <Switch\n id=\"text-spacing\"\n checked={preferences.textSpacing === 'increased'}\n onCheckedChange={(c) =>\n setPreferences({ textSpacing: c ? 'increased' : 'normal' })\n }\n />\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"line-height\">Increase line height</Label>\n <Switch\n id=\"line-height\"\n checked={preferences.lineHeight === 'increased'}\n onCheckedChange={(c) =>\n setPreferences({ lineHeight: c ? 'increased' : 'normal' })\n }\n />\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"underline-links\">Always underline links</Label>\n <Switch\n id=\"underline-links\"\n checked={preferences.underlineLinks}\n onCheckedChange={(c) => setPreferences({ underlineLinks: c })}\n />\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"focus-ring\">Thick focus ring</Label>\n <Switch\n id=\"focus-ring\"\n checked={preferences.focusRing === 'thick'}\n onCheckedChange={(c) =>\n setPreferences({ focusRing: c ? 'thick' : 'normal' })\n }\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Motion</Label>\n <Select\n value={preferences.reduceMotion}\n onValueChange={(v) =>\n setPreferences({ reduceMotion: v as any })\n }\n >\n <SelectTrigger aria-label=\"Motion preferences\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"system\">Follow system</SelectItem>\n <SelectItem value=\"reduce\">Reduce motion</SelectItem>\n <SelectItem value=\"no-preference\">Allow motion</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"contrast\">High contrast</Label>\n <Switch\n id=\"contrast\"\n checked={preferences.highContrast}\n onCheckedChange={(c) => setPreferences({ highContrast: c })}\n />\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"dyslexia-font\">Dyslexia-friendly font</Label>\n <Switch\n id=\"dyslexia-font\"\n checked={preferences.dyslexiaFont}\n onCheckedChange={(c) => setPreferences({ dyslexiaFont: c })}\n />\n </div>\n </div>\n\n <div className=\"mt-8 flex justify-end gap-2\">\n <DialogClose asChild>\n <Button variant=\"secondary\">Close</Button>\n </DialogClose>\n </div>\n </DialogContent>\n </DialogPortal>\n </Dialog>\n );\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"AccessibilityPanel.js","names":[],"sources":["../src/AccessibilityPanel.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\nimport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n} from '@lssm/lib.ui-kit-web/ui/dialog';\nimport { Button } from '@lssm/lib.design-system';\nimport { Switch } from '@lssm/lib.ui-kit-web/ui/switch';\nimport { Label } from '@lssm/lib.ui-kit-web/ui/label';\nimport {\n Select,\n SelectContent,\n SelectItem,\n SelectTrigger,\n SelectValue,\n} from '@lssm/lib.ui-kit-web/ui/select';\nimport { useA11YPreferences } from './preferences';\nimport { cn } from '@lssm/lib.ui-kit-web/ui/utils';\n\nexport function AccessibilityPanel({ className }: { className?: string }) {\n const { preferences, setPreferences } = useA11YPreferences();\n\n return (\n <Dialog>\n <DialogTrigger asChild>\n <Button variant=\"outline\" aria-haspopup=\"dialog\">\n Accessibility\n </Button>\n </DialogTrigger>\n <DialogPortal>\n <DialogOverlay className=\"fixed inset-0 z-50 bg-black/40\" />\n <DialogContent\n className={cn(\n // 'fixed right-0 top-0 z-50 h-full w-full max-w-md bg-background p-6 shadow-lg outline-hidden focus-visible:ring-2 focus-visible:ring-ring',\n 'bg-background focus-visible:ring-ring max-w-md p-6 shadow-lg outline-hidden focus-visible:ring-2',\n className\n )}\n aria-describedby=\"a11y-panel-desc\"\n >\n <DialogTitle className=\"text-lg font-semibold\">\n Accessibility settings\n </DialogTitle>\n <p id=\"a11y-panel-desc\" className=\"text-muted-foreground text-sm\">\n Adjust text size, spacing, focus and motion to suit your needs.\n </p>\n\n <div className=\"mt-6 space-y-6\">\n <div className=\"space-y-2\">\n <Label>Text size</Label>\n <Select\n value={preferences.textSize}\n onValueChange={(v) => setPreferences({ textSize: v as any })}\n >\n <SelectTrigger aria-label=\"Text size\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"s\">Small</SelectItem>\n <SelectItem value=\"m\">Medium</SelectItem>\n <SelectItem value=\"l\">Large</SelectItem>\n <SelectItem value=\"xl\">Extra Large</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"text-spacing\">\n Increase text spacing (WCAG 1.4.12)\n </Label>\n <Switch\n id=\"text-spacing\"\n checked={preferences.textSpacing === 'increased'}\n onCheckedChange={(c) =>\n setPreferences({ textSpacing: c ? 'increased' : 'normal' })\n }\n />\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"line-height\">Increase line height</Label>\n <Switch\n id=\"line-height\"\n checked={preferences.lineHeight === 'increased'}\n onCheckedChange={(c) =>\n setPreferences({ lineHeight: c ? 'increased' : 'normal' })\n }\n />\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"underline-links\">Always underline links</Label>\n <Switch\n id=\"underline-links\"\n checked={preferences.underlineLinks}\n onCheckedChange={(c) => setPreferences({ underlineLinks: c })}\n />\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"focus-ring\">Thick focus ring</Label>\n <Switch\n id=\"focus-ring\"\n checked={preferences.focusRing === 'thick'}\n onCheckedChange={(c) =>\n setPreferences({ focusRing: c ? 'thick' : 'normal' })\n }\n />\n </div>\n\n <div className=\"space-y-2\">\n <Label>Motion</Label>\n <Select\n value={preferences.reduceMotion}\n onValueChange={(v) =>\n setPreferences({ reduceMotion: v as any })\n }\n >\n <SelectTrigger aria-label=\"Motion preferences\">\n <SelectValue />\n </SelectTrigger>\n <SelectContent>\n <SelectItem value=\"system\">Follow system</SelectItem>\n <SelectItem value=\"reduce\">Reduce motion</SelectItem>\n <SelectItem value=\"no-preference\">Allow motion</SelectItem>\n </SelectContent>\n </Select>\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"contrast\">High contrast</Label>\n <Switch\n id=\"contrast\"\n checked={preferences.highContrast}\n onCheckedChange={(c) => setPreferences({ highContrast: c })}\n />\n </div>\n\n <div className=\"flex items-center justify-between\">\n <Label htmlFor=\"dyslexia-font\">Dyslexia-friendly font</Label>\n <Switch\n id=\"dyslexia-font\"\n checked={preferences.dyslexiaFont}\n onCheckedChange={(c) => setPreferences({ dyslexiaFont: c })}\n />\n </div>\n </div>\n\n <div className=\"mt-8 flex justify-end gap-2\">\n <DialogClose asChild>\n <Button variant=\"secondary\">Close</Button>\n </DialogClose>\n </div>\n </DialogContent>\n </DialogPortal>\n </Dialog>\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAyBA,SAAgB,mBAAmB,EAAE,aAAqC;CACxE,MAAM,EAAE,aAAa,mBAAmB,oBAAoB;AAE5D,QACE,qBAAC,qBACC,oBAAC;EAAc;YACb,oBAAC;GAAO,SAAQ;GAAU,iBAAc;aAAS;IAExC;GACK,EAChB,qBAAC,2BACC,oBAAC,iBAAc,WAAU,mCAAmC,EAC5D,qBAAC;EACC,WAAW,GAET,oGACA,UACD;EACD,oBAAiB;;GAEjB,oBAAC;IAAY,WAAU;cAAwB;KAEjC;GACd,oBAAC;IAAE,IAAG;IAAkB,WAAU;cAAgC;KAE9D;GAEJ,qBAAC;IAAI,WAAU;;KACb,qBAAC;MAAI,WAAU;iBACb,oBAAC,mBAAM,cAAiB,EACxB,qBAAC;OACC,OAAO,YAAY;OACnB,gBAAgB,MAAM,eAAe,EAAE,UAAU,GAAU,CAAC;kBAE5D,oBAAC;QAAc,cAAW;kBACxB,oBAAC,gBAAc;SACD,EAChB,qBAAC;QACC,oBAAC;SAAW,OAAM;mBAAI;UAAkB;QACxC,oBAAC;SAAW,OAAM;mBAAI;UAAmB;QACzC,oBAAC;SAAW,OAAM;mBAAI;UAAkB;QACxC,oBAAC;SAAW,OAAM;mBAAK;UAAwB;WACjC;QACT;OACL;KAEN,qBAAC;MAAI,WAAU;iBACb,oBAAC;OAAM,SAAQ;iBAAe;QAEtB,EACR,oBAAC;OACC,IAAG;OACH,SAAS,YAAY,gBAAgB;OACrC,kBAAkB,MAChB,eAAe,EAAE,aAAa,IAAI,cAAc,UAAU,CAAC;QAE7D;OACE;KAEN,qBAAC;MAAI,WAAU;iBACb,oBAAC;OAAM,SAAQ;iBAAc;QAA4B,EACzD,oBAAC;OACC,IAAG;OACH,SAAS,YAAY,eAAe;OACpC,kBAAkB,MAChB,eAAe,EAAE,YAAY,IAAI,cAAc,UAAU,CAAC;QAE5D;OACE;KAEN,qBAAC;MAAI,WAAU;iBACb,oBAAC;OAAM,SAAQ;iBAAkB;QAA8B,EAC/D,oBAAC;OACC,IAAG;OACH,SAAS,YAAY;OACrB,kBAAkB,MAAM,eAAe,EAAE,gBAAgB,GAAG,CAAC;QAC7D;OACE;KAEN,qBAAC;MAAI,WAAU;iBACb,oBAAC;OAAM,SAAQ;iBAAa;QAAwB,EACpD,oBAAC;OACC,IAAG;OACH,SAAS,YAAY,cAAc;OACnC,kBAAkB,MAChB,eAAe,EAAE,WAAW,IAAI,UAAU,UAAU,CAAC;QAEvD;OACE;KAEN,qBAAC;MAAI,WAAU;iBACb,oBAAC,mBAAM,WAAc,EACrB,qBAAC;OACC,OAAO,YAAY;OACnB,gBAAgB,MACd,eAAe,EAAE,cAAc,GAAU,CAAC;kBAG5C,oBAAC;QAAc,cAAW;kBACxB,oBAAC,gBAAc;SACD,EAChB,qBAAC;QACC,oBAAC;SAAW,OAAM;mBAAS;UAA0B;QACrD,oBAAC;SAAW,OAAM;mBAAS;UAA0B;QACrD,oBAAC;SAAW,OAAM;mBAAgB;UAAyB;WAC7C;QACT;OACL;KAEN,qBAAC;MAAI,WAAU;iBACb,oBAAC;OAAM,SAAQ;iBAAW;QAAqB,EAC/C,oBAAC;OACC,IAAG;OACH,SAAS,YAAY;OACrB,kBAAkB,MAAM,eAAe,EAAE,cAAc,GAAG,CAAC;QAC3D;OACE;KAEN,qBAAC;MAAI,WAAU;iBACb,oBAAC;OAAM,SAAQ;iBAAgB;QAA8B,EAC7D,oBAAC;OACC,IAAG;OACH,SAAS,YAAY;OACrB,kBAAkB,MAAM,eAAe,EAAE,cAAc,GAAG,CAAC;QAC3D;OACE;;KACF;GAEN,oBAAC;IAAI,WAAU;cACb,oBAAC;KAAY;eACX,oBAAC;MAAO,SAAQ;gBAAY;OAAc;MAC9B;KACV;;GACQ,IACH,IACR"}
@@ -1,5 +1,5 @@
1
1
  import * as React$1 from "react";
2
- import * as react_jsx_runtime1 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/AccessibilityProvider.d.ts
5
5
  declare function AccessibilityProvider({
@@ -8,7 +8,7 @@ declare function AccessibilityProvider({
8
8
  }: {
9
9
  children: React$1.ReactNode;
10
10
  skipTargetId?: string;
11
- }): react_jsx_runtime1.JSX.Element;
11
+ }): react_jsx_runtime0.JSX.Element;
12
12
  //#endregion
13
13
  export { AccessibilityProvider };
14
14
  //# sourceMappingURL=AccessibilityProvider.d.ts.map
@@ -1,6 +1,5 @@
1
1
  'use client';
2
2
 
3
-
4
3
  import { A11YPreferencesProvider } from "./preferences.js";
5
4
  import { NextRouteAnnouncer } from "./next-route-announcer.js";
6
5
  import "react";
@@ -1 +1 @@
1
- {"version":3,"file":"AccessibilityProvider.js","names":[],"sources":["../src/AccessibilityProvider.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { A11YPreferencesProvider } from './preferences';\nimport { SRLiveRegionProvider } from '@lssm/lib.ui-kit-web/ui/live-region';\nimport { NextRouteAnnouncer } from './next-route-announcer';\nimport { SkipLink } from '@lssm/lib.ui-kit-web/ui/skip-link';\n\nexport function AccessibilityProvider({\n children,\n skipTargetId = 'main',\n}: {\n children: React.ReactNode;\n skipTargetId?: string;\n}) {\n return (\n <A11YPreferencesProvider>\n <SRLiveRegionProvider>\n <SkipLink targetId={skipTargetId} />\n <NextRouteAnnouncer />\n {children}\n </SRLiveRegionProvider>\n </A11YPreferencesProvider>\n );\n}\n"],"mappings":";;;;;;;;;;;AAQA,SAAgB,sBAAsB,EACpC,UACA,eAAe,UAId;AACD,QACE,oBAAC,qCACC,qBAAC;EACC,oBAAC,YAAS,UAAU,eAAgB;EACpC,oBAAC,uBAAqB;EACrB;KACoB,GACC"}
1
+ {"version":3,"file":"AccessibilityProvider.js","names":[],"sources":["../src/AccessibilityProvider.tsx"],"sourcesContent":["'use client';\n\nimport * as React from 'react';\nimport { A11YPreferencesProvider } from './preferences';\nimport { SRLiveRegionProvider } from '@lssm/lib.ui-kit-web/ui/live-region';\nimport { NextRouteAnnouncer } from './next-route-announcer';\nimport { SkipLink } from '@lssm/lib.ui-kit-web/ui/skip-link';\n\nexport function AccessibilityProvider({\n children,\n skipTargetId = 'main',\n}: {\n children: React.ReactNode;\n skipTargetId?: string;\n}) {\n return (\n <A11YPreferencesProvider>\n <SRLiveRegionProvider>\n <SkipLink targetId={skipTargetId} />\n <NextRouteAnnouncer />\n {children}\n </SRLiveRegionProvider>\n </A11YPreferencesProvider>\n );\n}\n"],"mappings":";;;;;;;;;;AAQA,SAAgB,sBAAsB,EACpC,UACA,eAAe,UAId;AACD,QACE,oBAAC,qCACC,qBAAC;EACC,oBAAC,YAAS,UAAU,eAAgB;EACpC,oBAAC,uBAAqB;EACrB;KACoB,GACC"}
@@ -1,7 +1,7 @@
1
- import * as react_jsx_runtime0 from "react/jsx-runtime";
1
+ import * as react_jsx_runtime1 from "react/jsx-runtime";
2
2
 
3
3
  //#region src/next-route-announcer.d.ts
4
- declare function NextRouteAnnouncer(): react_jsx_runtime0.JSX.Element;
4
+ declare function NextRouteAnnouncer(): react_jsx_runtime1.JSX.Element;
5
5
  //#endregion
6
6
  export { NextRouteAnnouncer };
7
7
  //# sourceMappingURL=next-route-announcer.d.ts.map
@@ -1,6 +1,5 @@
1
1
  'use client';
2
2
 
3
-
4
3
  import * as React$1 from "react";
5
4
  import { jsx } from "react/jsx-runtime";
6
5
  import { usePathname } from "next/navigation";
@@ -1 +1 @@
1
- {"version":3,"file":"next-route-announcer.js","names":["React"],"sources":["../src/next-route-announcer.tsx"],"sourcesContent":["'use client';\nimport * as React from 'react';\nimport { usePathname } from 'next/navigation';\n\nexport function NextRouteAnnouncer() {\n const pathname = usePathname();\n const [message, setMessage] = React.useState('');\n\n React.useEffect(() => {\n // announce current document title whenever pathname changes\n if (typeof document !== 'undefined') {\n setMessage(document.title || pathname || '');\n }\n }, [pathname]);\n\n return (\n <div aria-live=\"polite\" aria-atomic=\"true\" className=\"sr-only\">\n {message}\n </div>\n );\n}\n"],"mappings":";;;;;;;;AAIA,SAAgB,qBAAqB;CACnC,MAAM,WAAW,aAAa;CAC9B,MAAM,CAAC,SAAS,cAAcA,QAAM,SAAS,GAAG;AAEhD,SAAM,gBAAgB;AAEpB,MAAI,OAAO,aAAa,YACtB,YAAW,SAAS,SAAS,YAAY,GAAG;IAE7C,CAAC,SAAS,CAAC;AAEd,QACE,oBAAC;EAAI,aAAU;EAAS,eAAY;EAAO,WAAU;YAClD;GACG"}
1
+ {"version":3,"file":"next-route-announcer.js","names":["React"],"sources":["../src/next-route-announcer.tsx"],"sourcesContent":["'use client';\nimport * as React from 'react';\nimport { usePathname } from 'next/navigation';\n\nexport function NextRouteAnnouncer() {\n const pathname = usePathname();\n const [message, setMessage] = React.useState('');\n\n React.useEffect(() => {\n // announce current document title whenever pathname changes\n if (typeof document !== 'undefined') {\n setMessage(document.title || pathname || '');\n }\n }, [pathname]);\n\n return (\n <div aria-live=\"polite\" aria-atomic=\"true\" className=\"sr-only\">\n {message}\n </div>\n );\n}\n"],"mappings":";;;;;;;AAIA,SAAgB,qBAAqB;CACnC,MAAM,WAAW,aAAa;CAC9B,MAAM,CAAC,SAAS,cAAcA,QAAM,SAAS,GAAG;AAEhD,SAAM,gBAAgB;AAEpB,MAAI,OAAO,aAAa,YACtB,YAAW,SAAS,SAAS,YAAY,GAAG;IAE7C,CAAC,SAAS,CAAC;AAEd,QACE,oBAAC;EAAI,aAAU;EAAS,eAAY;EAAO,WAAU;YAClD;GACG"}
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import * as react_jsx_runtime0 from "react/jsx-runtime";
2
+ import * as react_jsx_runtime2 from "react/jsx-runtime";
3
3
 
4
4
  //#region src/preferences.d.ts
5
5
  type TextSize = 's' | 'm' | 'l' | 'xl';
@@ -29,7 +29,7 @@ declare function A11YPreferencesProvider({
29
29
  children
30
30
  }: {
31
31
  children: React.ReactNode;
32
- }): react_jsx_runtime0.JSX.Element;
32
+ }): react_jsx_runtime2.JSX.Element;
33
33
  declare function a11yRootClassName(): string;
34
34
  //#endregion
35
35
  export { A11YPreferencesProvider, AccessibilityPreferences, DyslexiaFont, FocusRing, HighContrast, LineHeight, ReduceMotion, TextSize, TextSpacing, UnderlineLinks, a11yRootClassName, useA11YPreferences };
@@ -1,6 +1,5 @@
1
1
  'use client';
2
2
 
3
-
4
3
  import React from "react";
5
4
  import { jsxs } from "react/jsx-runtime";
6
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"preferences.js","names":["DEFAULT_PREFERENCES: AccessibilityPreferences","changed: Partial<AccessibilityPreferences>"],"sources":["../src/preferences.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nexport type TextSize = 's' | 'm' | 'l' | 'xl';\nexport type TextSpacing = 'normal' | 'increased';\nexport type LineHeight = 'normal' | 'increased';\nexport type UnderlineLinks = boolean;\nexport type FocusRing = 'normal' | 'thick';\nexport type ReduceMotion = 'system' | 'reduce' | 'no-preference';\nexport type HighContrast = boolean;\nexport type DyslexiaFont = boolean;\n\nexport interface AccessibilityPreferences {\n textSize: TextSize;\n textSpacing: TextSpacing;\n lineHeight: LineHeight;\n underlineLinks: UnderlineLinks;\n focusRing: FocusRing;\n reduceMotion: ReduceMotion;\n highContrast: HighContrast;\n dyslexiaFont: DyslexiaFont;\n}\n\nconst DEFAULT_PREFERENCES: AccessibilityPreferences = {\n textSize: 'm',\n textSpacing: 'normal',\n lineHeight: 'normal',\n underlineLinks: false,\n focusRing: 'normal',\n reduceMotion: 'system',\n highContrast: false,\n dyslexiaFont: false,\n};\n\nconst STORAGE_KEY = 'a11y:preferences:v1';\n\nfunction getStoredPreferences(): AccessibilityPreferences | null {\n try {\n const raw =\n typeof window !== 'undefined'\n ? window.localStorage.getItem(STORAGE_KEY)\n : null;\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n return { ...DEFAULT_PREFERENCES, ...parsed } as AccessibilityPreferences;\n } catch {\n return null;\n }\n}\n\nfunction savePreferences(prefs: AccessibilityPreferences) {\n try {\n if (typeof window !== 'undefined') {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));\n }\n } catch {\n // ignore\n }\n}\n\nfunction applyCssVariables(prefs: AccessibilityPreferences) {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n const body = document.body;\n // Type scale (base multiplier)\n const sizeMap: Record<TextSize, string> = {\n s: '0.95',\n m: '1',\n l: '1.1',\n xl: '1.25',\n };\n root.style.setProperty('--a11y-text-scale', sizeMap[prefs.textSize]);\n // Target size: increase on larger text choices\n const targetMin =\n prefs.textSize === 'l' || prefs.textSize === 'xl' ? '44px' : '0px';\n root.style.setProperty('--a11y-target-min', targetMin);\n\n // Spacing per WCAG 1.4.12\n root.style.setProperty(\n '--a11y-letter-spacing',\n prefs.textSpacing === 'increased' ? '0.12em' : 'normal'\n );\n root.style.setProperty(\n '--a11y-word-spacing',\n prefs.textSpacing === 'increased' ? '0.16em' : 'normal'\n );\n root.style.setProperty(\n '--a11y-line-height',\n prefs.lineHeight === 'increased' ? '1.6' : '1.5'\n );\n\n // Links\n root.style.setProperty(\n '--a11y-underline-links',\n // Use 'revert' so we don't override the site's default when disabled\n prefs.underlineLinks ? 'underline' : 'revert'\n );\n\n // Focus ring\n root.style.setProperty(\n '--a11y-focus-ring-width',\n prefs.focusRing === 'thick' ? '4px' : '2px'\n );\n\n // Reduce motion\n root.style.setProperty('--a11y-reduce-motion', prefs.reduceMotion);\n\n // Contrast\n root.style.setProperty(\n '--a11y-contrast-mode',\n prefs.highContrast ? 'high' : 'normal'\n );\n if (prefs.highContrast) {\n root.setAttribute('data-contrast', 'high');\n } else {\n root.removeAttribute('data-contrast');\n }\n\n // Dyslexia-friendly font (consumer should map to an available font family)\n root.style.setProperty(\n '--a11y-font-family-alt-enabled',\n prefs.dyslexiaFont ? '1' : '0'\n );\n\n // Inline fallbacks in case global CSS variables are not wired/included by the app bundler.\n // These properties are inheritable, so setting them on <body> yields broad coverage.\n if (body) {\n if (prefs.textSpacing === 'increased') {\n body.style.letterSpacing = '0.12em';\n body.style.wordSpacing = '0.16em';\n } else {\n body.style.letterSpacing = '';\n body.style.wordSpacing = '';\n }\n\n body.style.lineHeight = prefs.lineHeight === 'increased' ? '1.6' : '';\n }\n}\n\ninterface PreferencesContextValue {\n preferences: AccessibilityPreferences;\n setPreferences: (\n updater:\n | Partial<AccessibilityPreferences>\n | ((p: AccessibilityPreferences) => AccessibilityPreferences)\n ) => void;\n}\n\nconst PreferencesContext = React.createContext<PreferencesContextValue | null>(\n null\n);\n\nexport function useA11YPreferences() {\n const ctx = React.useContext(PreferencesContext);\n if (!ctx)\n throw new Error(\n 'useA11YPreferences must be used within A11YPreferencesProvider'\n );\n return ctx;\n}\n\nexport function A11YPreferencesProvider({\n children,\n}: {\n children: React.ReactNode;\n}) {\n const [preferences, setPreferencesState] =\n React.useState<AccessibilityPreferences>(DEFAULT_PREFERENCES);\n const [hydrated, setHydrated] = React.useState(false);\n\n React.useEffect(() => {\n const stored = getStoredPreferences();\n const next = stored ?? DEFAULT_PREFERENCES;\n setPreferencesState(next);\n setHydrated(true);\n applyCssVariables(next);\n }, []);\n\n const setPreferences = React.useCallback<\n PreferencesContextValue['setPreferences']\n >((updater) => {\n setPreferencesState((prev) => {\n const next =\n typeof updater === 'function' ? updater(prev) : { ...prev, ...updater };\n savePreferences(next);\n applyCssVariables(next);\n try {\n if (typeof window !== 'undefined') {\n const changed: Partial<AccessibilityPreferences> = {};\n for (const key of Object.keys(\n next\n ) as (keyof AccessibilityPreferences)[]) {\n if (next[key] !== prev[key]) {\n changed[key] = next[key] as any;\n }\n }\n window.dispatchEvent(\n new CustomEvent('a11y:pref_changed', {\n detail: {\n previous: prev,\n current: next,\n changed,\n },\n })\n );\n }\n } catch {}\n return next;\n });\n }, []);\n\n const value = React.useMemo(\n () => ({ preferences, setPreferences }),\n [preferences, setPreferences]\n );\n\n return (\n <PreferencesContext value={value}>\n {children}\n {!hydrated ? null : null}\n </PreferencesContext>\n );\n}\n\n// Helper to compute className additions that respect tokens (optional)\nexport function a11yRootClassName() {\n return 'a11y-root';\n}\n"],"mappings":";;;;;;;AAwBA,MAAMA,sBAAgD;CACpD,UAAU;CACV,aAAa;CACb,YAAY;CACZ,gBAAgB;CAChB,WAAW;CACX,cAAc;CACd,cAAc;CACd,cAAc;CACf;AAED,MAAM,cAAc;AAEpB,SAAS,uBAAwD;AAC/D,KAAI;EACF,MAAM,MACJ,OAAO,WAAW,cACd,OAAO,aAAa,QAAQ,YAAY,GACxC;AACN,MAAI,CAAC,IAAK,QAAO;EACjB,MAAM,SAAS,KAAK,MAAM,IAAI;AAC9B,SAAO;GAAE,GAAG;GAAqB,GAAG;GAAQ;SACtC;AACN,SAAO;;;AAIX,SAAS,gBAAgB,OAAiC;AACxD,KAAI;AACF,MAAI,OAAO,WAAW,YACpB,QAAO,aAAa,QAAQ,aAAa,KAAK,UAAU,MAAM,CAAC;SAE3D;;AAKV,SAAS,kBAAkB,OAAiC;AAC1D,KAAI,OAAO,aAAa,YAAa;CACrC,MAAM,OAAO,SAAS;CACtB,MAAM,OAAO,SAAS;AAQtB,MAAK,MAAM,YAAY,qBANmB;EACxC,GAAG;EACH,GAAG;EACH,GAAG;EACH,IAAI;EACL,CACmD,MAAM,UAAU;CAEpE,MAAM,YACJ,MAAM,aAAa,OAAO,MAAM,aAAa,OAAO,SAAS;AAC/D,MAAK,MAAM,YAAY,qBAAqB,UAAU;AAGtD,MAAK,MAAM,YACT,yBACA,MAAM,gBAAgB,cAAc,WAAW,SAChD;AACD,MAAK,MAAM,YACT,uBACA,MAAM,gBAAgB,cAAc,WAAW,SAChD;AACD,MAAK,MAAM,YACT,sBACA,MAAM,eAAe,cAAc,QAAQ,MAC5C;AAGD,MAAK,MAAM,YACT,0BAEA,MAAM,iBAAiB,cAAc,SACtC;AAGD,MAAK,MAAM,YACT,2BACA,MAAM,cAAc,UAAU,QAAQ,MACvC;AAGD,MAAK,MAAM,YAAY,wBAAwB,MAAM,aAAa;AAGlE,MAAK,MAAM,YACT,wBACA,MAAM,eAAe,SAAS,SAC/B;AACD,KAAI,MAAM,aACR,MAAK,aAAa,iBAAiB,OAAO;KAE1C,MAAK,gBAAgB,gBAAgB;AAIvC,MAAK,MAAM,YACT,kCACA,MAAM,eAAe,MAAM,IAC5B;AAID,KAAI,MAAM;AACR,MAAI,MAAM,gBAAgB,aAAa;AACrC,QAAK,MAAM,gBAAgB;AAC3B,QAAK,MAAM,cAAc;SACpB;AACL,QAAK,MAAM,gBAAgB;AAC3B,QAAK,MAAM,cAAc;;AAG3B,OAAK,MAAM,aAAa,MAAM,eAAe,cAAc,QAAQ;;;AAavE,MAAM,qBAAqB,MAAM,cAC/B,KACD;AAED,SAAgB,qBAAqB;CACnC,MAAM,MAAM,MAAM,WAAW,mBAAmB;AAChD,KAAI,CAAC,IACH,OAAM,IAAI,MACR,iEACD;AACH,QAAO;;AAGT,SAAgB,wBAAwB,EACtC,YAGC;CACD,MAAM,CAAC,aAAa,uBAClB,MAAM,SAAmC,oBAAoB;CAC/D,MAAM,CAAC,UAAU,eAAe,MAAM,SAAS,MAAM;AAErD,OAAM,gBAAgB;EAEpB,MAAM,OADS,sBAAsB,IACd;AACvB,sBAAoB,KAAK;AACzB,cAAY,KAAK;AACjB,oBAAkB,KAAK;IACtB,EAAE,CAAC;CAEN,MAAM,iBAAiB,MAAM,aAE1B,YAAY;AACb,uBAAqB,SAAS;GAC5B,MAAM,OACJ,OAAO,YAAY,aAAa,QAAQ,KAAK,GAAG;IAAE,GAAG;IAAM,GAAG;IAAS;AACzE,mBAAgB,KAAK;AACrB,qBAAkB,KAAK;AACvB,OAAI;AACF,QAAI,OAAO,WAAW,aAAa;KACjC,MAAMC,UAA6C,EAAE;AACrD,UAAK,MAAM,OAAO,OAAO,KACvB,KACD,CACC,KAAI,KAAK,SAAS,KAAK,KACrB,SAAQ,OAAO,KAAK;AAGxB,YAAO,cACL,IAAI,YAAY,qBAAqB,EACnC,QAAQ;MACN,UAAU;MACV,SAAS;MACT;MACD,EACF,CAAC,CACH;;WAEG;AACR,UAAO;IACP;IACD,EAAE,CAAC;AAON,QACE,qBAAC;EAAmB,OANR,MAAM,eACX;GAAE;GAAa;GAAgB,GACtC,CAAC,aAAa,eAAe,CAC9B;aAII,UACA,CAAC,WAAW,OAAO;GACD;;AAKzB,SAAgB,oBAAoB;AAClC,QAAO"}
1
+ {"version":3,"file":"preferences.js","names":["DEFAULT_PREFERENCES: AccessibilityPreferences","changed: Partial<AccessibilityPreferences>"],"sources":["../src/preferences.tsx"],"sourcesContent":["'use client';\n\nimport React from 'react';\n\nexport type TextSize = 's' | 'm' | 'l' | 'xl';\nexport type TextSpacing = 'normal' | 'increased';\nexport type LineHeight = 'normal' | 'increased';\nexport type UnderlineLinks = boolean;\nexport type FocusRing = 'normal' | 'thick';\nexport type ReduceMotion = 'system' | 'reduce' | 'no-preference';\nexport type HighContrast = boolean;\nexport type DyslexiaFont = boolean;\n\nexport interface AccessibilityPreferences {\n textSize: TextSize;\n textSpacing: TextSpacing;\n lineHeight: LineHeight;\n underlineLinks: UnderlineLinks;\n focusRing: FocusRing;\n reduceMotion: ReduceMotion;\n highContrast: HighContrast;\n dyslexiaFont: DyslexiaFont;\n}\n\nconst DEFAULT_PREFERENCES: AccessibilityPreferences = {\n textSize: 'm',\n textSpacing: 'normal',\n lineHeight: 'normal',\n underlineLinks: false,\n focusRing: 'normal',\n reduceMotion: 'system',\n highContrast: false,\n dyslexiaFont: false,\n};\n\nconst STORAGE_KEY = 'a11y:preferences:v1';\n\nfunction getStoredPreferences(): AccessibilityPreferences | null {\n try {\n const raw =\n typeof window !== 'undefined'\n ? window.localStorage.getItem(STORAGE_KEY)\n : null;\n if (!raw) return null;\n const parsed = JSON.parse(raw);\n return { ...DEFAULT_PREFERENCES, ...parsed } as AccessibilityPreferences;\n } catch {\n return null;\n }\n}\n\nfunction savePreferences(prefs: AccessibilityPreferences) {\n try {\n if (typeof window !== 'undefined') {\n window.localStorage.setItem(STORAGE_KEY, JSON.stringify(prefs));\n }\n } catch {\n // ignore\n }\n}\n\nfunction applyCssVariables(prefs: AccessibilityPreferences) {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n const body = document.body;\n // Type scale (base multiplier)\n const sizeMap: Record<TextSize, string> = {\n s: '0.95',\n m: '1',\n l: '1.1',\n xl: '1.25',\n };\n root.style.setProperty('--a11y-text-scale', sizeMap[prefs.textSize]);\n // Target size: increase on larger text choices\n const targetMin =\n prefs.textSize === 'l' || prefs.textSize === 'xl' ? '44px' : '0px';\n root.style.setProperty('--a11y-target-min', targetMin);\n\n // Spacing per WCAG 1.4.12\n root.style.setProperty(\n '--a11y-letter-spacing',\n prefs.textSpacing === 'increased' ? '0.12em' : 'normal'\n );\n root.style.setProperty(\n '--a11y-word-spacing',\n prefs.textSpacing === 'increased' ? '0.16em' : 'normal'\n );\n root.style.setProperty(\n '--a11y-line-height',\n prefs.lineHeight === 'increased' ? '1.6' : '1.5'\n );\n\n // Links\n root.style.setProperty(\n '--a11y-underline-links',\n // Use 'revert' so we don't override the site's default when disabled\n prefs.underlineLinks ? 'underline' : 'revert'\n );\n\n // Focus ring\n root.style.setProperty(\n '--a11y-focus-ring-width',\n prefs.focusRing === 'thick' ? '4px' : '2px'\n );\n\n // Reduce motion\n root.style.setProperty('--a11y-reduce-motion', prefs.reduceMotion);\n\n // Contrast\n root.style.setProperty(\n '--a11y-contrast-mode',\n prefs.highContrast ? 'high' : 'normal'\n );\n if (prefs.highContrast) {\n root.setAttribute('data-contrast', 'high');\n } else {\n root.removeAttribute('data-contrast');\n }\n\n // Dyslexia-friendly font (consumer should map to an available font family)\n root.style.setProperty(\n '--a11y-font-family-alt-enabled',\n prefs.dyslexiaFont ? '1' : '0'\n );\n\n // Inline fallbacks in case global CSS variables are not wired/included by the app bundler.\n // These properties are inheritable, so setting them on <body> yields broad coverage.\n if (body) {\n if (prefs.textSpacing === 'increased') {\n body.style.letterSpacing = '0.12em';\n body.style.wordSpacing = '0.16em';\n } else {\n body.style.letterSpacing = '';\n body.style.wordSpacing = '';\n }\n\n body.style.lineHeight = prefs.lineHeight === 'increased' ? '1.6' : '';\n }\n}\n\ninterface PreferencesContextValue {\n preferences: AccessibilityPreferences;\n setPreferences: (\n updater:\n | Partial<AccessibilityPreferences>\n | ((p: AccessibilityPreferences) => AccessibilityPreferences)\n ) => void;\n}\n\nconst PreferencesContext = React.createContext<PreferencesContextValue | null>(\n null\n);\n\nexport function useA11YPreferences() {\n const ctx = React.useContext(PreferencesContext);\n if (!ctx)\n throw new Error(\n 'useA11YPreferences must be used within A11YPreferencesProvider'\n );\n return ctx;\n}\n\nexport function A11YPreferencesProvider({\n children,\n}: {\n children: React.ReactNode;\n}) {\n const [preferences, setPreferencesState] =\n React.useState<AccessibilityPreferences>(DEFAULT_PREFERENCES);\n const [hydrated, setHydrated] = React.useState(false);\n\n React.useEffect(() => {\n const stored = getStoredPreferences();\n const next = stored ?? DEFAULT_PREFERENCES;\n setPreferencesState(next);\n setHydrated(true);\n applyCssVariables(next);\n }, []);\n\n const setPreferences = React.useCallback<\n PreferencesContextValue['setPreferences']\n >((updater) => {\n setPreferencesState((prev) => {\n const next =\n typeof updater === 'function' ? updater(prev) : { ...prev, ...updater };\n savePreferences(next);\n applyCssVariables(next);\n try {\n if (typeof window !== 'undefined') {\n const changed: Partial<AccessibilityPreferences> = {};\n for (const key of Object.keys(\n next\n ) as (keyof AccessibilityPreferences)[]) {\n if (next[key] !== prev[key]) {\n changed[key] = next[key] as any;\n }\n }\n window.dispatchEvent(\n new CustomEvent('a11y:pref_changed', {\n detail: {\n previous: prev,\n current: next,\n changed,\n },\n })\n );\n }\n } catch {}\n return next;\n });\n }, []);\n\n const value = React.useMemo(\n () => ({ preferences, setPreferences }),\n [preferences, setPreferences]\n );\n\n return (\n <PreferencesContext value={value}>\n {children}\n {!hydrated ? null : null}\n </PreferencesContext>\n );\n}\n\n// Helper to compute className additions that respect tokens (optional)\nexport function a11yRootClassName() {\n return 'a11y-root';\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lssm/lib.accessibility",
3
- "version": "1.7.4",
3
+ "version": "1.9.1",
4
4
  "scripts": {
5
5
  "build": "bun build:bundle && bun build:types",
6
6
  "build:bundle": "tsdown",
@@ -12,19 +12,19 @@
12
12
  "lint:check": "eslint src"
13
13
  },
14
14
  "dependencies": {
15
- "@lssm/lib.design-system": "^1.7.4",
16
- "@lssm/lib.ui-kit": "^1.7.4",
17
- "@lssm/lib.ui-kit-web": "^1.7.4"
15
+ "@lssm/lib.design-system": "*",
16
+ "@lssm/lib.ui-kit": "*",
17
+ "@lssm/lib.ui-kit-web": "*"
18
18
  },
19
19
  "devDependencies": {
20
20
  "@lssm/tool.tsdown": "*",
21
21
  "@lssm/tool.typescript": "*",
22
- "tsdown": "^0.15.9",
22
+ "tsdown": "^0.16.6",
23
23
  "typescript": "^5.9.3"
24
24
  },
25
25
  "peerDependencies": {
26
- "react": "19.1.0",
27
- "next": "15.5.6"
26
+ "react": "19.2.0",
27
+ "next": "16.0.3"
28
28
  },
29
29
  "type": "module",
30
30
  "main": "./dist/index.js",