@a11ypros/a11y-ui-components 1.0.1 → 1.0.3
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 +182 -157
- package/dist/components/Button/Button.d.ts +37 -0
- package/dist/components/Button/Button.d.ts.map +1 -0
- package/dist/components/Button/Button.js +52 -0
- package/dist/components/Button/index.d.ts +3 -0
- package/dist/components/Button/index.d.ts.map +1 -0
- package/dist/components/Button/index.js +1 -0
- package/dist/components/DataTable/DataTable.d.ts +71 -0
- package/dist/components/DataTable/DataTable.d.ts.map +1 -0
- package/dist/components/DataTable/DataTable.js +122 -0
- package/dist/components/DataTable/index.d.ts +3 -0
- package/dist/components/DataTable/index.d.ts.map +1 -0
- package/dist/components/DataTable/index.js +1 -0
- package/dist/components/Form/Checkbox.d.ts +36 -0
- package/dist/components/Form/Checkbox.d.ts.map +1 -0
- package/dist/components/Form/Checkbox.js +39 -0
- package/dist/components/Form/Fieldset.d.ts +33 -0
- package/dist/components/Form/Fieldset.d.ts.map +1 -0
- package/dist/components/Form/Fieldset.js +34 -0
- package/dist/components/Form/Input.d.ts +37 -0
- package/dist/components/Form/Input.d.ts.map +1 -0
- package/dist/components/Form/Input.js +41 -0
- package/dist/components/Form/Label.d.ts +30 -0
- package/dist/components/Form/Label.d.ts.map +1 -0
- package/dist/components/Form/Label.js +30 -0
- package/dist/components/Form/Radio.d.ts +53 -0
- package/dist/components/Form/Radio.d.ts.map +1 -0
- package/dist/components/Form/Radio.js +39 -0
- package/dist/components/Form/Select.d.ts +51 -0
- package/dist/components/Form/Select.d.ts.map +1 -0
- package/dist/components/Form/Select.js +49 -0
- package/dist/components/Form/Textarea.d.ts +44 -0
- package/dist/components/Form/Textarea.d.ts.map +1 -0
- package/dist/components/Form/Textarea.js +43 -0
- package/dist/components/Form/index.d.ts +8 -0
- package/dist/components/Form/index.d.ts.map +1 -0
- package/dist/components/Form/index.js +7 -0
- package/dist/components/Link/Link.d.ts +34 -0
- package/dist/components/Link/Link.d.ts.map +1 -0
- package/dist/components/Link/Link.js +48 -0
- package/dist/components/Link/index.d.ts +3 -0
- package/dist/components/Link/index.d.ts.map +1 -0
- package/dist/components/Link/index.js +1 -0
- package/dist/components/Modal/Modal.d.ts +64 -0
- package/dist/components/Modal/Modal.d.ts.map +1 -0
- package/dist/components/Modal/Modal.js +108 -0
- package/dist/components/Modal/index.d.ts +3 -0
- package/dist/components/Modal/index.d.ts.map +1 -0
- package/dist/components/Modal/index.js +1 -0
- package/dist/components/Tabs/Tabs.d.ts +63 -0
- package/dist/components/Tabs/Tabs.d.ts.map +1 -0
- package/dist/components/Tabs/Tabs.js +134 -0
- package/dist/components/Tabs/index.d.ts +3 -0
- package/dist/components/Tabs/index.d.ts.map +1 -0
- package/dist/components/Tabs/index.js +1 -0
- package/dist/components/Toast/Toast.d.ts +59 -0
- package/dist/components/Toast/Toast.d.ts.map +1 -0
- package/dist/components/Toast/Toast.js +91 -0
- package/dist/components/Toast/ToastProvider.d.ts +22 -0
- package/dist/components/Toast/ToastProvider.d.ts.map +1 -0
- package/dist/components/Toast/ToastProvider.js +33 -0
- package/dist/components/Toast/index.d.ts +5 -0
- package/dist/components/Toast/index.d.ts.map +1 -0
- package/dist/components/Toast/index.js +2 -0
- package/dist/hooks/useAriaLive.d.ts +9 -0
- package/dist/hooks/useAriaLive.d.ts.map +1 -0
- package/dist/hooks/useAriaLive.js +39 -0
- package/dist/hooks/useFocusReturn.d.ts +9 -0
- package/dist/hooks/useFocusReturn.d.ts.map +1 -0
- package/dist/hooks/useFocusReturn.js +33 -0
- package/dist/hooks/useFocusTrap.d.ts +9 -0
- package/dist/hooks/useFocusTrap.d.ts.map +1 -0
- package/dist/hooks/useFocusTrap.js +68 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/{packages/design-system/src/index.ts → dist/index.js} +0 -4
- package/dist/styles/index.d.ts +3 -0
- package/dist/styles/index.d.ts.map +1 -0
- package/dist/styles/index.js +1 -0
- package/dist/tokens/breakpoints.d.ts +25 -0
- package/dist/tokens/breakpoints.d.ts.map +1 -0
- package/dist/tokens/breakpoints.js +23 -0
- package/dist/tokens/colors.d.ts +81 -0
- package/dist/tokens/colors.d.ts.map +1 -0
- package/dist/tokens/colors.js +86 -0
- package/dist/tokens/index.d.ts +6 -0
- package/dist/tokens/index.d.ts.map +1 -0
- package/dist/tokens/index.js +5 -0
- package/dist/tokens/motion.d.ts +30 -0
- package/dist/tokens/motion.d.ts.map +1 -0
- package/dist/tokens/motion.js +34 -0
- package/dist/tokens/spacing.d.ts +22 -0
- package/dist/tokens/spacing.d.ts.map +1 -0
- package/dist/tokens/spacing.js +20 -0
- package/dist/tokens/theme.d.ts +159 -0
- package/dist/tokens/theme.d.ts.map +1 -0
- package/dist/tokens/theme.js +15 -0
- package/dist/tokens/typography.d.ts +45 -0
- package/dist/tokens/typography.d.ts.map +1 -0
- package/dist/tokens/typography.js +56 -0
- package/dist/utils/aria.d.ts +60 -0
- package/dist/utils/aria.d.ts.map +1 -0
- package/dist/utils/aria.js +86 -0
- package/dist/utils/focus.d.ts +30 -0
- package/dist/utils/focus.d.ts.map +1 -0
- package/dist/utils/focus.js +80 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +3 -0
- package/dist/utils/keyboard.d.ts +38 -0
- package/dist/utils/keyboard.d.ts.map +1 -0
- package/dist/utils/keyboard.js +59 -0
- package/package.json +49 -31
- package/.storybook/custom.css +0 -69
- package/.storybook/main.ts +0 -46
- package/.storybook/manager.ts +0 -26
- package/.storybook/package.json +0 -6
- package/.storybook/preview.tsx +0 -31
- package/.storybook/public/logo.png +0 -0
- package/.storybook/vite.config.ts +0 -24
- package/.storybook/welcome.mdx +0 -97
- package/DEPLOYMENT.md +0 -154
- package/apps/web/app/(docs)/audit/audit.css +0 -269
- package/apps/web/app/(docs)/audit/page.tsx +0 -271
- package/apps/web/app/(docs)/components/button/page.tsx +0 -49
- package/apps/web/app/(docs)/components/form/page.tsx +0 -92
- package/apps/web/app/(docs)/components/link/page.tsx +0 -31
- package/apps/web/app/(docs)/components/modal/page.tsx +0 -41
- package/apps/web/app/(docs)/components/page.tsx +0 -37
- package/apps/web/app/(docs)/components/table/page.tsx +0 -54
- package/apps/web/app/(docs)/components/tabs/page.tsx +0 -61
- package/apps/web/app/(docs)/components/toast/page.tsx +0 -51
- package/apps/web/app/api/audit/route.ts +0 -128
- package/apps/web/app/favicon.ico +0 -0
- package/apps/web/app/layout.tsx +0 -20
- package/apps/web/app/page.tsx +0 -17
- package/apps/web/app/styles/globals.css +0 -5
- package/apps/web/next-env.d.ts +0 -5
- package/apps/web/next.config.js +0 -21
- package/apps/web/package.json +0 -28
- package/apps/web/public/_headers +0 -17
- package/apps/web/public/_redirects +0 -31
- package/apps/web/public/logo.png +0 -0
- package/apps/web/tsconfig.json +0 -29
- package/netlify/functions/audit.ts +0 -163
- package/netlify.toml +0 -37
- package/packages/design-system/README.md +0 -252
- package/packages/design-system/package.json +0 -68
- package/packages/design-system/scripts/copy-css.js +0 -63
- package/packages/design-system/src/components/Button/Button.stories.tsx +0 -228
- package/packages/design-system/src/components/Button/Button.tsx +0 -137
- package/packages/design-system/src/components/Button/index.ts +0 -3
- package/packages/design-system/src/components/DataTable/DataTable.stories.tsx +0 -211
- package/packages/design-system/src/components/DataTable/DataTable.tsx +0 -293
- package/packages/design-system/src/components/DataTable/index.ts +0 -3
- package/packages/design-system/src/components/Form/Checkbox.stories.tsx +0 -252
- package/packages/design-system/src/components/Form/Checkbox.tsx +0 -114
- package/packages/design-system/src/components/Form/Fieldset.stories.tsx +0 -210
- package/packages/design-system/src/components/Form/Fieldset.tsx +0 -71
- package/packages/design-system/src/components/Form/Input.stories.tsx +0 -164
- package/packages/design-system/src/components/Form/Input.tsx +0 -113
- package/packages/design-system/src/components/Form/Label.tsx +0 -56
- package/packages/design-system/src/components/Form/Radio.stories.tsx +0 -265
- package/packages/design-system/src/components/Form/Radio.tsx +0 -147
- package/packages/design-system/src/components/Form/Select.stories.tsx +0 -295
- package/packages/design-system/src/components/Form/Select.tsx +0 -160
- package/packages/design-system/src/components/Form/Textarea.stories.tsx +0 -253
- package/packages/design-system/src/components/Form/Textarea.tsx +0 -145
- package/packages/design-system/src/components/Form/index.ts +0 -8
- package/packages/design-system/src/components/Link/Link.stories.tsx +0 -128
- package/packages/design-system/src/components/Link/Link.tsx +0 -117
- package/packages/design-system/src/components/Link/index.ts +0 -3
- package/packages/design-system/src/components/Modal/Modal.stories.tsx +0 -165
- package/packages/design-system/src/components/Modal/Modal.tsx +0 -202
- package/packages/design-system/src/components/Modal/index.ts +0 -3
- package/packages/design-system/src/components/Tabs/Tabs.stories.tsx +0 -213
- package/packages/design-system/src/components/Tabs/Tabs.tsx +0 -248
- package/packages/design-system/src/components/Tabs/index.ts +0 -3
- package/packages/design-system/src/components/Toast/Toast.stories.tsx +0 -153
- package/packages/design-system/src/components/Toast/Toast.tsx +0 -175
- package/packages/design-system/src/components/Toast/ToastProvider.tsx +0 -73
- package/packages/design-system/src/components/Toast/index.ts +0 -5
- package/packages/design-system/src/hooks/useAriaLive.ts +0 -51
- package/packages/design-system/src/hooks/useFocusReturn.ts +0 -40
- package/packages/design-system/src/hooks/useFocusTrap.ts +0 -82
- package/packages/design-system/src/styles/index.ts +0 -3
- package/packages/design-system/src/tokens/breakpoints.ts +0 -28
- package/packages/design-system/src/tokens/colors.ts +0 -98
- package/packages/design-system/src/tokens/index.ts +0 -6
- package/packages/design-system/src/tokens/motion.ts +0 -41
- package/packages/design-system/src/tokens/spacing.ts +0 -24
- package/packages/design-system/src/tokens/theme.ts +0 -19
- package/packages/design-system/src/tokens/typography.ts +0 -64
- package/packages/design-system/src/utils/aria.ts +0 -108
- package/packages/design-system/src/utils/focus.ts +0 -87
- package/packages/design-system/src/utils/index.ts +0 -4
- package/packages/design-system/src/utils/keyboard.ts +0 -77
- package/packages/design-system/tsconfig.json +0 -17
- package/public/logo.png +0 -0
- package/scripts/fix-storybook-paths.js +0 -53
- package/tsconfig.json +0 -20
- /package/{packages/design-system/src → dist}/components/Button/Button.css +0 -0
- /package/{packages/design-system/src → dist}/components/DataTable/DataTable.css +0 -0
- /package/{packages/design-system/src → dist}/components/Form/Checkbox.css +0 -0
- /package/{packages/design-system/src → dist}/components/Form/Fieldset.css +0 -0
- /package/{packages/design-system/src → dist}/components/Form/Input.css +0 -0
- /package/{packages/design-system/src → dist}/components/Form/Label.css +0 -0
- /package/{packages/design-system/src → dist}/components/Form/Radio.css +0 -0
- /package/{packages/design-system/src → dist}/components/Form/Select.css +0 -0
- /package/{packages/design-system/src → dist}/components/Form/Textarea.css +0 -0
- /package/{packages/design-system/src → dist}/components/Link/Link.css +0 -0
- /package/{packages/design-system/src → dist}/components/Modal/Modal.css +0 -0
- /package/{packages/design-system/src → dist}/components/Tabs/Tabs.css +0 -0
- /package/{packages/design-system/src → dist}/components/Toast/Toast.css +0 -0
- /package/{packages/design-system/src → dist}/components/Toast/ToastProvider.css +0 -0
- /package/{packages/design-system/src → dist}/styles/components.css +0 -0
- /package/{packages/design-system/src → dist}/styles/global.css +0 -0
package/DEPLOYMENT.md
DELETED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# Deployment Guide for ui.a11ypros.com
|
|
2
|
-
|
|
3
|
-
This guide covers deploying the component library and Storybook to Netlify at `ui.a11ypros.com`.
|
|
4
|
-
|
|
5
|
-
## What's Configured
|
|
6
|
-
|
|
7
|
-
✅ **Netlify Configuration** (`netlify.toml`)
|
|
8
|
-
- Builds Storybook and Next.js app
|
|
9
|
-
- Serves Storybook at `/storybook`
|
|
10
|
-
- Routes API calls to Netlify Functions
|
|
11
|
-
|
|
12
|
-
✅ **Next.js Static Export**
|
|
13
|
-
- Configured for static site generation
|
|
14
|
-
- Outputs to `apps/web/out`
|
|
15
|
-
|
|
16
|
-
✅ **Storybook Build**
|
|
17
|
-
- Builds to `apps/web/public/storybook-static`
|
|
18
|
-
- Accessible at `ui.a11ypros.com/storybook`
|
|
19
|
-
|
|
20
|
-
✅ **Netlify Function**
|
|
21
|
-
- Audit API converted to serverless function
|
|
22
|
-
- Located at `netlify/functions/audit.ts`
|
|
23
|
-
- Accessible via `/api/audit` (auto-redirected)
|
|
24
|
-
|
|
25
|
-
## Deployment Steps
|
|
26
|
-
|
|
27
|
-
### 1. Connect Repository to Netlify
|
|
28
|
-
|
|
29
|
-
1. Go to [Netlify Dashboard](https://app.netlify.com)
|
|
30
|
-
2. Click "Add new site" → "Import an existing project"
|
|
31
|
-
3. Connect your Git provider and select this repository
|
|
32
|
-
|
|
33
|
-
### 2. Configure Build Settings
|
|
34
|
-
|
|
35
|
-
Netlify should auto-detect `netlify.toml`, but verify:
|
|
36
|
-
- **Base directory:** `.` (root)
|
|
37
|
-
- **Build command:** `npm install && npm run build-storybook && npm run build --workspace=apps/web`
|
|
38
|
-
- **Publish directory:** `apps/web/out`
|
|
39
|
-
|
|
40
|
-
### 3. Set Environment Variables
|
|
41
|
-
|
|
42
|
-
Go to **Site settings → Environment variables** and add:
|
|
43
|
-
- `ANTHROPIC_API_KEY` - Your Anthropic API key for the audit function
|
|
44
|
-
|
|
45
|
-
### 4. Configure Custom Domain
|
|
46
|
-
|
|
47
|
-
1. Go to **Site settings → Domain management**
|
|
48
|
-
2. Click "Add custom domain"
|
|
49
|
-
3. Enter `ui.a11ypros.com`
|
|
50
|
-
4. Follow Netlify's DNS instructions:
|
|
51
|
-
- Add a **CNAME** record at your domain provider:
|
|
52
|
-
```
|
|
53
|
-
Type: CNAME
|
|
54
|
-
Name: ui
|
|
55
|
-
Value: [your-netlify-site-name].netlify.app
|
|
56
|
-
```
|
|
57
|
-
5. Wait for DNS propagation (5-60 minutes)
|
|
58
|
-
|
|
59
|
-
### 5. Deploy
|
|
60
|
-
|
|
61
|
-
- Netlify will automatically deploy on every push to your main branch
|
|
62
|
-
- Or click "Deploy site" to deploy manually
|
|
63
|
-
|
|
64
|
-
## URLs After Deployment
|
|
65
|
-
|
|
66
|
-
- **Main App:** `https://ui.a11ypros.com`
|
|
67
|
-
- **Storybook:** `https://ui.a11ypros.com/storybook`
|
|
68
|
-
- **Audit API:** `https://ui.a11ypros.com/api/audit` (POST)
|
|
69
|
-
|
|
70
|
-
## How It Works
|
|
71
|
-
|
|
72
|
-
1. **Build Process:**
|
|
73
|
-
- Installs dependencies
|
|
74
|
-
- Builds Storybook → `apps/web/public/storybook-static`
|
|
75
|
-
- Builds Next.js app → `apps/web/out` (includes Storybook from public/)
|
|
76
|
-
|
|
77
|
-
2. **Routing:**
|
|
78
|
-
- `/storybook/*` → Served from `/storybook-static/*`
|
|
79
|
-
- `/api/audit` → Redirected to `/.netlify/functions/audit`
|
|
80
|
-
- `/*` → Next.js SPA routes
|
|
81
|
-
|
|
82
|
-
3. **Netlify Function:**
|
|
83
|
-
- Serverless function handles the audit API
|
|
84
|
-
- Uses `ANTHROPIC_API_KEY` environment variable
|
|
85
|
-
- Supports CORS for cross-origin requests
|
|
86
|
-
|
|
87
|
-
## Troubleshooting
|
|
88
|
-
|
|
89
|
-
### Build Fails
|
|
90
|
-
|
|
91
|
-
- Check Node.js version (should be 18+)
|
|
92
|
-
- Verify all dependencies are in `package.json`
|
|
93
|
-
- Check build logs in Netlify dashboard
|
|
94
|
-
|
|
95
|
-
### Storybook Not Loading
|
|
96
|
-
|
|
97
|
-
- Verify Storybook build completed successfully
|
|
98
|
-
- Check that `apps/web/public/storybook-static` exists
|
|
99
|
-
- Verify redirect rules in `netlify.toml`
|
|
100
|
-
|
|
101
|
-
### API Not Working
|
|
102
|
-
|
|
103
|
-
- Ensure `ANTHROPIC_API_KEY` is set in Netlify environment variables
|
|
104
|
-
- Check Netlify Function logs in dashboard
|
|
105
|
-
- Verify the function is deployed (check Functions tab)
|
|
106
|
-
|
|
107
|
-
### DNS Issues
|
|
108
|
-
|
|
109
|
-
- Wait for DNS propagation (can take up to 48 hours, usually 5-60 minutes)
|
|
110
|
-
- Verify CNAME record is correct
|
|
111
|
-
- Check DNS propagation with `dig ui.a11ypros.com`
|
|
112
|
-
|
|
113
|
-
## Local Testing
|
|
114
|
-
|
|
115
|
-
Test the build locally before deploying:
|
|
116
|
-
|
|
117
|
-
```bash
|
|
118
|
-
# Install dependencies
|
|
119
|
-
npm install
|
|
120
|
-
|
|
121
|
-
# Build Storybook
|
|
122
|
-
npm run build-storybook
|
|
123
|
-
|
|
124
|
-
# Build Next.js app
|
|
125
|
-
npm run build --workspace=apps/web
|
|
126
|
-
|
|
127
|
-
# Test locally (optional)
|
|
128
|
-
npx serve apps/web/out
|
|
129
|
-
```
|
|
130
|
-
|
|
131
|
-
Visit `http://localhost:3000` for the app and `http://localhost:3000/storybook` for Storybook.
|
|
132
|
-
|
|
133
|
-
## File Structure
|
|
134
|
-
|
|
135
|
-
```
|
|
136
|
-
├── netlify.toml # Netlify configuration
|
|
137
|
-
├── netlify/
|
|
138
|
-
│ └── functions/
|
|
139
|
-
│ └── audit.ts # Netlify Function for audit API
|
|
140
|
-
├── apps/
|
|
141
|
-
│ └── web/
|
|
142
|
-
│ ├── public/
|
|
143
|
-
│ │ └── storybook-static/ # Storybook build output
|
|
144
|
-
│ └── out/ # Next.js build output
|
|
145
|
-
└── package.json # Root dependencies
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## Notes
|
|
149
|
-
|
|
150
|
-
- The Next.js app uses static export, so no server-side features
|
|
151
|
-
- API routes are converted to Netlify Functions
|
|
152
|
-
- Storybook is served as static files alongside the app
|
|
153
|
-
- All routes are client-side (SPA mode)
|
|
154
|
-
|
|
@@ -1,269 +0,0 @@
|
|
|
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
|
-
|
|
@@ -1,271 +0,0 @@
|
|
|
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
|
-
|