@a11ypros/a11y-ui-components 1.0.0 → 1.0.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.
Files changed (217) hide show
  1. package/.storybook/custom.css +69 -0
  2. package/.storybook/main.ts +46 -0
  3. package/.storybook/manager.ts +26 -0
  4. package/.storybook/package.json +6 -0
  5. package/.storybook/preview.tsx +31 -0
  6. package/.storybook/public/logo.png +0 -0
  7. package/.storybook/vite.config.ts +24 -0
  8. package/.storybook/welcome.mdx +97 -0
  9. package/DEPLOYMENT.md +154 -0
  10. package/README.md +227 -0
  11. package/apps/web/app/(docs)/audit/audit.css +269 -0
  12. package/apps/web/app/(docs)/audit/page.tsx +271 -0
  13. package/apps/web/app/(docs)/components/button/page.tsx +49 -0
  14. package/apps/web/app/(docs)/components/form/page.tsx +92 -0
  15. package/apps/web/app/(docs)/components/link/page.tsx +31 -0
  16. package/apps/web/app/(docs)/components/modal/page.tsx +41 -0
  17. package/apps/web/app/(docs)/components/page.tsx +37 -0
  18. package/apps/web/app/(docs)/components/table/page.tsx +54 -0
  19. package/apps/web/app/(docs)/components/tabs/page.tsx +61 -0
  20. package/apps/web/app/(docs)/components/toast/page.tsx +51 -0
  21. package/apps/web/app/api/audit/route.ts +128 -0
  22. package/apps/web/app/favicon.ico +0 -0
  23. package/apps/web/app/layout.tsx +20 -0
  24. package/apps/web/app/page.tsx +17 -0
  25. package/apps/web/app/styles/globals.css +5 -0
  26. package/apps/web/next-env.d.ts +5 -0
  27. package/apps/web/next.config.js +21 -0
  28. package/apps/web/package.json +28 -0
  29. package/apps/web/public/_headers +17 -0
  30. package/apps/web/public/_redirects +31 -0
  31. package/apps/web/public/logo.png +0 -0
  32. package/apps/web/tsconfig.json +29 -0
  33. package/netlify/functions/audit.ts +163 -0
  34. package/netlify.toml +37 -0
  35. package/package.json +30 -58
  36. package/packages/design-system/README.md +252 -0
  37. package/packages/design-system/package.json +68 -0
  38. package/packages/design-system/scripts/copy-css.js +63 -0
  39. package/packages/design-system/src/components/Button/Button.stories.tsx +228 -0
  40. package/packages/design-system/src/components/Button/Button.tsx +137 -0
  41. package/packages/design-system/src/components/Button/index.ts +3 -0
  42. package/packages/design-system/src/components/DataTable/DataTable.stories.tsx +211 -0
  43. package/packages/design-system/src/components/DataTable/DataTable.tsx +293 -0
  44. package/packages/design-system/src/components/DataTable/index.ts +3 -0
  45. package/packages/design-system/src/components/Form/Checkbox.stories.tsx +252 -0
  46. package/packages/design-system/src/components/Form/Checkbox.tsx +114 -0
  47. package/packages/design-system/src/components/Form/Fieldset.stories.tsx +210 -0
  48. package/packages/design-system/src/components/Form/Fieldset.tsx +71 -0
  49. package/packages/design-system/src/components/Form/Input.stories.tsx +164 -0
  50. package/packages/design-system/src/components/Form/Input.tsx +113 -0
  51. package/packages/design-system/src/components/Form/Label.tsx +56 -0
  52. package/packages/design-system/src/components/Form/Radio.stories.tsx +265 -0
  53. package/packages/design-system/src/components/Form/Radio.tsx +147 -0
  54. package/packages/design-system/src/components/Form/Select.stories.tsx +295 -0
  55. package/packages/design-system/src/components/Form/Select.tsx +160 -0
  56. package/packages/design-system/src/components/Form/Textarea.stories.tsx +253 -0
  57. package/packages/design-system/src/components/Form/Textarea.tsx +145 -0
  58. package/packages/design-system/src/components/Form/index.ts +8 -0
  59. package/packages/design-system/src/components/Link/Link.stories.tsx +128 -0
  60. package/packages/design-system/src/components/Link/Link.tsx +117 -0
  61. package/packages/design-system/src/components/Link/index.ts +3 -0
  62. package/packages/design-system/src/components/Modal/Modal.stories.tsx +165 -0
  63. package/packages/design-system/src/components/Modal/Modal.tsx +202 -0
  64. package/packages/design-system/src/components/Modal/index.ts +3 -0
  65. package/packages/design-system/src/components/Tabs/Tabs.stories.tsx +213 -0
  66. package/packages/design-system/src/components/Tabs/Tabs.tsx +248 -0
  67. package/packages/design-system/src/components/Tabs/index.ts +3 -0
  68. package/packages/design-system/src/components/Toast/Toast.stories.tsx +153 -0
  69. package/packages/design-system/src/components/Toast/Toast.tsx +175 -0
  70. package/packages/design-system/src/components/Toast/ToastProvider.tsx +73 -0
  71. package/packages/design-system/src/components/Toast/index.ts +5 -0
  72. package/packages/design-system/src/hooks/useAriaLive.ts +51 -0
  73. package/packages/design-system/src/hooks/useFocusReturn.ts +40 -0
  74. package/packages/design-system/src/hooks/useFocusTrap.ts +82 -0
  75. package/{dist/index.js → packages/design-system/src/index.ts} +4 -0
  76. package/packages/design-system/src/styles/index.ts +3 -0
  77. package/packages/design-system/src/tokens/breakpoints.ts +28 -0
  78. package/packages/design-system/src/tokens/colors.ts +98 -0
  79. package/packages/design-system/src/tokens/index.ts +6 -0
  80. package/packages/design-system/src/tokens/motion.ts +41 -0
  81. package/packages/design-system/src/tokens/spacing.ts +24 -0
  82. package/packages/design-system/src/tokens/theme.ts +19 -0
  83. package/packages/design-system/src/tokens/typography.ts +64 -0
  84. package/packages/design-system/src/utils/aria.ts +108 -0
  85. package/packages/design-system/src/utils/focus.ts +87 -0
  86. package/packages/design-system/src/utils/index.ts +4 -0
  87. package/packages/design-system/src/utils/keyboard.ts +77 -0
  88. package/packages/design-system/tsconfig.json +17 -0
  89. package/public/logo.png +0 -0
  90. package/scripts/fix-storybook-paths.js +53 -0
  91. package/tsconfig.json +20 -0
  92. package/dist/components/Button/Button.d.ts +0 -37
  93. package/dist/components/Button/Button.d.ts.map +0 -1
  94. package/dist/components/Button/Button.js +0 -52
  95. package/dist/components/Button/index.d.ts +0 -3
  96. package/dist/components/Button/index.d.ts.map +0 -1
  97. package/dist/components/Button/index.js +0 -1
  98. package/dist/components/DataTable/DataTable.d.ts +0 -71
  99. package/dist/components/DataTable/DataTable.d.ts.map +0 -1
  100. package/dist/components/DataTable/DataTable.js +0 -122
  101. package/dist/components/DataTable/index.d.ts +0 -3
  102. package/dist/components/DataTable/index.d.ts.map +0 -1
  103. package/dist/components/DataTable/index.js +0 -1
  104. package/dist/components/Form/Checkbox.d.ts +0 -36
  105. package/dist/components/Form/Checkbox.d.ts.map +0 -1
  106. package/dist/components/Form/Checkbox.js +0 -39
  107. package/dist/components/Form/Fieldset.d.ts +0 -33
  108. package/dist/components/Form/Fieldset.d.ts.map +0 -1
  109. package/dist/components/Form/Fieldset.js +0 -34
  110. package/dist/components/Form/Input.d.ts +0 -37
  111. package/dist/components/Form/Input.d.ts.map +0 -1
  112. package/dist/components/Form/Input.js +0 -41
  113. package/dist/components/Form/Label.d.ts +0 -30
  114. package/dist/components/Form/Label.d.ts.map +0 -1
  115. package/dist/components/Form/Label.js +0 -30
  116. package/dist/components/Form/Radio.d.ts +0 -53
  117. package/dist/components/Form/Radio.d.ts.map +0 -1
  118. package/dist/components/Form/Radio.js +0 -39
  119. package/dist/components/Form/Select.d.ts +0 -51
  120. package/dist/components/Form/Select.d.ts.map +0 -1
  121. package/dist/components/Form/Select.js +0 -49
  122. package/dist/components/Form/Textarea.d.ts +0 -44
  123. package/dist/components/Form/Textarea.d.ts.map +0 -1
  124. package/dist/components/Form/Textarea.js +0 -43
  125. package/dist/components/Form/index.d.ts +0 -8
  126. package/dist/components/Form/index.d.ts.map +0 -1
  127. package/dist/components/Form/index.js +0 -7
  128. package/dist/components/Link/Link.d.ts +0 -34
  129. package/dist/components/Link/Link.d.ts.map +0 -1
  130. package/dist/components/Link/Link.js +0 -48
  131. package/dist/components/Link/index.d.ts +0 -3
  132. package/dist/components/Link/index.d.ts.map +0 -1
  133. package/dist/components/Link/index.js +0 -1
  134. package/dist/components/Modal/Modal.d.ts +0 -64
  135. package/dist/components/Modal/Modal.d.ts.map +0 -1
  136. package/dist/components/Modal/Modal.js +0 -108
  137. package/dist/components/Modal/index.d.ts +0 -3
  138. package/dist/components/Modal/index.d.ts.map +0 -1
  139. package/dist/components/Modal/index.js +0 -1
  140. package/dist/components/Tabs/Tabs.d.ts +0 -63
  141. package/dist/components/Tabs/Tabs.d.ts.map +0 -1
  142. package/dist/components/Tabs/Tabs.js +0 -134
  143. package/dist/components/Tabs/index.d.ts +0 -3
  144. package/dist/components/Tabs/index.d.ts.map +0 -1
  145. package/dist/components/Tabs/index.js +0 -1
  146. package/dist/components/Toast/Toast.d.ts +0 -59
  147. package/dist/components/Toast/Toast.d.ts.map +0 -1
  148. package/dist/components/Toast/Toast.js +0 -91
  149. package/dist/components/Toast/ToastProvider.d.ts +0 -22
  150. package/dist/components/Toast/ToastProvider.d.ts.map +0 -1
  151. package/dist/components/Toast/ToastProvider.js +0 -33
  152. package/dist/components/Toast/index.d.ts +0 -5
  153. package/dist/components/Toast/index.d.ts.map +0 -1
  154. package/dist/components/Toast/index.js +0 -2
  155. package/dist/hooks/useAriaLive.d.ts +0 -9
  156. package/dist/hooks/useAriaLive.d.ts.map +0 -1
  157. package/dist/hooks/useAriaLive.js +0 -39
  158. package/dist/hooks/useFocusReturn.d.ts +0 -9
  159. package/dist/hooks/useFocusReturn.d.ts.map +0 -1
  160. package/dist/hooks/useFocusReturn.js +0 -33
  161. package/dist/hooks/useFocusTrap.d.ts +0 -9
  162. package/dist/hooks/useFocusTrap.d.ts.map +0 -1
  163. package/dist/hooks/useFocusTrap.js +0 -68
  164. package/dist/index.d.ts +0 -22
  165. package/dist/index.d.ts.map +0 -1
  166. package/dist/styles/index.d.ts +0 -3
  167. package/dist/styles/index.d.ts.map +0 -1
  168. package/dist/styles/index.js +0 -1
  169. package/dist/tokens/breakpoints.d.ts +0 -25
  170. package/dist/tokens/breakpoints.d.ts.map +0 -1
  171. package/dist/tokens/breakpoints.js +0 -23
  172. package/dist/tokens/colors.d.ts +0 -81
  173. package/dist/tokens/colors.d.ts.map +0 -1
  174. package/dist/tokens/colors.js +0 -86
  175. package/dist/tokens/index.d.ts +0 -6
  176. package/dist/tokens/index.d.ts.map +0 -1
  177. package/dist/tokens/index.js +0 -5
  178. package/dist/tokens/motion.d.ts +0 -30
  179. package/dist/tokens/motion.d.ts.map +0 -1
  180. package/dist/tokens/motion.js +0 -34
  181. package/dist/tokens/spacing.d.ts +0 -22
  182. package/dist/tokens/spacing.d.ts.map +0 -1
  183. package/dist/tokens/spacing.js +0 -20
  184. package/dist/tokens/theme.d.ts +0 -159
  185. package/dist/tokens/theme.d.ts.map +0 -1
  186. package/dist/tokens/theme.js +0 -15
  187. package/dist/tokens/typography.d.ts +0 -45
  188. package/dist/tokens/typography.d.ts.map +0 -1
  189. package/dist/tokens/typography.js +0 -56
  190. package/dist/utils/aria.d.ts +0 -60
  191. package/dist/utils/aria.d.ts.map +0 -1
  192. package/dist/utils/aria.js +0 -86
  193. package/dist/utils/focus.d.ts +0 -30
  194. package/dist/utils/focus.d.ts.map +0 -1
  195. package/dist/utils/focus.js +0 -80
  196. package/dist/utils/index.d.ts +0 -4
  197. package/dist/utils/index.d.ts.map +0 -1
  198. package/dist/utils/index.js +0 -3
  199. package/dist/utils/keyboard.d.ts +0 -38
  200. package/dist/utils/keyboard.d.ts.map +0 -1
  201. package/dist/utils/keyboard.js +0 -59
  202. /package/{dist → packages/design-system/src}/components/Button/Button.css +0 -0
  203. /package/{dist → packages/design-system/src}/components/DataTable/DataTable.css +0 -0
  204. /package/{dist → packages/design-system/src}/components/Form/Checkbox.css +0 -0
  205. /package/{dist → packages/design-system/src}/components/Form/Fieldset.css +0 -0
  206. /package/{dist → packages/design-system/src}/components/Form/Input.css +0 -0
  207. /package/{dist → packages/design-system/src}/components/Form/Label.css +0 -0
  208. /package/{dist → packages/design-system/src}/components/Form/Radio.css +0 -0
  209. /package/{dist → packages/design-system/src}/components/Form/Select.css +0 -0
  210. /package/{dist → packages/design-system/src}/components/Form/Textarea.css +0 -0
  211. /package/{dist → packages/design-system/src}/components/Link/Link.css +0 -0
  212. /package/{dist → packages/design-system/src}/components/Modal/Modal.css +0 -0
  213. /package/{dist → packages/design-system/src}/components/Tabs/Tabs.css +0 -0
  214. /package/{dist → packages/design-system/src}/components/Toast/Toast.css +0 -0
  215. /package/{dist → packages/design-system/src}/components/Toast/ToastProvider.css +0 -0
  216. /package/{dist → packages/design-system/src}/styles/components.css +0 -0
  217. /package/{dist → packages/design-system/src}/styles/global.css +0 -0
