@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,269 @@
1
+ .audit-page {
2
+ min-height: 100vh;
3
+ padding: var(--spacing-6, 1.5rem);
4
+ background-color: var(--color-background-secondary, #fafafa);
5
+
6
+
7
+ /* Override design system colors to match your brand */
8
+ --color-primary-500: #0e8168; /* Your primary teal color */
9
+ --color-primary-600: #075985; /* Darker shade for hover */
10
+ --color-primary-700: #0369a1; /* Even darker for active */
11
+ }
12
+
13
+ .audit-container {
14
+ max-width: 64rem; /* 1024px */
15
+ margin: 0 auto;
16
+ }
17
+
18
+ .audit-header {
19
+ margin-bottom: var(--spacing-8, 2rem);
20
+ }
21
+
22
+ .audit-header-top {
23
+ display: flex;
24
+ align-items: baseline;
25
+ justify-content: space-between;
26
+ gap: var(--spacing-4, 1rem);
27
+ margin-bottom: var(--spacing-4, 1rem);
28
+ }
29
+
30
+ .audit-header-logo {
31
+ flex-shrink: 0;
32
+ }
33
+
34
+ .audit-header h1 {
35
+ font-size: var(--font-size-3xl, 1.875rem);
36
+ font-weight: var(--font-weight-bold, 700);
37
+ margin-bottom: var(--spacing-4, 1rem);
38
+ color: var(--color-text-primary, #171717);
39
+ }
40
+
41
+ .audit-header p {
42
+ font-size: var(--font-size-lg, 1.125rem);
43
+ color: var(--color-text-secondary, #525252);
44
+ line-height: var(--line-height-relaxed, 1.625);
45
+ }
46
+
47
+ .audit-input-section {
48
+ background-color: var(--color-background-default, #ffffff);
49
+ padding: var(--spacing-6, 1.5rem);
50
+ border-radius: 0.5rem;
51
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
52
+ margin-bottom: var(--spacing-6, 1.5rem);
53
+ }
54
+
55
+ .audit-label {
56
+ display: block;
57
+ font-size: var(--font-size-base, 1rem);
58
+ font-weight: var(--font-weight-semibold, 600);
59
+ color: var(--color-text-primary, #171717);
60
+ margin-bottom: var(--spacing-2, 0.5rem);
61
+ }
62
+
63
+ .audit-textarea {
64
+ width: 100%;
65
+ padding: var(--spacing-3, 0.75rem) var(--spacing-4, 1rem);
66
+ font-family: var(--font-family-mono, Menlo, Monaco, 'Courier New', monospace);
67
+ font-size: var(--font-size-sm, 0.875rem);
68
+ line-height: var(--line-height-relaxed, 1.625);
69
+ color: var(--color-text-primary, #171717);
70
+ background-color: var(--color-background-default, #ffffff);
71
+ border: 1px solid var(--color-border-default, #bbbbbb);
72
+ border-radius: 0.375rem;
73
+ resize: vertical;
74
+ transition: border-color var(--motion-duration-normal, 200ms) var(--motion-easing-ease-out, cubic-bezier(0, 0, 0.2, 1));
75
+ }
76
+
77
+ .audit-page .btn--primary {
78
+ background-color: var(--color-primary-500);
79
+ border-color: var(--color-primary-500);
80
+ }
81
+
82
+ .audit-page .btn--primary:hover:not(:disabled) {
83
+ background-color: var(--color-primary-600);
84
+ border-color: var(--color-primary-600);
85
+ }
86
+
87
+ .audit-page .btn--primary:focus-visible {
88
+ outline-color: var(--color-primary-500);
89
+ box-shadow: 0 0 0 3px rgba(14, 129, 104, 0.2); /* Adjust to match your primary */
90
+ }
91
+
92
+ .audit-textarea:focus {
93
+ outline: none;
94
+ border-color: var(--color-border-focus, #0ea5e9);
95
+ box-shadow: 0 0 0 3px rgba(14, 165, 233, 0.1);
96
+ }
97
+
98
+ .audit-textarea:focus-visible {
99
+ outline: 2px solid var(--color-border-focus, #0ea5e9);
100
+ outline-offset: 2px;
101
+ }
102
+
103
+ .audit-help-text {
104
+ font-size: var(--font-size-sm, 0.875rem);
105
+ color: var(--color-text-secondary, #525252);
106
+ margin-top: var(--spacing-2, 0.5rem);
107
+ margin-bottom: var(--spacing-4, 1rem);
108
+ }
109
+
110
+ .audit-error {
111
+ padding: var(--spacing-4, 1rem);
112
+ background-color: rgba(239, 68, 68, 0.1);
113
+ border: 1px solid var(--color-error-500, #ef4444);
114
+ border-radius: 0.375rem;
115
+ color: var(--color-error-700, #b91c1c);
116
+ margin-bottom: var(--spacing-6, 1.5rem);
117
+ }
118
+
119
+ .audit-results {
120
+ background-color: var(--color-background-default, #ffffff);
121
+ padding: var(--spacing-6, 1.5rem);
122
+ border-radius: 0.5rem;
123
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
124
+ }
125
+
126
+ .audit-results h2 {
127
+ font-size: var(--font-size-2xl, 1.5rem);
128
+ font-weight: var(--font-weight-semibold, 600);
129
+ margin-bottom: var(--spacing-4, 1rem);
130
+ color: var(--color-text-primary, #171717);
131
+ }
132
+
133
+ .audit-summary {
134
+ font-size: var(--font-size-lg, 1.125rem);
135
+ color: var(--color-text-secondary, #525252);
136
+ margin-bottom: var(--spacing-6, 1.5rem);
137
+ padding-bottom: var(--spacing-4, 1rem);
138
+ border-bottom: 1px solid var(--color-border-default, #bbbbbb);
139
+ }
140
+
141
+ .audit-success {
142
+ padding: var(--spacing-6, 1.5rem);
143
+ background-color: rgba(34, 197, 94, 0.1);
144
+ border: 1px solid var(--color-success-500, #22c55e);
145
+ border-radius: 0.375rem;
146
+ color: var(--color-success-700, #15803d);
147
+ text-align: center;
148
+ font-size: var(--font-size-lg, 1.125rem);
149
+ }
150
+
151
+ .audit-issues {
152
+ display: flex;
153
+ flex-direction: column;
154
+ gap: var(--spacing-6, 1.5rem);
155
+ }
156
+
157
+ .audit-sc-group {
158
+ border: 1px solid var(--color-border-default, #bbbbbb);
159
+ border-radius: 0.375rem;
160
+ padding: var(--spacing-4, 1rem);
161
+ }
162
+
163
+ .audit-sc-title {
164
+ font-size: var(--font-size-xl, 1.25rem);
165
+ font-weight: var(--font-weight-semibold, 600);
166
+ color: var(--color-text-primary, #171717);
167
+ margin-bottom: var(--spacing-4, 1rem);
168
+ }
169
+
170
+ .audit-sc-count {
171
+ font-weight: var(--font-weight-normal, 400);
172
+ color: var(--color-text-secondary, #525252);
173
+ }
174
+
175
+ .audit-issue {
176
+ border-left: 4px solid;
177
+ padding: var(--spacing-3, 0.75rem);
178
+ margin-bottom: var(--spacing-3, 0.75rem);
179
+ background-color: var(--color-background-secondary, #fafafa);
180
+ border-radius: 0.25rem;
181
+ }
182
+
183
+ .audit-issue--error {
184
+ border-left-color: var(--color-error-500, #ef4444);
185
+ }
186
+
187
+ .audit-issue--warning {
188
+ border-left-color: var(--color-warning-500, #f59e0b);
189
+ }
190
+
191
+ .audit-issue--info {
192
+ border-left-color: var(--color-primary-500, #0ea5e9);
193
+ }
194
+
195
+ .audit-issue-header {
196
+ width: 100%;
197
+ display: flex;
198
+ align-items: center;
199
+ gap: var(--spacing-3, 0.75rem);
200
+ background: none;
201
+ border: none;
202
+ padding: 0;
203
+ cursor: pointer;
204
+ text-align: left;
205
+ }
206
+
207
+ .audit-severity-badge {
208
+ display: inline-block;
209
+ padding: var(--spacing-1, 0.25rem) var(--spacing-2, 0.5rem);
210
+ border-radius: 0.25rem;
211
+ color: var(--color-white, #ffffff);
212
+ font-size: var(--font-size-xs, 0.75rem);
213
+ font-weight: var(--font-weight-semibold, 600);
214
+ text-transform: uppercase;
215
+ flex-shrink: 0;
216
+ }
217
+
218
+ .audit-issue-message {
219
+ flex: 1;
220
+ font-weight: var(--font-weight-medium, 500);
221
+ color: var(--color-text-primary, #171717);
222
+ }
223
+
224
+ .audit-issue-toggle {
225
+ color: var(--color-text-secondary, #525252);
226
+ font-size: var(--font-size-sm, 0.875rem);
227
+ flex-shrink: 0;
228
+ }
229
+
230
+ .audit-issue-details {
231
+ margin-top: var(--spacing-3, 0.75rem);
232
+ padding-top: var(--spacing-3, 0.75rem);
233
+ border-top: 1px solid var(--color-border-default, #bbbbbb);
234
+ }
235
+
236
+ .audit-issue-suggestion {
237
+ margin-bottom: var(--spacing-3, 0.75rem);
238
+ color: var(--color-text-primary, #171717);
239
+ line-height: var(--line-height-relaxed, 1.625);
240
+ }
241
+
242
+ .audit-issue-code {
243
+ margin-top: var(--spacing-3, 0.75rem);
244
+ }
245
+
246
+ .audit-issue-code strong {
247
+ display: block;
248
+ margin-bottom: var(--spacing-2, 0.5rem);
249
+ color: var(--color-text-primary, #171717);
250
+ }
251
+
252
+ /* High contrast mode support */
253
+ @media (prefers-contrast: high) {
254
+ .audit-issue {
255
+ border-left-width: 6px;
256
+ }
257
+
258
+ .audit-textarea:focus-visible {
259
+ outline-width: 3px;
260
+ }
261
+ }
262
+
263
+ /* Reduced motion */
264
+ @media (prefers-reduced-motion: reduce) {
265
+ .audit-textarea {
266
+ transition: none;
267
+ }
268
+ }
269
+
@@ -0,0 +1,271 @@
1
+ 'use client'
2
+
3
+ import { useState, useEffect } from 'react'
4
+ import dynamic from 'next/dynamic'
5
+ import Image from 'next/image'
6
+ import { Button } from '@a11ypros/a11y-ui-components'
7
+ import './audit.css'
8
+
9
+ // Dynamically import SyntaxHighlighter to avoid SSR/build issues
10
+ const SyntaxHighlighter = dynamic(
11
+ () => import('react-syntax-highlighter').then((mod) => mod.Prism as any),
12
+ { ssr: false }
13
+ )
14
+
15
+ interface AuditIssue {
16
+ wcagSC: string
17
+ severity: 'error' | 'warning' | 'info'
18
+ message: string
19
+ suggestion: string
20
+ codeSuggestion?: string
21
+ }
22
+
23
+ interface AuditResponse {
24
+ issues: AuditIssue[]
25
+ summary: string
26
+ }
27
+
28
+ export default function AuditPage() {
29
+ const [code, setCode] = useState('')
30
+ const [loading, setLoading] = useState(false)
31
+ const [result, setResult] = useState<AuditResponse | null>(null)
32
+ const [error, setError] = useState<string | null>(null)
33
+ const [expandedIssues, setExpandedIssues] = useState<Set<number>>(new Set())
34
+ const [codeStyle, setCodeStyle] = useState<any>(null)
35
+
36
+ // Load the style on client side only
37
+ useEffect(() => {
38
+ import('react-syntax-highlighter/dist/cjs/styles/prism').then((mod) => {
39
+ setCodeStyle(mod.vscDarkPlus)
40
+ })
41
+ }, [])
42
+
43
+ const handleAudit = async () => {
44
+ if (!code.trim()) {
45
+ setError('Please enter JSX code to audit')
46
+ return
47
+ }
48
+
49
+ setLoading(true)
50
+ setError(null)
51
+ setResult(null)
52
+
53
+ try {
54
+ const response = await fetch('/api/audit', {
55
+ method: 'POST',
56
+ headers: {
57
+ 'Content-Type': 'application/json',
58
+ },
59
+ body: JSON.stringify({ code }),
60
+ })
61
+
62
+ if (!response.ok) {
63
+ const errorData = await response.json()
64
+ throw new Error(errorData.error || 'Failed to perform audit')
65
+ }
66
+
67
+ const data: AuditResponse = await response.json()
68
+ setResult(data)
69
+ // Expand all issues by default
70
+ setExpandedIssues(new Set(data.issues.map((_, i) => i)))
71
+ } catch (err) {
72
+ setError(err instanceof Error ? err.message : 'An error occurred')
73
+ } finally {
74
+ setLoading(false)
75
+ }
76
+ }
77
+
78
+ const toggleIssue = (index: number) => {
79
+ const newExpanded = new Set(expandedIssues)
80
+ if (newExpanded.has(index)) {
81
+ newExpanded.delete(index)
82
+ } else {
83
+ newExpanded.add(index)
84
+ }
85
+ setExpandedIssues(newExpanded)
86
+ }
87
+
88
+ const getSeverityColor = (severity: string) => {
89
+ switch (severity) {
90
+ case 'error':
91
+ return 'var(--color-error-500, #ef4444)'
92
+ case 'warning':
93
+ return 'var(--color-warning-500, #f59e0b)'
94
+ case 'info':
95
+ return 'var(--color-primary-500, #0ea5e9)'
96
+ default:
97
+ return 'var(--color-neutral-500, #737373)'
98
+ }
99
+ }
100
+
101
+ const getSeverityLabel = (severity: string) => {
102
+ return severity.charAt(0).toUpperCase() + severity.slice(1)
103
+ }
104
+
105
+ // Group issues by WCAG SC
106
+ const groupedIssues = result?.issues.reduce((acc, issue, index) => {
107
+ if (!acc[issue.wcagSC]) {
108
+ acc[issue.wcagSC] = []
109
+ }
110
+ acc[issue.wcagSC].push({ ...issue, originalIndex: index })
111
+ return acc
112
+ }, {} as Record<string, Array<AuditIssue & { originalIndex: number }>>)
113
+
114
+ return (
115
+ <main className="audit-page">
116
+ <div className="audit-container">
117
+ <header className="audit-header">
118
+ <div className="audit-header-top">
119
+ <h1>AI Accessibility Audit Assistant</h1>
120
+ <div className="audit-header-logo">
121
+ <Image
122
+ src="/logo.png"
123
+ alt="A11y Pros Logo"
124
+ width={150}
125
+ height={25}
126
+ priority
127
+ />
128
+ </div>
129
+
130
+ </div>
131
+ <p>
132
+ Paste your JSX code snippet below to get an AI-powered accessibility review
133
+ based on WCAG 2.1 and 2.2 guidelines.
134
+ </p>
135
+ </header>
136
+
137
+ <div className="audit-input-section">
138
+ <label htmlFor="code-input" className="audit-label">
139
+ JSX Code Snippet
140
+ </label>
141
+ <textarea
142
+ id="code-input"
143
+ className="audit-textarea"
144
+ value={code}
145
+ onChange={(e) => setCode(e.target.value)}
146
+ placeholder="<button onClick={handleClick}>Click me</button>"
147
+ rows={12}
148
+ aria-describedby="code-input-help"
149
+ />
150
+ <p id="code-input-help" className="audit-help-text">
151
+ Enter your JSX/React component code to analyze for accessibility issues.
152
+ </p>
153
+ <Button
154
+ onClick={handleAudit}
155
+ loading={loading}
156
+ disabled={!code.trim() || loading}
157
+ variant="primary"
158
+ size="lg"
159
+ className='audit-button'
160
+ >
161
+ {loading ? 'Auditing...' : 'Run Accessibility Audit'}
162
+ </Button>
163
+ </div>
164
+
165
+ {error && (
166
+ <div className="audit-error" role="alert">
167
+ <strong>Error:</strong> {error}
168
+ </div>
169
+ )}
170
+
171
+ {result && (
172
+ <div className="audit-results">
173
+ <h2>Audit Results</h2>
174
+ <p className="audit-summary">{result.summary}</p>
175
+
176
+ {result.issues.length === 0 ? (
177
+ <div className="audit-success">
178
+ <p>✓ No accessibility issues found!</p>
179
+ </div>
180
+ ) : (
181
+ <div className="audit-issues">
182
+ {groupedIssues &&
183
+ Object.entries(groupedIssues).map(([wcagSC, issues]) => (
184
+ <div key={wcagSC} className="audit-sc-group">
185
+ <h3 className="audit-sc-title">
186
+ WCAG {wcagSC}
187
+ <span className="audit-sc-count">
188
+ {' '}
189
+ ({issues.length} issue{issues.length !== 1 ? 's' : ''})
190
+ </span>
191
+ </h3>
192
+ {issues.map((issue, idx) => {
193
+ const globalIndex = issue.originalIndex
194
+ const isExpanded = expandedIssues.has(globalIndex)
195
+ return (
196
+ <div
197
+ key={globalIndex}
198
+ className={`audit-issue audit-issue--${issue.severity}`}
199
+ >
200
+ <button
201
+ className="audit-issue-header"
202
+ onClick={() => toggleIssue(globalIndex)}
203
+ aria-expanded={isExpanded}
204
+ aria-controls={`issue-${globalIndex}`}
205
+ >
206
+ <span
207
+ className="audit-severity-badge"
208
+ style={{ backgroundColor: getSeverityColor(issue.severity) }}
209
+ >
210
+ {getSeverityLabel(issue.severity)}
211
+ </span>
212
+ <span className="audit-issue-message">{issue.message}</span>
213
+ <span className="audit-issue-toggle" aria-hidden="true">
214
+ {isExpanded ? '▼' : '▶'}
215
+ </span>
216
+ </button>
217
+ {isExpanded && (
218
+ <div
219
+ id={`issue-${globalIndex}`}
220
+ className="audit-issue-details"
221
+ >
222
+ <div className="audit-issue-suggestion">
223
+ <strong>Suggestion:</strong> {issue.suggestion}
224
+ </div>
225
+ {issue.codeSuggestion && (
226
+ <div className="audit-issue-code">
227
+ <strong>Code suggestion:</strong>
228
+ {codeStyle ? (
229
+ // @ts-expect-error - react-syntax-highlighter type issues with dynamic import
230
+ <SyntaxHighlighter
231
+ language="jsx"
232
+ style={codeStyle}
233
+ customStyle={{
234
+ marginTop: '0.5rem',
235
+ borderRadius: '0.375rem',
236
+ fontSize: '0.875rem',
237
+ }}
238
+ >
239
+ {issue.codeSuggestion}
240
+ </SyntaxHighlighter>
241
+ ) : (
242
+ <pre style={{
243
+ padding: '1rem',
244
+ background: '#1e1e1e',
245
+ color: '#d4d4d4',
246
+ borderRadius: '0.375rem',
247
+ marginTop: '0.5rem',
248
+ fontSize: '0.875rem',
249
+ overflow: 'auto'
250
+ }}>
251
+ {issue.codeSuggestion}
252
+ </pre>
253
+ )}
254
+ </div>
255
+ )}
256
+ </div>
257
+ )}
258
+ </div>
259
+ )
260
+ })}
261
+ </div>
262
+ ))}
263
+ </div>
264
+ )}
265
+ </div>
266
+ )}
267
+ </div>
268
+ </main>
269
+ )
270
+ }
271
+
@@ -0,0 +1,49 @@
1
+ 'use client'
2
+
3
+ import { Button } from '@a11ypros/a11y-ui-components'
4
+
5
+ export default function ButtonPage() {
6
+ return (
7
+ <main style={{ padding: '2rem' }}>
8
+ <h1>Button Component</h1>
9
+ <p>Accessible button component with full keyboard support.</p>
10
+
11
+ <section style={{ marginTop: '2rem' }}>
12
+ <h2>Variants</h2>
13
+ <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
14
+ <Button variant="primary">Primary</Button>
15
+ <Button variant="secondary">Secondary</Button>
16
+ <Button variant="ghost">Ghost</Button>
17
+ <Button variant="danger">Danger</Button>
18
+ </div>
19
+ </section>
20
+
21
+ <section style={{ marginTop: '2rem' }}>
22
+ <h2>Sizes</h2>
23
+ <div style={{ display: 'flex', gap: '1rem', alignItems: 'center', flexWrap: 'wrap' }}>
24
+ <Button size="sm">Small</Button>
25
+ <Button size="md">Medium</Button>
26
+ <Button size="lg">Large</Button>
27
+ </div>
28
+ </section>
29
+
30
+ <section style={{ marginTop: '2rem' }}>
31
+ <h2>States</h2>
32
+ <div style={{ display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
33
+ <Button loading>Loading</Button>
34
+ <Button disabled>Disabled</Button>
35
+ </div>
36
+ </section>
37
+
38
+ <section style={{ marginTop: '2rem' }}>
39
+ <h2>Accessibility</h2>
40
+ <ul>
41
+ <li>WCAG 2.1.1 Keyboard: Full keyboard support (Enter/Space)</li>
42
+ <li>WCAG 2.4.7 Focus Visible: Clear focus indicators</li>
43
+ <li>WCAG 4.1.2 Name, Role, Value: Proper ARIA attributes</li>
44
+ </ul>
45
+ </section>
46
+ </main>
47
+ )
48
+ }
49
+
@@ -0,0 +1,92 @@
1
+ 'use client'
2
+
3
+ import { useState } from 'react'
4
+ import { Input, Textarea, Select, Checkbox, Radio, Fieldset, Label } from '@a11ypros/a11y-ui-components'
5
+
6
+ export default function FormPage() {
7
+ const [email, setEmail] = useState('')
8
+ const [message, setMessage] = useState('')
9
+ const [country, setCountry] = useState('')
10
+ const [agree, setAgree] = useState(false)
11
+ const [size, setSize] = useState('')
12
+
13
+ return (
14
+ <main style={{ padding: '2rem' }}>
15
+ <h1>Form Components</h1>
16
+ <p>Accessible form components with proper labels and ARIA attributes.</p>
17
+
18
+ <section style={{ marginTop: '2rem' }}>
19
+ <h2>Input</h2>
20
+ <Input
21
+ label="Email address"
22
+ type="email"
23
+ value={email}
24
+ onChange={(e) => setEmail(e.target.value)}
25
+ placeholder="you@example.com"
26
+ required
27
+ />
28
+ </section>
29
+
30
+ <section style={{ marginTop: '2rem' }}>
31
+ <h2>Textarea</h2>
32
+ <Textarea
33
+ label="Message"
34
+ value={message}
35
+ onChange={(e) => setMessage(e.target.value)}
36
+ maxLength={500}
37
+ showCount
38
+ />
39
+ </section>
40
+
41
+ <section style={{ marginTop: '2rem' }}>
42
+ <h2>Select</h2>
43
+ <Select
44
+ label="Country"
45
+ options={[
46
+ { value: 'us', label: 'United States' },
47
+ { value: 'ca', label: 'Canada' },
48
+ { value: 'uk', label: 'United Kingdom' },
49
+ ]}
50
+ value={country}
51
+ onChange={(e) => setCountry(e.target.value)}
52
+ />
53
+ </section>
54
+
55
+ <section style={{ marginTop: '2rem' }}>
56
+ <h2>Checkbox</h2>
57
+ <Checkbox
58
+ id="agree"
59
+ label="I agree to the terms and conditions"
60
+ checked={agree}
61
+ onChange={(e) => setAgree(e.target.checked)}
62
+ />
63
+ </section>
64
+
65
+ <section style={{ marginTop: '2rem' }}>
66
+ <h2>Radio</h2>
67
+ <Radio
68
+ name="size"
69
+ label="Size"
70
+ options={[
71
+ { value: 's', label: 'Small' },
72
+ { value: 'm', label: 'Medium' },
73
+ { value: 'l', label: 'Large' },
74
+ ]}
75
+ value={size}
76
+ onChange={(e) => setSize(e.target.value)}
77
+ />
78
+ </section>
79
+
80
+ <section style={{ marginTop: '2rem' }}>
81
+ <h2>Accessibility</h2>
82
+ <ul>
83
+ <li>WCAG 1.3.1 Info and Relationships: Proper label-input association</li>
84
+ <li>WCAG 2.5.3 Label in Name: Label text matches accessible name</li>
85
+ <li>WCAG 4.1.2 Name, Role, Value: Proper ARIA attributes</li>
86
+ <li>WCAG 4.1.3 Status Messages: Error messages announced</li>
87
+ </ul>
88
+ </section>
89
+ </main>
90
+ )
91
+ }
92
+