@a11ypros/a11y-ui-components 1.0.5 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.storybook/custom.css +85 -0
- package/.storybook/main.ts +51 -0
- package/.storybook/manager-head.html +2 -0
- package/.storybook/manager.ts +31 -0
- package/.storybook/package.json +6 -0
- package/.storybook/preview.tsx +74 -0
- package/.storybook/public/logo.png +0 -0
- package/.storybook/vite.config.ts +24 -0
- package/.storybook/welcome.mdx +265 -0
- package/DEPLOYMENT.md +154 -0
- package/README.md +230 -287
- package/apps/web/app/(docs)/audit/audit.css +269 -0
- package/apps/web/app/(docs)/audit/page.tsx +271 -0
- package/apps/web/app/(docs)/components/button/page.tsx +49 -0
- package/apps/web/app/(docs)/components/form/page.tsx +92 -0
- package/apps/web/app/(docs)/components/link/page.tsx +31 -0
- package/apps/web/app/(docs)/components/modal/page.tsx +41 -0
- package/apps/web/app/(docs)/components/page.tsx +37 -0
- package/apps/web/app/(docs)/components/table/page.tsx +54 -0
- package/apps/web/app/(docs)/components/tabs/page.tsx +61 -0
- package/apps/web/app/api/audit/route.ts +128 -0
- package/apps/web/app/favicon.ico +0 -0
- package/apps/web/app/layout.tsx +20 -0
- package/apps/web/app/page.tsx +17 -0
- package/apps/web/app/styles/globals.css +5 -0
- package/apps/web/next-env.d.ts +6 -0
- package/apps/web/next.config.js +21 -0
- package/apps/web/package.json +29 -0
- package/apps/web/public/_headers +17 -0
- package/apps/web/public/_redirects +31 -0
- package/apps/web/public/logo.png +0 -0
- package/apps/web/tsconfig.json +29 -0
- package/netlify/functions/audit.ts +163 -0
- package/netlify.toml +37 -0
- package/package.json +28 -50
- package/packages/design-system/README.md +376 -0
- package/packages/design-system/create-tests.sh +215 -0
- package/packages/design-system/package.json +70 -0
- package/packages/design-system/scripts/copy-css.js +63 -0
- package/packages/design-system/src/components/Accordion/Accordion.stories.tsx +174 -0
- package/packages/design-system/src/components/Accordion/Accordion.tsx +211 -0
- package/packages/design-system/src/components/Accordion/index.ts +2 -0
- package/packages/design-system/src/components/Button/Button.stories.tsx +240 -0
- package/packages/design-system/src/components/Button/Button.test.tsx +148 -0
- package/packages/design-system/src/components/Button/Button.tsx +137 -0
- package/packages/design-system/src/components/Button/index.ts +3 -0
- package/packages/design-system/src/components/DataTable/DataTable.stories.tsx +220 -0
- package/packages/design-system/src/components/DataTable/DataTable.tsx +299 -0
- package/packages/design-system/src/components/DataTable/index.ts +3 -0
- package/packages/design-system/src/components/Form/Checkbox.stories.tsx +261 -0
- package/packages/design-system/src/components/Form/Checkbox.tsx +114 -0
- package/packages/design-system/src/components/Form/Fieldset.stories.tsx +219 -0
- package/packages/design-system/src/components/Form/Fieldset.tsx +71 -0
- package/packages/design-system/src/components/Form/Input.stories.tsx +173 -0
- package/packages/design-system/src/components/Form/Input.test.tsx +110 -0
- package/packages/design-system/src/components/Form/Input.tsx +113 -0
- package/packages/design-system/src/components/Form/Label.tsx +56 -0
- package/packages/design-system/src/components/Form/Radio.stories.tsx +274 -0
- package/packages/design-system/src/components/Form/Radio.tsx +147 -0
- package/packages/design-system/src/components/Form/Select.stories.tsx +304 -0
- package/packages/design-system/src/components/Form/Select.tsx +160 -0
- package/packages/design-system/src/components/Form/Textarea.stories.tsx +262 -0
- package/packages/design-system/src/components/Form/Textarea.tsx +145 -0
- package/packages/design-system/src/components/Form/index.ts +8 -0
- package/packages/design-system/src/components/Link/Link.stories.tsx +137 -0
- package/packages/design-system/src/components/Link/Link.tsx +112 -0
- package/packages/design-system/src/components/Link/index.ts +3 -0
- package/packages/design-system/src/components/Modal/Modal.stories.tsx +174 -0
- package/packages/design-system/src/components/Modal/Modal.test.tsx +173 -0
- package/packages/design-system/src/components/Modal/Modal.tsx +208 -0
- package/packages/design-system/src/components/Modal/index.ts +3 -0
- package/packages/design-system/src/components/Tabs/Tabs.stories.tsx +222 -0
- package/packages/design-system/src/components/Tabs/Tabs.tsx +254 -0
- package/packages/design-system/src/components/Tabs/index.ts +3 -0
- package/packages/design-system/src/hooks/useAriaLive.test.ts +110 -0
- package/packages/design-system/src/hooks/useAriaLive.ts +51 -0
- package/packages/design-system/src/hooks/useFocusReturn.test.ts +123 -0
- package/packages/design-system/src/hooks/useFocusReturn.ts +40 -0
- package/packages/design-system/src/hooks/useFocusTrap.test.ts +190 -0
- package/packages/design-system/src/hooks/useFocusTrap.ts +82 -0
- package/{dist/index.js → packages/design-system/src/index.ts} +4 -2
- package/{dist → packages/design-system/src}/styles/components.css +0 -4
- package/packages/design-system/src/styles/index.ts +3 -0
- package/packages/design-system/src/test-utils.tsx +71 -0
- package/packages/design-system/src/tokens/breakpoints.ts +28 -0
- package/packages/design-system/src/tokens/colors.ts +98 -0
- package/packages/design-system/src/tokens/index.ts +6 -0
- package/packages/design-system/src/tokens/motion.ts +41 -0
- package/packages/design-system/src/tokens/spacing.ts +24 -0
- package/packages/design-system/src/tokens/theme.ts +19 -0
- package/packages/design-system/src/tokens/typography.ts +64 -0
- package/packages/design-system/src/utils/aria.test.ts +215 -0
- package/packages/design-system/src/utils/aria.ts +108 -0
- package/packages/design-system/src/utils/focus.test.ts +175 -0
- package/packages/design-system/src/utils/focus.ts +87 -0
- package/packages/design-system/src/utils/index.ts +4 -0
- package/packages/design-system/src/utils/keyboard.test.ts +184 -0
- package/packages/design-system/src/utils/keyboard.ts +77 -0
- package/packages/design-system/tsconfig.json +17 -0
- package/packages/design-system/vitest.config +0 -0
- package/packages/design-system/vitest.config.ts +34 -0
- package/public/logo.png +0 -0
- package/scripts/fix-storybook-paths.js +53 -0
- package/tsconfig.json +20 -0
- package/dist/components/Accordion/Accordion.d.ts +0 -76
- package/dist/components/Accordion/Accordion.d.ts.map +0 -1
- package/dist/components/Accordion/Accordion.js +0 -91
- package/dist/components/Accordion/index.d.ts +0 -3
- package/dist/components/Accordion/index.d.ts.map +0 -1
- package/dist/components/Accordion/index.js +0 -1
- package/dist/components/Button/Button.d.ts +0 -37
- package/dist/components/Button/Button.d.ts.map +0 -1
- package/dist/components/Button/Button.js +0 -52
- package/dist/components/Button/index.d.ts +0 -3
- package/dist/components/Button/index.d.ts.map +0 -1
- package/dist/components/Button/index.js +0 -1
- package/dist/components/DataTable/DataTable.d.ts +0 -75
- package/dist/components/DataTable/DataTable.d.ts.map +0 -1
- package/dist/components/DataTable/DataTable.js +0 -122
- package/dist/components/DataTable/index.d.ts +0 -3
- package/dist/components/DataTable/index.d.ts.map +0 -1
- package/dist/components/DataTable/index.js +0 -1
- package/dist/components/Form/Checkbox.d.ts +0 -36
- package/dist/components/Form/Checkbox.d.ts.map +0 -1
- package/dist/components/Form/Checkbox.js +0 -39
- package/dist/components/Form/Fieldset.d.ts +0 -33
- package/dist/components/Form/Fieldset.d.ts.map +0 -1
- package/dist/components/Form/Fieldset.js +0 -34
- package/dist/components/Form/Input.d.ts +0 -37
- package/dist/components/Form/Input.d.ts.map +0 -1
- package/dist/components/Form/Input.js +0 -41
- package/dist/components/Form/Label.d.ts +0 -30
- package/dist/components/Form/Label.d.ts.map +0 -1
- package/dist/components/Form/Label.js +0 -30
- package/dist/components/Form/Radio.d.ts +0 -53
- package/dist/components/Form/Radio.d.ts.map +0 -1
- package/dist/components/Form/Radio.js +0 -39
- package/dist/components/Form/Select.d.ts +0 -51
- package/dist/components/Form/Select.d.ts.map +0 -1
- package/dist/components/Form/Select.js +0 -49
- package/dist/components/Form/Textarea.d.ts +0 -44
- package/dist/components/Form/Textarea.d.ts.map +0 -1
- package/dist/components/Form/Textarea.js +0 -43
- package/dist/components/Form/index.d.ts +0 -8
- package/dist/components/Form/index.d.ts.map +0 -1
- package/dist/components/Form/index.js +0 -7
- package/dist/components/Link/Link.d.ts +0 -34
- package/dist/components/Link/Link.d.ts.map +0 -1
- package/dist/components/Link/Link.js +0 -48
- package/dist/components/Link/index.d.ts +0 -3
- package/dist/components/Link/index.d.ts.map +0 -1
- package/dist/components/Link/index.js +0 -1
- package/dist/components/Modal/Modal.d.ts +0 -68
- package/dist/components/Modal/Modal.d.ts.map +0 -1
- package/dist/components/Modal/Modal.js +0 -108
- package/dist/components/Modal/index.d.ts +0 -3
- package/dist/components/Modal/index.d.ts.map +0 -1
- package/dist/components/Modal/index.js +0 -1
- package/dist/components/Tabs/Tabs.d.ts +0 -67
- package/dist/components/Tabs/Tabs.d.ts.map +0 -1
- package/dist/components/Tabs/Tabs.js +0 -134
- package/dist/components/Tabs/index.d.ts +0 -3
- package/dist/components/Tabs/index.d.ts.map +0 -1
- package/dist/components/Tabs/index.js +0 -1
- package/dist/components/Toast/Toast.css +0 -100
- package/dist/components/Toast/Toast.d.ts +0 -63
- package/dist/components/Toast/Toast.d.ts.map +0 -1
- package/dist/components/Toast/Toast.js +0 -92
- package/dist/components/Toast/ToastProvider.css +0 -48
- package/dist/components/Toast/ToastProvider.d.ts +0 -22
- package/dist/components/Toast/ToastProvider.d.ts.map +0 -1
- package/dist/components/Toast/ToastProvider.js +0 -33
- package/dist/components/Toast/index.d.ts +0 -5
- package/dist/components/Toast/index.d.ts.map +0 -1
- package/dist/components/Toast/index.js +0 -2
- package/dist/hooks/useAriaLive.d.ts +0 -9
- package/dist/hooks/useAriaLive.d.ts.map +0 -1
- package/dist/hooks/useAriaLive.js +0 -39
- package/dist/hooks/useFocusReturn.d.ts +0 -9
- package/dist/hooks/useFocusReturn.d.ts.map +0 -1
- package/dist/hooks/useFocusReturn.js +0 -33
- package/dist/hooks/useFocusTrap.d.ts +0 -9
- package/dist/hooks/useFocusTrap.d.ts.map +0 -1
- package/dist/hooks/useFocusTrap.js +0 -68
- package/dist/index.d.ts +0 -23
- package/dist/index.d.ts.map +0 -1
- package/dist/styles/index.d.ts +0 -3
- package/dist/styles/index.d.ts.map +0 -1
- package/dist/styles/index.js +0 -1
- package/dist/tokens/breakpoints.d.ts +0 -25
- package/dist/tokens/breakpoints.d.ts.map +0 -1
- package/dist/tokens/breakpoints.js +0 -23
- package/dist/tokens/colors.d.ts +0 -81
- package/dist/tokens/colors.d.ts.map +0 -1
- package/dist/tokens/colors.js +0 -86
- package/dist/tokens/index.d.ts +0 -6
- package/dist/tokens/index.d.ts.map +0 -1
- package/dist/tokens/index.js +0 -5
- package/dist/tokens/motion.d.ts +0 -30
- package/dist/tokens/motion.d.ts.map +0 -1
- package/dist/tokens/motion.js +0 -34
- package/dist/tokens/spacing.d.ts +0 -22
- package/dist/tokens/spacing.d.ts.map +0 -1
- package/dist/tokens/spacing.js +0 -20
- package/dist/tokens/theme.d.ts +0 -159
- package/dist/tokens/theme.d.ts.map +0 -1
- package/dist/tokens/theme.js +0 -15
- package/dist/tokens/typography.d.ts +0 -45
- package/dist/tokens/typography.d.ts.map +0 -1
- package/dist/tokens/typography.js +0 -56
- package/dist/utils/aria.d.ts +0 -60
- package/dist/utils/aria.d.ts.map +0 -1
- package/dist/utils/aria.js +0 -86
- package/dist/utils/focus.d.ts +0 -30
- package/dist/utils/focus.d.ts.map +0 -1
- package/dist/utils/focus.js +0 -80
- package/dist/utils/index.d.ts +0 -4
- package/dist/utils/index.d.ts.map +0 -1
- package/dist/utils/index.js +0 -3
- package/dist/utils/keyboard.d.ts +0 -38
- package/dist/utils/keyboard.d.ts.map +0 -1
- package/dist/utils/keyboard.js +0 -59
- /package/{dist → packages/design-system/src}/components/Accordion/Accordion.css +0 -0
- /package/{dist → packages/design-system/src}/components/Button/Button.css +0 -0
- /package/{dist → packages/design-system/src}/components/DataTable/DataTable.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Checkbox.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Fieldset.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Input.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Label.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Radio.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Select.css +0 -0
- /package/{dist → packages/design-system/src}/components/Form/Textarea.css +0 -0
- /package/{dist → packages/design-system/src}/components/Link/Link.css +0 -0
- /package/{dist → packages/design-system/src}/components/Modal/Modal.css +0 -0
- /package/{dist → packages/design-system/src}/components/Tabs/Tabs.css +0 -0
- /package/{dist → packages/design-system/src}/styles/global.css +0 -0
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/* Custom Storybook styles */
|
|
2
|
+
|
|
3
|
+
/* Theme backgrounds for docs view */
|
|
4
|
+
[data-theme="dark"] .docs-story,
|
|
5
|
+
[data-theme="dark"] .sbdocs-preview {
|
|
6
|
+
background-color: #171717 !important;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
[data-theme="light"] .docs-story,
|
|
10
|
+
[data-theme="light"] .sbdocs-preview {
|
|
11
|
+
background-color: #ffffff !important;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/* Override Storybook's default bright blue link color */
|
|
15
|
+
.sbdocs a,
|
|
16
|
+
.sbdocs a:link,
|
|
17
|
+
.docs-story a,
|
|
18
|
+
.docs-story a:link {
|
|
19
|
+
color: #0e8168 !important;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
.sbdocs a:hover,
|
|
23
|
+
.docs-story a:hover {
|
|
24
|
+
color: #075985 !important;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.sbdocs a:visited,
|
|
28
|
+
.docs-story a:visited {
|
|
29
|
+
color: #0369a1 !important;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/* Fix controls pane button hover states - ensure text is visible */
|
|
33
|
+
.sb-controls button:hover,
|
|
34
|
+
.sb-controls button:focus,
|
|
35
|
+
[data-testid="control-buttons-container"] button:hover,
|
|
36
|
+
[data-testid="control-buttons-container"] button:focus,
|
|
37
|
+
button[aria-label*="Set"]:hover,
|
|
38
|
+
button[aria-label*="Set"]:focus,
|
|
39
|
+
button[aria-label*="Reset"]:hover,
|
|
40
|
+
button[aria-label*="Reset"]:focus,
|
|
41
|
+
[id*="storybook-panel"] button:hover,
|
|
42
|
+
[id*="storybook-panel"] button:focus {
|
|
43
|
+
color: #171717 !important;
|
|
44
|
+
background-color: #f5f5f5 !important;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/* Ensure button text is visible in all states */
|
|
48
|
+
.sb-controls button,
|
|
49
|
+
[data-testid="control-buttons-container"] button,
|
|
50
|
+
button[aria-label*="Set"],
|
|
51
|
+
button[aria-label*="Reset"],
|
|
52
|
+
[id*="storybook-panel"] button {
|
|
53
|
+
color: #171717 !important;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/* Override left sidebar navigation hover colors to match color scheme */
|
|
57
|
+
.sidebar-container [role="menuitem"]:hover,
|
|
58
|
+
.sidebar-container [role="menuitem"]:focus,
|
|
59
|
+
.sidebar-container a:hover,
|
|
60
|
+
.sidebar-container a:focus,
|
|
61
|
+
[data-nodetype="story"]:hover,
|
|
62
|
+
[data-nodetype="story"]:focus,
|
|
63
|
+
[data-nodetype="component"]:hover,
|
|
64
|
+
[data-nodetype="component"]:focus,
|
|
65
|
+
[data-nodetype="group"]:hover,
|
|
66
|
+
[data-nodetype="group"]:focus {
|
|
67
|
+
background-color: #f5f5f5 !important;
|
|
68
|
+
color: #0e8168 !important;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/* Override selected state to use primary color */
|
|
72
|
+
.sidebar-container [role="menuitem"][aria-selected="true"],
|
|
73
|
+
.sidebar-container [role="menuitem"][aria-current="true"],
|
|
74
|
+
[data-nodetype="story"][aria-selected="true"],
|
|
75
|
+
[data-nodetype="component"][aria-selected="true"] {
|
|
76
|
+
background-color: #f0f9ff !important;
|
|
77
|
+
color: #0e8168 !important;
|
|
78
|
+
border-left-color: #0e8168 !important;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
.sb-bar button:hover {
|
|
82
|
+
color: #000000 !important;
|
|
83
|
+
background-color: #f5f5f5;;
|
|
84
|
+
}
|
|
85
|
+
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import type { StorybookConfig } from '@storybook/react-vite'
|
|
2
|
+
|
|
3
|
+
const config: StorybookConfig = {
|
|
4
|
+
stories: [
|
|
5
|
+
'../.storybook/welcome.mdx',
|
|
6
|
+
'../packages/design-system/src/**/*.stories.@(js|jsx|ts|tsx|mdx)'],
|
|
7
|
+
addons: [
|
|
8
|
+
'@storybook/addon-links',
|
|
9
|
+
'@storybook/addon-a11y',
|
|
10
|
+
'@storybook/addon-docs',
|
|
11
|
+
],
|
|
12
|
+
framework: {
|
|
13
|
+
name: '@storybook/react-vite',
|
|
14
|
+
options: {
|
|
15
|
+
viteConfigPath: '.storybook/vite.config.ts',
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
viteFinal: async (config) => {
|
|
19
|
+
config.define = {
|
|
20
|
+
...config.define,
|
|
21
|
+
'process.env': {},
|
|
22
|
+
'process': { env: {} },
|
|
23
|
+
}
|
|
24
|
+
return config
|
|
25
|
+
},
|
|
26
|
+
docs: {
|
|
27
|
+
autodocs: 'tag',
|
|
28
|
+
},
|
|
29
|
+
typescript: {
|
|
30
|
+
check: false,
|
|
31
|
+
reactDocgen: 'react-docgen-typescript',
|
|
32
|
+
reactDocgenTypescriptOptions: {
|
|
33
|
+
shouldExtractLiteralValuesFromEnum: true,
|
|
34
|
+
propFilter: (prop) => (prop.parent ? !/node_modules/.test(prop.parent.fileName) : true),
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
core: {
|
|
38
|
+
disableTelemetry: true,
|
|
39
|
+
},
|
|
40
|
+
staticDirs: ['../public'],
|
|
41
|
+
managerHead: (head) => `
|
|
42
|
+
${head}
|
|
43
|
+
<title>A11Y Pros Accessible Design System</title>
|
|
44
|
+
<meta name="description" content="Accessibility-first React UI component library - WCAG 2.1/2.2 compliant components" />
|
|
45
|
+
`,
|
|
46
|
+
// Base path is configured in vite.config.ts to use /storybook-static/
|
|
47
|
+
// This ensures assets use absolute paths that match where files are located
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export default config
|
|
51
|
+
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { addons } from 'storybook/internal/manager-api'
|
|
2
|
+
import { create } from 'storybook/internal/theming'
|
|
3
|
+
import './custom.css'
|
|
4
|
+
|
|
5
|
+
const theme = create({
|
|
6
|
+
base: 'light',
|
|
7
|
+
brandTitle: 'A11y Pros Design System',
|
|
8
|
+
brandUrl: '/storybook',
|
|
9
|
+
brandImage: '/storybook-static/logo.png', // Path to your logo file (served from staticDirs)
|
|
10
|
+
brandTarget: '_self',
|
|
11
|
+
|
|
12
|
+
colorPrimary: '#0e8168',
|
|
13
|
+
colorSecondary: '#001d2f',
|
|
14
|
+
|
|
15
|
+
// Toolbar colors
|
|
16
|
+
barTextColor: '#171717',
|
|
17
|
+
barSelectedColor: '#0e8168', // Selected nav item color (matches colorPrimary)
|
|
18
|
+
barHoverColor: '#f5f5f5', // Hover color for nav items in left sidebar
|
|
19
|
+
barBg: '#ffffff',
|
|
20
|
+
|
|
21
|
+
})
|
|
22
|
+
|
|
23
|
+
addons.setConfig({
|
|
24
|
+
theme,
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Explicitly set document title
|
|
28
|
+
if (typeof window !== 'undefined') {
|
|
29
|
+
window.document.title = 'A11Y Pros Accessible Design System'
|
|
30
|
+
}
|
|
31
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Preview } from '@storybook/react'
|
|
2
|
+
import React, { useEffect } from 'react'
|
|
3
|
+
import '../packages/design-system/src/styles/global.css'
|
|
4
|
+
import './custom.css'
|
|
5
|
+
|
|
6
|
+
const withTheme = (Story, context) => {
|
|
7
|
+
const theme = context.globals.theme || 'light'
|
|
8
|
+
|
|
9
|
+
useEffect(() => {
|
|
10
|
+
document.documentElement.setAttribute('data-theme', theme)
|
|
11
|
+
// Also update Storybook's docs background
|
|
12
|
+
const docsStory = document.querySelector('.docs-story')
|
|
13
|
+
if (docsStory) {
|
|
14
|
+
docsStory.setAttribute('data-theme', theme)
|
|
15
|
+
}
|
|
16
|
+
}, [theme])
|
|
17
|
+
|
|
18
|
+
return (
|
|
19
|
+
<div data-theme={theme} style={{
|
|
20
|
+
backgroundColor: theme === 'dark' ? '#171717' : '#ffffff',
|
|
21
|
+
minHeight: '100%',
|
|
22
|
+
padding: '1rem'
|
|
23
|
+
}}>
|
|
24
|
+
<Story />
|
|
25
|
+
</div>
|
|
26
|
+
)
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const preview: Preview = {
|
|
30
|
+
decorators: [withTheme],
|
|
31
|
+
globalTypes: {
|
|
32
|
+
theme: {
|
|
33
|
+
description: 'Global theme for components',
|
|
34
|
+
defaultValue: 'light',
|
|
35
|
+
toolbar: {
|
|
36
|
+
title: 'Theme',
|
|
37
|
+
icon: 'circlehollow',
|
|
38
|
+
items: [
|
|
39
|
+
{ value: 'light', title: 'Light', icon: 'sun' },
|
|
40
|
+
{ value: 'dark', title: 'Dark', icon: 'moon' },
|
|
41
|
+
],
|
|
42
|
+
dynamicTitle: true,
|
|
43
|
+
},
|
|
44
|
+
},
|
|
45
|
+
},
|
|
46
|
+
parameters: {
|
|
47
|
+
actions: { argTypesRegex: '^on[A-Z].*' },
|
|
48
|
+
backgrounds: {
|
|
49
|
+
disable: true, // Disable since we're handling it with theme
|
|
50
|
+
},
|
|
51
|
+
controls: {
|
|
52
|
+
matchers: {
|
|
53
|
+
color: /(background|color)$/i,
|
|
54
|
+
date: /Date$/i,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
a11y: {
|
|
58
|
+
config: {
|
|
59
|
+
rules: [
|
|
60
|
+
{
|
|
61
|
+
id: 'color-contrast',
|
|
62
|
+
enabled: true,
|
|
63
|
+
},
|
|
64
|
+
],
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
docs: {
|
|
68
|
+
toc: false,
|
|
69
|
+
},
|
|
70
|
+
},
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default preview
|
|
74
|
+
|
|
Binary file
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { defineConfig } from 'vite'
|
|
2
|
+
|
|
3
|
+
export default defineConfig({
|
|
4
|
+
base: '/storybook-static/',
|
|
5
|
+
define: {
|
|
6
|
+
'process.env': {},
|
|
7
|
+
'process': {
|
|
8
|
+
env: {},
|
|
9
|
+
},
|
|
10
|
+
},
|
|
11
|
+
build: {
|
|
12
|
+
// Ensure assets use absolute paths
|
|
13
|
+
assetsDir: 'assets',
|
|
14
|
+
// Disable source maps in production to avoid eval issues
|
|
15
|
+
sourcemap: false,
|
|
16
|
+
// Ensure proper minification
|
|
17
|
+
minify: 'esbuild',
|
|
18
|
+
},
|
|
19
|
+
// Ensure proper module resolution
|
|
20
|
+
resolve: {
|
|
21
|
+
preserveSymlinks: false,
|
|
22
|
+
},
|
|
23
|
+
})
|
|
24
|
+
|
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
export const meta = {
|
|
2
|
+
title: 'Welcome',
|
|
3
|
+
};
|
|
4
|
+
|
|
5
|
+
# Welcome to the A11y Pros Accessible Design System
|
|
6
|
+
|
|
7
|
+
Welcome to our **accessibility-first React UI component library**. This design system provides a comprehensive collection of fully accessible components built with React, TypeScript, and modern web standards.
|
|
8
|
+
|
|
9
|
+
[View on GitHub](https://github.com/ryan0122/a11ypros-components) • [Npm Package](https://www.npmjs.com/package/@a11ypros/a11y-ui-components)
|
|
10
|
+
|
|
11
|
+
## About This Design System
|
|
12
|
+
|
|
13
|
+
This design system is **authored with accessibility in mind by a certified WAS (Web Accessibility Specialist)**. Every component has been carefully crafted to meet WCAG 2.1/2.2 Level AA standards, ensuring that your applications are usable by everyone, regardless of their abilities or the assistive technologies they use.
|
|
14
|
+
|
|
15
|
+
**Note**: Currently, English is the only supported language for screen reader text and ARIA labels. Internationalization (i18n) support is coming soon.
|
|
16
|
+
|
|
17
|
+
### Exploring Components
|
|
18
|
+
|
|
19
|
+
Browse the component stories in the sidebar to explore each component's:
|
|
20
|
+
|
|
21
|
+
### Key Features
|
|
22
|
+
|
|
23
|
+
- **React 18+ Support**: Compatible with React 18.2.0 and React 19.0.0+
|
|
24
|
+
- **Light & Dark Mode**: Built-in theming support with semantic color tokens for light and dark modes
|
|
25
|
+
- **Fully Accessible Components**: All React UI components are fully accessible, featuring proper ARIA attributes, keyboard navigation, and screen reader support
|
|
26
|
+
- **WCAG 2.1/2.2 Compliant**: Components follow accessibility best practices and meet WCAG Level AA standards
|
|
27
|
+
- **Keyboard Navigation**: Complete keyboard support for all interactive elements
|
|
28
|
+
- **Focus Management**: Proper focus trapping and return focus patterns for modals and dialogs
|
|
29
|
+
- **Semantic HTML**: Components use semantic HTML with ARIA enhancements where needed
|
|
30
|
+
- **Reduced Motion Support**: Respects `prefers-reduced-motion` media query
|
|
31
|
+
- **High Contrast Support**: Supports `prefers-contrast` for better visibility
|
|
32
|
+
- **Design Token System**: Consistent spacing, colors, typography, and motion tokens
|
|
33
|
+
|
|
34
|
+
## Available Components
|
|
35
|
+
|
|
36
|
+
Our component library includes:
|
|
37
|
+
|
|
38
|
+
- **Button**: Accessible button with variants, sizes, and loading states
|
|
39
|
+
- **Link**: Semantic link component with external link detection
|
|
40
|
+
- **Modal**: Focus-trapped modal dialog with ARIA support
|
|
41
|
+
- **DataTable**: Accessible table with keyboard navigation and sorting
|
|
42
|
+
- **Tabs**: Tab component with arrow key navigation
|
|
43
|
+
- **Form Components**: Input, Textarea, Select, Checkbox, Radio, Fieldset, Label
|
|
44
|
+
|
|
45
|
+
## Coming Soon
|
|
46
|
+
|
|
47
|
+
The following components are currently in development and will be available soon:
|
|
48
|
+
|
|
49
|
+
- **DatePicker**: Accessible date selection component with keyboard navigation
|
|
50
|
+
- **Banner**: Alert banner component with ARIA live region support
|
|
51
|
+
- **Phone Text Field**: Specialized input field for phone numbers with formatting
|
|
52
|
+
- **Combobox**: Accessible autocomplete/combobox component with ARIA 1.2 patterns
|
|
53
|
+
|
|
54
|
+
## Getting Started
|
|
55
|
+
|
|
56
|
+
### Installation
|
|
57
|
+
|
|
58
|
+
Install the component library via npm:
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
npm i @a11ypros/a11y-ui-components
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Or using yarn:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
yarn add @a11ypros/a11y-ui-components
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Or using pnpm:
|
|
71
|
+
|
|
72
|
+
```bash
|
|
73
|
+
pnpm add @a11ypros/a11y-ui-components
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Usage
|
|
77
|
+
|
|
78
|
+
Once installed, you can import and use components in your React application:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
import { Button, Modal, DataTable } from '@a11ypros/a11y-ui-components';
|
|
82
|
+
|
|
83
|
+
function App() {
|
|
84
|
+
return (
|
|
85
|
+
<Button variant="primary" size="medium">
|
|
86
|
+
Click me
|
|
87
|
+
</Button>
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Browse the component stories in the sidebar to explore each component's:
|
|
93
|
+
- Usage examples
|
|
94
|
+
- Accessibility features
|
|
95
|
+
- Keyboard interaction patterns
|
|
96
|
+
- WCAG compliance information
|
|
97
|
+
- Props and API documentation
|
|
98
|
+
|
|
99
|
+
### Customization with Design Tokens
|
|
100
|
+
|
|
101
|
+
The library exports design tokens and utility functions for customizing your theme:
|
|
102
|
+
|
|
103
|
+
```tsx
|
|
104
|
+
import { colors, spacing, typography, breakpoints, motion } from '@a11ypros/a11y-ui-components';
|
|
105
|
+
import { generateFocusRing, trapFocus, announceToScreenReader } from '@a11ypros/a11y-ui-components';
|
|
106
|
+
|
|
107
|
+
// Use design tokens in your custom components
|
|
108
|
+
const MyComponent = () => (
|
|
109
|
+
<div style={{
|
|
110
|
+
color: colors.primary[600],
|
|
111
|
+
padding: spacing[4],
|
|
112
|
+
fontSize: typography.size.base
|
|
113
|
+
}}>
|
|
114
|
+
Custom styled component with design tokens
|
|
115
|
+
</div>
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
// Use accessibility utilities
|
|
119
|
+
const MyModal = () => {
|
|
120
|
+
const modalRef = useRef(null);
|
|
121
|
+
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (modalRef.current) {
|
|
124
|
+
trapFocus(modalRef.current);
|
|
125
|
+
announceToScreenReader('Modal opened');
|
|
126
|
+
}
|
|
127
|
+
}, []);
|
|
128
|
+
|
|
129
|
+
return <div ref={modalRef}>...</div>;
|
|
130
|
+
};
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
**Available Tokens:**
|
|
134
|
+
- **Colors**: WCAG AA compliant color palettes (primary, neutral, success, warning, error)
|
|
135
|
+
- **Spacing**: Consistent spacing scale based on 4px/8px grid
|
|
136
|
+
- **Typography**: Font sizes, weights, and line heights
|
|
137
|
+
- **Breakpoints**: Responsive design breakpoints
|
|
138
|
+
- **Motion**: Animation durations respecting `prefers-reduced-motion`
|
|
139
|
+
|
|
140
|
+
**Utility Functions:**
|
|
141
|
+
- **ARIA helpers**: `announceToScreenReader`, `generateAriaLabel`
|
|
142
|
+
- **Focus management**: `trapFocus`, `restoreFocus`, `generateFocusRing`
|
|
143
|
+
- **Keyboard**: `handleEscape`, `handleArrowKeys`
|
|
144
|
+
|
|
145
|
+
All tokens maintain WCAG 2.1/2.2 Level AA compliance for color contrast and spacing.
|
|
146
|
+
|
|
147
|
+
### Overriding Design Tokens
|
|
148
|
+
|
|
149
|
+
You can override the default design tokens by customizing CSS custom properties in your application:
|
|
150
|
+
|
|
151
|
+
```css
|
|
152
|
+
/* In your global CSS file */
|
|
153
|
+
:root {
|
|
154
|
+
/* Override primary color */
|
|
155
|
+
--color-primary-500: #your-brand-color;
|
|
156
|
+
--color-primary-600: #your-darker-shade;
|
|
157
|
+
--color-primary-700: #your-darkest-shade;
|
|
158
|
+
|
|
159
|
+
/* Override spacing */
|
|
160
|
+
--spacing-4: 1.5rem; /* default is 1rem */
|
|
161
|
+
|
|
162
|
+
/* Override typography */
|
|
163
|
+
--font-size-base: 1.125rem; /* default is 1rem */
|
|
164
|
+
--font-weight-bold: 600; /* default is 700 */
|
|
165
|
+
|
|
166
|
+
/* Override motion */
|
|
167
|
+
--duration-normal: 250ms; /* default is 200ms */
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Light & Dark Mode Theming
|
|
172
|
+
|
|
173
|
+
The design system includes built-in support for light and dark modes using semantic color tokens. Toggle between themes by setting the `data-theme` attribute:
|
|
174
|
+
|
|
175
|
+
```html
|
|
176
|
+
<!-- Light mode (default) -->
|
|
177
|
+
<html data-theme="light">
|
|
178
|
+
|
|
179
|
+
<!-- Dark mode -->
|
|
180
|
+
<html data-theme="dark">
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
Implement a theme toggle in your application:
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
const ThemeToggle = () => {
|
|
187
|
+
const [theme, setTheme] = useState('light');
|
|
188
|
+
|
|
189
|
+
const toggleTheme = () => {
|
|
190
|
+
const newTheme = theme === 'light' ? 'dark' : 'light';
|
|
191
|
+
setTheme(newTheme);
|
|
192
|
+
document.documentElement.setAttribute('data-theme', newTheme);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
return (
|
|
196
|
+
<button onClick={toggleTheme}>
|
|
197
|
+
Switch to {theme === 'light' ? 'dark' : 'light'} mode
|
|
198
|
+
</button>
|
|
199
|
+
);
|
|
200
|
+
};
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**Theme-Aware Semantic Colors:**
|
|
204
|
+
|
|
205
|
+
The system uses semantic color variables that automatically adapt to the current theme:
|
|
206
|
+
- `--color-text-primary`: Main text color (dark in light mode, light in dark mode)
|
|
207
|
+
- `--color-text-secondary`: Secondary text color
|
|
208
|
+
- `--color-background-default`: Primary background
|
|
209
|
+
- `--color-background-secondary`: Secondary background (cards, hover states)
|
|
210
|
+
- `--color-link`: Link color (maintains WCAG AA contrast in both themes)
|
|
211
|
+
- `--color-border-default`: Border colors
|
|
212
|
+
|
|
213
|
+
All color combinations are tested to meet WCAG AA contrast requirements (4.5:1) in both light and dark modes.
|
|
214
|
+
|
|
215
|
+
**Important**: When overriding colors, ensure your custom colors maintain WCAG AA contrast ratios:
|
|
216
|
+
- **4.5:1** minimum for normal text
|
|
217
|
+
- **3:1** minimum for large text (18pt+) and UI components
|
|
218
|
+
|
|
219
|
+
**Available CSS Custom Properties:**
|
|
220
|
+
- Semantic Colors (theme-aware): `--color-text-primary`, `--color-text-secondary`, `--color-background-default`, `--color-background-secondary`, `--color-link`, `--color-border-default`, `--color-border-hover`, `--color-border-focus`
|
|
221
|
+
- Base Colors: `--color-primary-[50-900]`, `--color-neutral-[50-900]`, `--color-success-[500-700]`, etc.
|
|
222
|
+
- Spacing: `--spacing-[0,1,2,3,4,5,6,8,10,12,16,20,24,32]`
|
|
223
|
+
- Typography: `--font-size-[xs,sm,base,lg,xl,2xl,3xl,4xl]`, `--font-weight-[normal,medium,semibold,bold]`
|
|
224
|
+
- Motion: `--duration-[fast,normal,slow]`
|
|
225
|
+
|
|
226
|
+
## Accessibility First
|
|
227
|
+
|
|
228
|
+
As a **certified WAS web accessibility specialist**, I've ensured that every component in this library:
|
|
229
|
+
|
|
230
|
+
- Uses semantic HTML elements
|
|
231
|
+
- Implements proper ARIA attributes
|
|
232
|
+
- Supports full keyboard navigation
|
|
233
|
+
- Provides visible focus indicators
|
|
234
|
+
- Meets WCAG contrast requirements
|
|
235
|
+
- Works seamlessly with screen readers
|
|
236
|
+
- Handles focus management appropriately
|
|
237
|
+
|
|
238
|
+
Each component story includes detailed accessibility documentation, so you can understand how to use these components in an accessible way.
|
|
239
|
+
|
|
240
|
+
## Important Note on Accessibility
|
|
241
|
+
|
|
242
|
+
> **Note**: While these components are built with accessibility in mind and meet WCAG 2.1/2.2 Level AA standards, **simply using these components does not guarantee an accessible application**. These components are foundational building blocks that must be used properly within the larger consuming application with accessibility in mind.
|
|
243
|
+
|
|
244
|
+
To ensure your application is truly accessible, consider:
|
|
245
|
+
|
|
246
|
+
- **Proper Implementation**: Use components according to their documented patterns and accessibility guidelines
|
|
247
|
+
- **Application-Level Considerations**: Ensure proper page structure, heading hierarchy, and landmark regions
|
|
248
|
+
- **Content Accessibility**: Write clear, descriptive text and provide alternative text for images
|
|
249
|
+
- **Testing**: Regularly test your application with keyboard navigation and screen readers
|
|
250
|
+
- **User Experience**: Consider the full user journey and how components work together
|
|
251
|
+
|
|
252
|
+
These components provide a solid foundation, but accessibility is achieved through thoughtful implementation across your entire application.
|
|
253
|
+
|
|
254
|
+
## Design Tokens
|
|
255
|
+
|
|
256
|
+
The design system uses CSS custom properties for theming, ensuring consistency across your application while maintaining accessibility standards for color contrast and spacing.
|
|
257
|
+
|
|
258
|
+
## Need Help?
|
|
259
|
+
|
|
260
|
+
Explore the component stories to see examples, accessibility notes, and implementation details. Each component is documented with its WCAG compliance information and keyboard interaction patterns.
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
**Built with accessibility in mind by a certified WAS web accessibility specialist.**
|
|
265
|
+
|
package/DEPLOYMENT.md
ADDED
|
@@ -0,0 +1,154 @@
|
|
|
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
|
+
|