@@ -0,0 +1,31 @@
1
+ 'use client'
2
+
3
+ import { Link } from '@a11ypros/a11y-ui-components'
4
+
5
+ export default function LinkPage() {
6
+ return (
7
+ <main style={{ padding: '2rem' }}>
8
+ <h1>Link Component</h1>
9
+ <p>Accessible link component with external link detection.</p>
10
+
11
+ <section style={{ marginTop: '2rem' }}>
12
+ <h2>Examples</h2>
13
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
14
+ <Link href="/components">Internal Link</Link>
15
+ <Link href="https://example.com" external>External Link</Link>
16
+ <Link href="#main-content" skip>Skip Link</Link>
17
+ </div>
18
+ </section>
19
+
20
+ <section style={{ marginTop: '2rem' }}>
21
+ <h2>Accessibility</h2>
22
+ <ul>
23
+ <li>WCAG 2.4.4 Link Purpose: Clear link text or aria-label</li>
24
+ <li>WCAG 2.4.7 Focus Visible: Clear focus indicators</li>
25
+ <li>WCAG 4.1.2 Name, Role, Value: Proper semantic HTML</li>
26
+ </ul>
27
+ </section>
28
+ </main>
29
+ )
30
+ }
31
+
@@ -0,0 +1,41 @@
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { Modal, Button } from '@a11ypros/a11y-ui-components'
5
+
6
+ export default function ModalPage() {
7
+ const [isOpen, setIsOpen] = useState(false)
8
+
9
+ return (
10
+ <main style={{ padding: '2rem' }}>
11
+ <h1>Modal Component</h1>
12
+ <p>Accessible modal dialog with focus trap and keyboard support.</p>
13
+
14
+ <section style={{ marginTop: '2rem' }}>
15
+ <h2>Example</h2>
16
+ <Button onClick={() => setIsOpen(true)}>Open Modal</Button>
17
+ <Modal
18
+ isOpen={isOpen}
19
+ onClose={() => setIsOpen(false)}
20
+ title="Example Modal"
21
+ >
22
+ <p>This is a modal dialog. Press ESC to close or click outside.</p>
23
+ <div style={{ marginTop: '1rem' }}>
24
+ <Button onClick={() => setIsOpen(false)}>Close</Button>
25
+ </div>
26
+ </Modal>
27
+ </section>
28
+
29
+ <section style={{ marginTop: '2rem' }}>
30
+ <h2>Accessibility</h2>
31
+ <ul>
32
+ <li>WCAG 2.1.1 Keyboard: ESC key support, focus trap</li>
33
+ <li>WCAG 2.1.2 No Keyboard Trap: Focus returns to trigger</li>
34
+ <li>WCAG 2.4.3 Focus Order: Focus trapped within modal</li>
35
+ <li>WCAG 4.1.2 Name, Role, Value: ARIA modal pattern</li>
36
+ </ul>
37
+ </section>
38
+ </main>
39
+ )
40
+ }
41
+
@@ -0,0 +1,37 @@
1
+ 'use client'
2
+
3
+ import Link from 'next/link'
4
+ import { Button, Link as DesignSystemLink } from '@a11ypros/a11y-ui-components'
5
+
6
+ export default function ComponentsPage() {
7
+ return (
8
+ <main>
9
+ <h1>Design System Components</h1>
10
+ <p>Browse and test all accessible components in the design system.</p>
11
+
12
+ <nav>
13
+ <h2>Components</h2>
14
+ <ul>
15
+ <li><Link href="/components/button">Button</Link></li>
16
+ <li><Link href="/components/link">Link</Link></li>
17
+ <li><Link href="/components/modal">Modal</Link></li>
18
+ <li><Link href="/components/form">Form Components</Link></li>
19
+ <li><Link href="/components/table">Data Table</Link></li>
20
+ <li><Link href="/components/toast">Toast</Link></li>
21
+ <li><Link href="/components/tabs">Tabs</Link></li>
22
+ </ul>
23
+ </nav>
24
+
25
+ <section>
26
+ <h2>Quick Examples</h2>
27
+ <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap', marginTop: '1rem' }}>
28
+ <Button variant="primary">Primary Button</Button>
29
+ <Button variant="secondary">Secondary Button</Button>
30
+ <Button variant="ghost">Ghost Button</Button>
31
+ <DesignSystemLink href="/audit">Go to Audit Tool</DesignSystemLink>
32
+ </div>
33
+ </section>
34
+ </main>
35
+ )
36
+ }
37
+
@@ -0,0 +1,54 @@
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { DataTable } from '@a11ypros/a11y-ui-components'
5
+
6
+ const sampleData = [
7
+ { id: '1', name: 'John Doe', email: 'john@example.com', role: 'Admin' },
8
+ { id: '2', name: 'Jane Smith', email: 'jane@example.com', role: 'User' },
9
+ { id: '3', name: 'Bob Johnson', email: 'bob@example.com', role: 'User' },
10
+ ]
11
+
12
+ const columns = [
13
+ { key: 'name', header: 'Name', sortable: true },
14
+ { key: 'email', header: 'Email', sortable: true },
15
+ { key: 'role', header: 'Role', sortable: true },
16
+ ]
17
+
18
+ export default function TablePage() {
19
+ const [selectedRows, setSelectedRows] = useState<string[]>([])
20
+ const [sortConfig, setSortConfig] = useState<{ column: string; direction: 'asc' | 'desc' } | undefined>()
21
+
22
+ return (
23
+ <main style={{ padding: '2rem' }}>
24
+ <h1>DataTable Component</h1>
25
+ <p>Accessible data table with keyboard navigation and sorting.</p>
26
+
27
+ <section style={{ marginTop: '2rem' }}>
28
+ <h2>Example</h2>
29
+ <DataTable
30
+ data={sampleData}
31
+ columns={columns}
32
+ getRowId={(row) => row.id}
33
+ selectable
34
+ selectedRows={selectedRows}
35
+ onSelectionChange={setSelectedRows}
36
+ sortConfig={sortConfig}
37
+ onSortChange={(column, direction) => setSortConfig({ column, direction })}
38
+ caption="User list"
39
+ />
40
+ </section>
41
+
42
+ <section style={{ marginTop: '2rem' }}>
43
+ <h2>Accessibility</h2>
44
+ <ul>
45
+ <li>WCAG 1.3.1 Info and Relationships: Semantic table structure</li>
46
+ <li>WCAG 2.1.1 Keyboard: Arrow keys, Home/End navigation</li>
47
+ <li>WCAG 4.1.2 Name, Role, Value: Proper ARIA attributes</li>
48
+ <li>WCAG 4.1.3 Status Messages: Sort announcements</li>
49
+ </ul>
50
+ </section>
51
+ </main>
52
+ )
53
+ }
54
+
@@ -0,0 +1,61 @@
1
+ 'use client'
2
+
3
+ import { Tabs } from '@a11ypros/a11y-ui-components'
4
+
5
+ export default function TabsPage() {
6
+ return (
7
+ <main style={{ padding: '2rem' }}>
8
+ <h1>Tabs Component</h1>
9
+ <p>Accessible tabs component with arrow key navigation.</p>
10
+
11
+ <section style={{ marginTop: '2rem' }}>
12
+ <h2>Example</h2>
13
+ <Tabs
14
+ aria-label="Settings tabs"
15
+ items={[
16
+ {
17
+ id: 'general',
18
+ label: 'General',
19
+ content: (
20
+ <div>
21
+ <h3>General Settings</h3>
22
+ <p>Configure general application settings here.</p>
23
+ </div>
24
+ ),
25
+ },
26
+ {
27
+ id: 'account',
28
+ label: 'Account',
29
+ content: (
30
+ <div>
31
+ <h3>Account Settings</h3>
32
+ <p>Manage your account preferences.</p>
33
+ </div>
34
+ ),
35
+ },
36
+ {
37
+ id: 'privacy',
38
+ label: 'Privacy',
39
+ content: (
40
+ <div>
41
+ <h3>Privacy Settings</h3>
42
+ <p>Control your privacy and data settings.</p>
43
+ </div>
44
+ ),
45
+ },
46
+ ]}
47
+ />
48
+ </section>
49
+
50
+ <section style={{ marginTop: '2rem' }}>
51
+ <h2>Accessibility</h2>
52
+ <ul>
53
+ <li>WCAG 2.1.1 Keyboard: Arrow key navigation, Home/End support</li>
54
+ <li>WCAG 4.1.2 Name, Role, Value: ARIA tabs pattern</li>
55
+ <li>WCAG 2.4.3 Focus Order: Proper focus management</li>
56
+ </ul>
57
+ </section>
58
+ </main>
59
+ )
60
+ }
61
+
@@ -0,0 +1,51 @@
1
+ 'use client'
2
+
3
+ import { ToastProvider, useToast } from '@a11ypros/a11y-ui-components'
4
+ import { Button } from '@a11ypros/a11y-ui-components'
5
+
6
+ function ToastDemo() {
7
+ const { addToast } = useToast()
8
+
9
+ return (
10
+ <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
11
+ <Button onClick={() => addToast({ message: 'Info message', type: 'info' })}>
12
+ Show Info Toast
13
+ </Button>
14
+ <Button onClick={() => addToast({ message: 'Success!', type: 'success' })}>
15
+ Show Success Toast
16
+ </Button>
17
+ <Button onClick={() => addToast({ message: 'Warning message', type: 'warning' })}>
18
+ Show Warning Toast
19
+ </Button>
20
+ <Button onClick={() => addToast({ message: 'Error occurred', type: 'error' })}>
21
+ Show Error Toast
22
+ </Button>
23
+ </div>
24
+ )
25
+ }
26
+
27
+ export default function ToastPage() {
28
+ return (
29
+ <main style={{ padding: '2rem' }}>
30
+ <h1>Toast Component</h1>
31
+ <p>Accessible toast notifications with ARIA live regions.</p>
32
+
33
+ <section style={{ marginTop: '2rem' }}>
34
+ <h2>Examples</h2>
35
+ <ToastProvider>
36
+ <ToastDemo />
37
+ </ToastProvider>
38
+ </section>
39
+
40
+ <section style={{ marginTop: '2rem' }}>
41
+ <h2>Accessibility</h2>
42
+ <ul>
43
+ <li>WCAG 4.1.3 Status Messages: ARIA live region announcements</li>
44
+ <li>WCAG 2.1.1 Keyboard: ESC key support</li>
45
+ <li>WCAG 4.1.2 Name, Role, Value: Proper ARIA attributes</li>
46
+ </ul>
47
+ </section>
48
+ </main>
49
+ )
50
+ }
51
+
@@ -0,0 +1,128 @@
1
+ import { NextRequest, NextResponse } from 'next/server'
2
+ import Anthropic from '@anthropic-ai/sdk'
3
+
4
+ const anthropic = new Anthropic({
5
+ apiKey: process.env.ANTHROPIC_API_KEY || '',
6
+ })
7
+
8
+ export interface AuditIssue {
9
+ wcagSC: string // WCAG Success Criterion (e.g., "1.3.1", "2.4.7")
10
+ severity: 'error' | 'warning' | 'info'
11
+ message: string
12
+ suggestion: string
13
+ codeSuggestion?: string
14
+ }
15
+
16
+ export interface AuditResponse {
17
+ issues: AuditIssue[]
18
+ summary: string
19
+ }
20
+
21
+ const WCAG_PROMPT = `You are an accessibility expert reviewing JSX/React code for WCAG 2.1 and 2.2 compliance.
22
+
23
+ Analyze the provided JSX code snippet and identify accessibility issues. For each issue:
24
+ 1. Map it to the specific WCAG Success Criterion (SC) number (e.g., "1.3.1", "2.4.7", "4.1.2")
25
+ 2. Determine severity: "error" (violates WCAG), "warning" (may cause issues), or "info" (best practice)
26
+ 3. Provide a clear message explaining the issue
27
+ 4. Suggest how to fix it
28
+ 5. If applicable, provide corrected code
29
+
30
+ Focus on:
31
+ - Semantic HTML (1.3.1)
32
+ - Keyboard accessibility (2.1.1, 2.1.2)
33
+ - Focus management (2.4.7)
34
+ - ARIA usage (4.1.2, 4.1.3)
35
+ - Color contrast (1.4.3)
36
+ - Form labels (2.5.3)
37
+ - Status messages (4.1.3)
38
+
39
+ Return your response as a JSON object with this structure:
40
+ {
41
+ "issues": [
42
+ {
43
+ "wcagSC": "2.4.7",
44
+ "severity": "error",
45
+ "message": "Button lacks visible focus indicator",
46
+ "suggestion": "Add focus-visible styles with 2px outline",
47
+ "codeSuggestion": "button:focus-visible { outline: 2px solid #0ea5e9; }"
48
+ }
49
+ ],
50
+ "summary": "Found 3 issues: 2 errors, 1 warning"
51
+ }
52
+
53
+ Only return valid JSON, no markdown formatting.`
54
+
55
+ export async function POST(request: NextRequest) {
56
+ try {
57
+ const { code } = await request.json()
58
+
59
+ if (!code || typeof code !== 'string') {
60
+ return NextResponse.json(
61
+ { error: 'Code snippet is required' },
62
+ { status: 400 }
63
+ )
64
+ }
65
+
66
+ if (!process.env.ANTHROPIC_API_KEY) {
67
+ return NextResponse.json(
68
+ { error: 'ANTHROPIC_API_KEY environment variable is not set' },
69
+ { status: 500 }
70
+ )
71
+ }
72
+
73
+ const message = await anthropic.messages.create({
74
+ model: 'claude-sonnet-4-20250514',
75
+ max_tokens: 4096,
76
+ messages: [
77
+ {
78
+ role: 'user',
79
+ content: `${WCAG_PROMPT}\n\nCode to review:\n\`\`\`jsx\n${code}\n\`\`\``,
80
+ },
81
+ ],
82
+ })
83
+
84
+ const content = message.content[0]
85
+ if (content.type !== 'text') {
86
+ throw new Error('Unexpected response type from Anthropic')
87
+ }
88
+
89
+ // Parse the JSON response
90
+ let auditResult: AuditResponse
91
+ try {
92
+ // Extract JSON from the response (handle markdown code blocks if present)
93
+ let jsonText = content.text.trim()
94
+ if (jsonText.startsWith('```')) {
95
+ jsonText = jsonText.replace(/^```(?:json)?\n/, '').replace(/\n```$/, '')
96
+ }
97
+ auditResult = JSON.parse(jsonText)
98
+ } catch (parseError) {
99
+ // If parsing fails, try to extract JSON from the text
100
+ const jsonMatch = content.text.match(/\{[\s\S]*\}/)
101
+ if (jsonMatch) {
102
+ auditResult = JSON.parse(jsonMatch[0])
103
+ } else {
104
+ throw new Error('Failed to parse audit response as JSON')
105
+ }
106
+ }
107
+
108
+ // Validate response structure
109
+ if (!auditResult.issues || !Array.isArray(auditResult.issues)) {
110
+ auditResult = {
111
+ issues: [],
112
+ summary: 'No issues found or invalid response format',
113
+ }
114
+ }
115
+
116
+ return NextResponse.json(auditResult)
117
+ } catch (error) {
118
+ console.error('Audit API error:', error)
119
+ return NextResponse.json(
120
+ {
121
+ error: 'Failed to perform accessibility audit',
122
+ message: error instanceof Error ? error.message : 'Unknown error',
123
+ },
124
+ { status: 500 }
125
+ )
126
+ }
127
+ }
128
+
Binary file
@@ -0,0 +1,20 @@
1
+ import type { Metadata } from 'next'
2
+ import './styles/globals.css'
3
+
4
+ export const metadata: Metadata = {
5
+ title: 'Accessible Design System',
6
+ description: 'An accessible design system with AI-powered accessibility auditing',
7
+ }
8
+
9
+ export default function RootLayout({
10
+ children,
11
+ }: {
12
+ children: React.ReactNode
13
+ }) {
14
+ return (
15
+ <html lang="en">
16
+ <body>{children}</body>
17
+ </html>
18
+ )
19
+ }
20
+
@@ -0,0 +1,17 @@
1
+ import Link from 'next/link'
2
+
3
+ export default function Home() {
4
+ return (
5
+ <main>
6
+ <nav>
7
+ <ul>
8
+ <li><Link href="/components">Components</Link></li>
9
+ <li><Link href="/audit">AI Audit Assistant</Link></li>
10
+ </ul>
11
+ </nav>
12
+ <h1>Accessible Design System</h1>
13
+ <p>Welcome to the accessible design system documentation.</p>
14
+ </main>
15
+ )
16
+ }
17
+
@@ -0,0 +1,5 @@
1
+ /* Import design system global styles */
2
+ @import '../../../../packages/design-system/src/styles/global.css';
3
+
4
+ /* App-specific global styles */
5
+
@@ -0,0 +1,5 @@
1
+ /// <reference types="next" />
2
+ /// <reference types="next/image-types/global" />
3
+
4
+ // NOTE: This file should not be edited
5
+ // see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.
@@ -0,0 +1,21 @@
1
+ /** @type {import('next').NextConfig} */
2
+ const nextConfig = {
3
+ transpilePackages: ['@a11ypros/a11y-ui-components'],
4
+ // Enable static export for Netlify
5
+ output: 'export',
6
+ // Disable image optimization for static export
7
+ images: {
8
+ unoptimized: true,
9
+ },
10
+ // Increase static page generation timeout (default is 60s)
11
+ staticPageGenerationTimeout: 120,
12
+ webpack: (config) => {
13
+ config.resolve.alias = {
14
+ ...config.resolve.alias,
15
+ }
16
+ return config
17
+ },
18
+ }
19
+
20
+ module.exports = nextConfig
21
+
@@ -0,0 +1,28 @@
1
+ {
2
+ "name": "@apps/web",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "scripts": {
6
+ "dev": "next dev",
7
+ "build": "next build",
8
+ "start": "next start",
9
+ "lint": "next lint"
10
+ },
11
+ "dependencies": {
12
+ "@anthropic-ai/sdk": "^0.20.0",
13
+ "@a11ypros/a11y-ui-components": "*",
14
+ "next": "^14.1.0",
15
+ "react": "^18.2.0",
16
+ "react-dom": "^18.2.0",
17
+ "react-syntax-highlighter": "^15.5.0",
18
+ "prismjs": "^1.29.0"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^20.11.5",
22
+ "@types/react": "^18.2.48",
23
+ "@types/react-dom": "^18.2.18",
24
+ "@types/react-syntax-highlighter": "^15.5.11",
25
+ "typescript": "^5.3.3"
26
+ }
27
+ }
28
+
@@ -0,0 +1,17 @@
1
+ # Headers for Storybook - VERY permissive CSP to allow eval
2
+ # IMPORTANT: Order matters - more specific paths must come first
3
+ # The _headers file is processed AFTER netlify.toml, so this will override any CSP
4
+ f# This CSP works for both local development and production
5
+
6
+ # Storybook root - must come first to match /storybook before catch-all
7
+ # 'unsafe-eval' must be in script-src (not default-src) for Storybook to work
8
+ /storybook
9
+ Content-Security-Policy: default-src 'self' 'unsafe-inline' data: blob: http: https: ws: wss:; script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: http: https:; style-src 'self' 'unsafe-inline' data: http: https:; img-src 'self' data: blob: http: https:; font-src 'self' data: http: https:; connect-src 'self' ws: wss: blob: http: https:; worker-src 'self' blob: http: https:; frame-src 'self' http: https:; frame-ancestors 'self';
10
+
11
+ # Storybook sub-paths
12
+ /storybook/*
13
+ Content-Security-Policy: default-src 'self' 'unsafe-inline' data: blob: http: https: ws: wss:; script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: http: https:; style-src 'self' 'unsafe-inline' data: http: https:; img-src 'self' data: blob: http: https:; font-src 'self' data: http: https:; connect-src 'self' ws: wss: blob: http: https:; worker-src 'self' blob: http: https:; frame-src 'self' http: https:; frame-ancestors 'self';
14
+
15
+ # Storybook static files
16
+ /storybook-static/*
17
+ Content-Security-Policy: default-src 'self' 'unsafe-inline' data: blob: http: https: ws: wss:; script-src 'self' 'unsafe-inline' 'unsafe-eval' data: blob: http: https:; style-src 'self' 'unsafe-inline' data: http: https:; img-src 'self' data: blob: http: https:; font-src 'self' data: http: https:; connect-src 'self' ws: wss: blob: http: https:; worker-src 'self' blob: http: https:; frame-src 'self' http: https:; frame-ancestors 'self';
@@ -0,0 +1,31 @@
1
+ # Storybook redirects - must be in public/ to be included in build
2
+ # IMPORTANT: Order matters! More specific rules must come first
3
+
4
+ # Storybook JSON files (must come before catch-all)
5
+ # Storybook may request these from root
6
+ /index.json /storybook-static/index.json 200
7
+ /project.json /storybook-static/project.json 200
8
+
9
+ # Storybook iframe.html - must come before other Storybook rules
10
+ # Storybook loads components in an iframe with query parameters
11
+ /iframe.html /storybook-static/iframe.html 200
12
+ /storybook/iframe.html /storybook-static/iframe.html 200
13
+
14
+ # Storybook root path
15
+ /storybook /storybook-static/index.html 200
16
+
17
+ # Storybook sub-paths (JS files, assets, etc.)
18
+ # This catches /storybook/sb-manager/runtime.js → /storybook-static/sb-manager/runtime.js
19
+ /storybook/* /storybook-static/:splat 200
20
+
21
+ # Netlify Function for audit API
22
+ /api/audit /.netlify/functions/audit 200
23
+
24
+ # Storybook static files (ensures they're served correctly)
25
+ /storybook-static/* /storybook-static/:splat 200
26
+
27
+ # Next.js SPA fallback (must be last)
28
+ # Note: This will catch everything else, including Storybook query params
29
+ # But Storybook iframe.html should be handled above
30
+ /* /index.html 200
31
+
Binary file
@@ -0,0 +1,29 @@
1
+ {
2
+ "extends": "../../tsconfig.json",
3
+ "compilerOptions": {
4
+ "target": "ES2017",
5
+ "lib": ["dom", "dom.iterable", "esnext"],
6
+ "allowJs": true,
7
+ "skipLibCheck": true,
8
+ "strict": true,
9
+ "noEmit": true,
10
+ "esModuleInterop": true,
11
+ "module": "esnext",
12
+ "moduleResolution": "bundler",
13
+ "resolveJsonModule": true,
14
+ "isolatedModules": true,
15
+ "jsx": "preserve",
16
+ "incremental": true,
17
+ "plugins": [
18
+ {
19
+ "name": "next"
20
+ }
21
+ ],
22
+ "paths": {
23
+ "@a11ypros/a11y-ui-components": ["../../packages/design-system/src"]
24
+ }
25
+ },
26
+ "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
27
+ "exclude": ["node_modules"]
28
+ }
29
+