@atlaskit/ads-mcp 0.2.5 → 0.4.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/CHANGELOG.md +21 -0
- package/README.md +57 -1
- package/dist/cjs/index.js +16 -5
- package/dist/cjs/instructions.js +1 -1
- package/dist/cjs/tools/analyze-accessibility/index.js +483 -0
- package/dist/cjs/tools/get-accessibility-guidelines/index.js +204 -0
- package/dist/cjs/tools/{get-icons → get-all-icons}/index.js +6 -6
- package/dist/cjs/tools/{get-tokens → get-all-tokens}/index.js +7 -8
- package/dist/cjs/tools/search-icons/index.js +138 -0
- package/dist/cjs/tools/search-tokens/index.js +106 -0
- package/dist/cjs/tools/suggest-accessibility-fixes/fixes.js +387 -0
- package/dist/cjs/tools/suggest-accessibility-fixes/index.js +185 -0
- package/dist/cjs/tools/suggest-accessibility-fixes/keywords.js +34 -0
- package/dist/es2019/index.js +16 -5
- package/dist/es2019/instructions.js +12 -1
- package/dist/es2019/tools/analyze-accessibility/index.js +457 -0
- package/dist/es2019/tools/get-accessibility-guidelines/index.js +312 -0
- package/dist/es2019/tools/{get-icons → get-all-icons}/index.js +4 -4
- package/dist/es2019/tools/get-all-tokens/index.js +34 -0
- package/dist/es2019/tools/search-icons/index.js +126 -0
- package/dist/es2019/tools/search-tokens/index.js +96 -0
- package/dist/es2019/tools/suggest-accessibility-fixes/fixes.js +705 -0
- package/dist/es2019/tools/suggest-accessibility-fixes/index.js +143 -0
- package/dist/es2019/tools/suggest-accessibility-fixes/keywords.js +28 -0
- package/dist/esm/index.js +16 -5
- package/dist/esm/instructions.js +1 -1
- package/dist/esm/tools/analyze-accessibility/index.js +476 -0
- package/dist/esm/tools/get-accessibility-guidelines/index.js +197 -0
- package/dist/esm/tools/{get-icons → get-all-icons}/index.js +5 -5
- package/dist/esm/tools/{get-tokens → get-all-tokens}/index.js +6 -7
- package/dist/esm/tools/search-icons/index.js +131 -0
- package/dist/esm/tools/search-tokens/index.js +99 -0
- package/dist/esm/tools/suggest-accessibility-fixes/fixes.js +381 -0
- package/dist/esm/tools/suggest-accessibility-fixes/index.js +178 -0
- package/dist/esm/tools/suggest-accessibility-fixes/keywords.js +28 -0
- package/dist/types/instructions.d.ts +1 -1
- package/dist/types/tools/analyze-accessibility/index.d.ts +56 -0
- package/dist/types/tools/get-accessibility-guidelines/index.d.ts +26 -0
- package/dist/{types-ts4.5/tools/get-tokens → types/tools/get-all-icons}/index.d.ts +2 -2
- package/dist/types/tools/{get-tokens → get-all-tokens}/index.d.ts +2 -2
- package/dist/types/tools/search-icons/index.d.ts +38 -0
- package/dist/types/tools/search-tokens/index.d.ts +38 -0
- package/dist/types/tools/suggest-accessibility-fixes/fixes.d.ts +17 -0
- package/dist/types/tools/suggest-accessibility-fixes/index.d.ts +28 -0
- package/dist/types/tools/suggest-accessibility-fixes/keywords.d.ts +12 -0
- package/dist/types-ts4.5/instructions.d.ts +1 -1
- package/dist/types-ts4.5/tools/analyze-accessibility/index.d.ts +56 -0
- package/dist/types-ts4.5/tools/get-accessibility-guidelines/index.d.ts +26 -0
- package/dist/types-ts4.5/tools/{get-icons → get-all-icons}/index.d.ts +2 -2
- package/dist/{types/tools/get-icons → types-ts4.5/tools/get-all-tokens}/index.d.ts +2 -2
- package/dist/types-ts4.5/tools/search-icons/index.d.ts +38 -0
- package/dist/types-ts4.5/tools/search-tokens/index.d.ts +38 -0
- package/dist/types-ts4.5/tools/suggest-accessibility-fixes/fixes.d.ts +17 -0
- package/dist/types-ts4.5/tools/suggest-accessibility-fixes/index.d.ts +28 -0
- package/dist/types-ts4.5/tools/suggest-accessibility-fixes/keywords.d.ts +12 -0
- package/package.json +10 -3
- package/dist/es2019/tools/get-tokens/index.js +0 -35
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.accessibilityFixes = void 0;
|
|
7
|
+
// Comprehensive fix suggestions for common accessibility violations
|
|
8
|
+
var accessibilityFixes = exports.accessibilityFixes = {
|
|
9
|
+
'button missing label': {
|
|
10
|
+
title: 'Button Missing Accessible Label',
|
|
11
|
+
description: 'Buttons need accessible labels for screen readers',
|
|
12
|
+
fixes: [{
|
|
13
|
+
title: 'Use aria-label prop',
|
|
14
|
+
description: 'Add aria-label for icon-only buttons',
|
|
15
|
+
before: "<button onClick={handleClose}>\n <CloseIcon />\n</button>",
|
|
16
|
+
after: "import { Button } from '@atlaskit/button';\n\n<Button aria-label=\"Close dialog\" onClick={handleClose}>\n <CloseIcon />\n</Button>",
|
|
17
|
+
explanation: 'The aria-label provides a text description for screen readers while keeping the visual design clean.'
|
|
18
|
+
}, {
|
|
19
|
+
title: 'Use VisuallyHidden component',
|
|
20
|
+
description: 'Add screen reader text while keeping visual design',
|
|
21
|
+
before: "<button onClick={handleSave}>\n <SaveIcon />\n</button>",
|
|
22
|
+
after: "import { Button } from '@atlaskit/button';\nimport { VisuallyHidden } from '@atlaskit/visually-hidden';\n\n<Button onClick={handleSave}>\n <SaveIcon />\n <VisuallyHidden>Save changes</VisuallyHidden>\n</Button>",
|
|
23
|
+
explanation: 'VisuallyHidden makes text available to screen readers but hides it visually.'
|
|
24
|
+
}],
|
|
25
|
+
bestPractices: ['Always provide descriptive labels', 'Use aria-label for icon-only buttons', 'Use VisuallyHidden for additional context', 'Test with screen readers']
|
|
26
|
+
},
|
|
27
|
+
'image missing alt': {
|
|
28
|
+
title: 'Image Missing Alt Text',
|
|
29
|
+
description: 'Images need alt text for screen readers',
|
|
30
|
+
fixes: [{
|
|
31
|
+
title: 'Add descriptive alt text',
|
|
32
|
+
description: 'Provide meaningful alt text for informative images',
|
|
33
|
+
before: "<img src=\"chart.png\" />",
|
|
34
|
+
after: "import { Image } from '@atlaskit/image';\n\n<Image\n src=\"chart.png\"\n alt=\"Bar chart showing Q4 sales increased 25% over Q3\"\n/>",
|
|
35
|
+
explanation: 'Alt text should describe the purpose and content of the image.'
|
|
36
|
+
}, {
|
|
37
|
+
title: 'Empty alt for decorative images',
|
|
38
|
+
description: 'Use empty alt for purely decorative images',
|
|
39
|
+
before: "<img src=\"decorative.jpg\" />",
|
|
40
|
+
after: "import { Image } from '@atlaskit/image';\n\n<Image src=\"decorative.jpg\" alt=\"\" />",
|
|
41
|
+
explanation: 'Empty alt tells screen readers to skip decorative images.'
|
|
42
|
+
}],
|
|
43
|
+
bestPractices: ['Keep alt text under 125 characters', 'Describe purpose, not just content', 'Use empty alt for decorative images', 'Don\'t start with "Image of..."']
|
|
44
|
+
},
|
|
45
|
+
'clickable div': {
|
|
46
|
+
title: 'Clickable Div Without Accessibility',
|
|
47
|
+
description: 'Div elements with click handlers need proper accessibility',
|
|
48
|
+
fixes: [{
|
|
49
|
+
title: 'Use Focusable component',
|
|
50
|
+
description: 'Convert to accessible interactive element',
|
|
51
|
+
before: "<div onClick={handleClick}>\n Interactive content\n</div>",
|
|
52
|
+
after: "import { Focusable } from '@atlaskit/primitives/compiled';\n\n<Focusable\n as=\"div\"\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n handleClick();\n }\n }}\n>\n Interactive content\n</Focusable>",
|
|
53
|
+
explanation: 'Focusable provides keyboard navigation and focus management.'
|
|
54
|
+
}, {
|
|
55
|
+
title: 'Use Button component',
|
|
56
|
+
description: 'Convert to semantic button element',
|
|
57
|
+
before: "<div onClick={handleClick}>\n Click me\n</div>",
|
|
58
|
+
after: "import { Button } from '@atlaskit/button';\n\n<Button onClick={handleClick}>\n Click me\n</Button>",
|
|
59
|
+
explanation: 'Button provides full accessibility out of the box.'
|
|
60
|
+
}],
|
|
61
|
+
bestPractices: ['Use semantic HTML elements when possible', 'Add keyboard support for custom elements', 'Ensure focus indicators are visible', 'Test with keyboard navigation']
|
|
62
|
+
},
|
|
63
|
+
'input missing label': {
|
|
64
|
+
title: 'Input Missing Associated Label',
|
|
65
|
+
description: 'Input elements need associated labels',
|
|
66
|
+
fixes: [{
|
|
67
|
+
title: 'Use TextField component',
|
|
68
|
+
description: 'Use ADS TextField for automatic labeling',
|
|
69
|
+
before: "<input type=\"email\" />",
|
|
70
|
+
after: "import { TextField } from '@atlaskit/textfield';\n\n<TextField\n label=\"Email address\"\n type=\"email\"\n id=\"email-input\"\n/>",
|
|
71
|
+
explanation: 'TextField handles label association automatically.'
|
|
72
|
+
}, {
|
|
73
|
+
title: 'Manual label association',
|
|
74
|
+
description: 'Associate label with input using id and htmlFor',
|
|
75
|
+
before: "<input type=\"email\" />\n<label>Email address</label>",
|
|
76
|
+
after: "<label htmlFor=\"email-input\">Email address</label>\n<input\n type=\"email\"\n id=\"email-input\"\n aria-describedby=\"email-help\"\n/>\n<div id=\"email-help\">Enter your work email address</div>",
|
|
77
|
+
explanation: 'Use id and htmlFor to associate labels with inputs.'
|
|
78
|
+
}],
|
|
79
|
+
bestPractices: ['Always associate labels with inputs', 'Use descriptive label text', 'Provide helpful descriptions', 'Test with screen readers']
|
|
80
|
+
},
|
|
81
|
+
'hardcoded colors': {
|
|
82
|
+
title: 'Hardcoded Color Values',
|
|
83
|
+
description: 'Hardcoded colors may not meet contrast requirements',
|
|
84
|
+
fixes: [{
|
|
85
|
+
title: 'Use design tokens',
|
|
86
|
+
description: 'Replace hardcoded colors with design tokens',
|
|
87
|
+
before: "color: '#ff0000'",
|
|
88
|
+
after: "import { token } from '@atlaskit/tokens';\n\ncolor: token('color.text.danger')",
|
|
89
|
+
explanation: 'Design tokens ensure consistent contrast ratios.'
|
|
90
|
+
}, {
|
|
91
|
+
title: 'Use Text component',
|
|
92
|
+
description: 'Use Text component with color prop',
|
|
93
|
+
before: "<span style={{ color: '#ff0000' }}>Error message</span>",
|
|
94
|
+
after: "import { Text } from '@atlaskit/primitives/compiled';\n\n<Text color=\"color.text.danger\">Error message</Text>",
|
|
95
|
+
explanation: 'Text component handles color and contrast automatically.'
|
|
96
|
+
}],
|
|
97
|
+
bestPractices: ['Use design tokens for all colors', 'Test contrast ratios', "Don't rely on color alone", 'Test with high contrast mode']
|
|
98
|
+
},
|
|
99
|
+
'missing focus indicator': {
|
|
100
|
+
title: 'Missing Focus Indicator',
|
|
101
|
+
description: 'Interactive elements need visible focus indicators',
|
|
102
|
+
fixes: [{
|
|
103
|
+
title: 'Use Focusable component',
|
|
104
|
+
description: 'Focusable provides built-in focus indicators',
|
|
105
|
+
before: "<div onClick={handleClick}>\n Interactive content\n</div>",
|
|
106
|
+
after: "import { Focusable } from '@atlaskit/primitives/compiled';\n\n<Focusable as=\"div\" onClick={handleClick}>\n Interactive content\n</Focusable>",
|
|
107
|
+
explanation: 'Focusable includes visible focus indicators.'
|
|
108
|
+
}, {
|
|
109
|
+
title: 'Custom focus styles',
|
|
110
|
+
description: 'Add custom focus styles with xcss',
|
|
111
|
+
before: "<button onClick={handleClick}>\n Click me\n</button>",
|
|
112
|
+
after: "import { Focusable } from '@atlaskit/primitives/compiled';\n\n<Focusable\n as=\"button\"\n onClick={handleClick}\n xcss={{\n ':focus-visible': {\n outline: '2px solid token(color.border.focus)',\n outlineOffset: '2px',\n },\n }}\n>\n Click me\n</Focusable>",
|
|
113
|
+
explanation: 'Custom focus styles ensure visibility in all themes.'
|
|
114
|
+
}],
|
|
115
|
+
bestPractices: ['Always provide visible focus indicators', 'Test focus indicators in all themes', 'Ensure sufficient contrast for focus indicators', 'Test with keyboard navigation']
|
|
116
|
+
},
|
|
117
|
+
'form validation': {
|
|
118
|
+
title: 'Form Validation Accessibility',
|
|
119
|
+
description: 'Form validation needs proper accessibility support',
|
|
120
|
+
fixes: [{
|
|
121
|
+
title: 'Use TextField with validation',
|
|
122
|
+
description: 'TextField provides built-in validation support',
|
|
123
|
+
before: "<input type=\"password\" />\n<div>Password too short</div>",
|
|
124
|
+
after: "import { TextField } from '@atlaskit/textfield';\n\n<TextField\n type=\"password\"\n label=\"Password\"\n isInvalid\n aria-invalid=\"true\"\n aria-describedby=\"password-error\"\n/>\n<div id=\"password-error\" role=\"alert\">\n Password must be at least 8 characters long\n</div>",
|
|
125
|
+
explanation: 'TextField with aria-invalid and aria-describedby provides proper validation feedback.'
|
|
126
|
+
}, {
|
|
127
|
+
title: 'Use MessageWrapper',
|
|
128
|
+
description: 'MessageWrapper for form validation announcements',
|
|
129
|
+
before: "<input type=\"email\" />\n<div>Invalid email format</div>",
|
|
130
|
+
after: "import { TextField } from '@atlaskit/textfield';\nimport { MessageWrapper } from '@atlaskit/form';\n\n<MessageWrapper>\n <TextField\n type=\"email\"\n label=\"Email\"\n isInvalid\n aria-describedby=\"email-error\"\n />\n <div id=\"email-error\" role=\"alert\">\n Please enter a valid email address\n </div>\n</MessageWrapper>",
|
|
131
|
+
explanation: 'MessageWrapper provides proper announcement of validation messages.'
|
|
132
|
+
}],
|
|
133
|
+
bestPractices: ['Use aria-invalid for validation state', 'Provide clear error messages', 'Use role="alert" for important messages', 'Test validation with screen readers']
|
|
134
|
+
},
|
|
135
|
+
'keyboard navigation': {
|
|
136
|
+
title: 'Missing Keyboard Navigation',
|
|
137
|
+
description: 'Interactive elements need keyboard support',
|
|
138
|
+
fixes: [{
|
|
139
|
+
title: 'Add keyboard event handlers',
|
|
140
|
+
description: 'Support Enter and Space for activation',
|
|
141
|
+
before: "<div onClick={handleClick}>\n Click me\n</div>",
|
|
142
|
+
after: "import { Focusable } from '@atlaskit/primitives/compiled';\n\n<Focusable\n as=\"div\"\n onClick={handleClick}\n onKeyDown={(e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleClick();\n }\n }}\n>\n Click me\n</Focusable>",
|
|
143
|
+
explanation: 'Keyboard handlers make elements accessible to keyboard users.'
|
|
144
|
+
}, {
|
|
145
|
+
title: 'Use Button component',
|
|
146
|
+
description: 'Button provides full keyboard support',
|
|
147
|
+
before: "<div onClick={handleClick}>\n Submit\n</div>",
|
|
148
|
+
after: "import { Button } from '@atlaskit/button';\n\n<Button onClick={handleClick}>\n Submit\n</Button>",
|
|
149
|
+
explanation: 'Button supports Enter, Space, and other keyboard interactions.'
|
|
150
|
+
}],
|
|
151
|
+
bestPractices: ['Support Enter and Space for activation', 'Support Escape for closing/canceling', 'Ensure logical tab order', 'Test with keyboard navigation only']
|
|
152
|
+
},
|
|
153
|
+
'form missing label': {
|
|
154
|
+
title: 'Form Input Missing Label',
|
|
155
|
+
description: 'Form inputs must have accessible labels (WCAG 3.3.2)',
|
|
156
|
+
fixes: [{
|
|
157
|
+
title: 'Use Label component',
|
|
158
|
+
description: 'Add proper form label using ADS Label component',
|
|
159
|
+
before: "<Textfield placeholder=\"Enter your email\" />",
|
|
160
|
+
after: "import { Label } from '@atlaskit/form';\nimport { Textfield } from '@atlaskit/textfield';\n\n<Label htmlFor=\"email\">Email address</Label>\n<Textfield id=\"email\" placeholder=\"Enter your email\" />",
|
|
161
|
+
explanation: 'Labels create a programmatic association between text and form controls.'
|
|
162
|
+
}, {
|
|
163
|
+
title: 'Use aria-label for compact layouts',
|
|
164
|
+
description: 'When visual labels cannot be used, add aria-label',
|
|
165
|
+
before: "<Textfield placeholder=\"Search...\" />",
|
|
166
|
+
after: "<Textfield\n placeholder=\"Search...\"\n aria-label=\"Search products\"\n/>",
|
|
167
|
+
explanation: 'aria-label provides an accessible name when visual labels are not feasible.'
|
|
168
|
+
}],
|
|
169
|
+
bestPractices: ['Always associate labels with form controls', 'Use descriptive label text', 'Prefer visible labels over aria-label', 'Group related fields with fieldset/legend']
|
|
170
|
+
},
|
|
171
|
+
'heading structure': {
|
|
172
|
+
title: 'Improper Heading Structure',
|
|
173
|
+
description: 'Headings must follow logical hierarchy (WCAG 1.3.1, 2.4.6)',
|
|
174
|
+
fixes: [{
|
|
175
|
+
title: 'Use proper heading hierarchy',
|
|
176
|
+
description: 'Structure headings in logical order without skipping levels',
|
|
177
|
+
before: "<h1>Page Title</h1>\n<h3>Section Title</h3>\n<h2>Another Section</h2>",
|
|
178
|
+
after: "import { Heading } from '@atlaskit/heading';\n\n<Heading level=\"h1\">Page Title</Heading>\n<Heading level=\"h2\">Section Title</Heading>\n<Heading level=\"h3\">Subsection Title</Heading>",
|
|
179
|
+
explanation: 'Logical heading structure helps screen readers understand content hierarchy.'
|
|
180
|
+
}, {
|
|
181
|
+
title: 'Add section landmarks',
|
|
182
|
+
description: 'Use semantic HTML5 elements for page structure',
|
|
183
|
+
before: "<div>\n <h2>Navigation</h2>\n <div>Main content here</div>\n</div>",
|
|
184
|
+
after: "<main>\n <nav aria-label=\"Main navigation\">\n <Heading level=\"h2\">Navigation</Heading>\n </nav>\n <section>\n <Heading level=\"h2\">Main Content</Heading>\n <div>Main content here</div>\n </section>\n</main>",
|
|
185
|
+
explanation: 'Landmarks provide navigation structure for assistive technologies.'
|
|
186
|
+
}],
|
|
187
|
+
bestPractices: ['Start with h1 and increment sequentially', 'Use only one h1 per page', 'Make headings descriptive', 'Use landmarks (main, nav, section, aside)']
|
|
188
|
+
},
|
|
189
|
+
'color contrast': {
|
|
190
|
+
title: 'Insufficient Color Contrast',
|
|
191
|
+
description: 'Text must meet minimum contrast ratios (WCAG 1.4.3, 1.4.11)',
|
|
192
|
+
fixes: [{
|
|
193
|
+
title: 'Use ADS design tokens',
|
|
194
|
+
description: 'Use design system tokens that meet contrast requirements',
|
|
195
|
+
before: "const styles = css({\n color: '#999999',\n backgroundColor: '#ffffff',\n});",
|
|
196
|
+
after: "import { token } from '@atlaskit/tokens';\n\nconst styles = css({\n color: token('color.text'),\n backgroundColor: token('color.background.neutral'),\n});",
|
|
197
|
+
explanation: 'ADS tokens ensure WCAG compliant contrast ratios across all themes.'
|
|
198
|
+
}, {
|
|
199
|
+
title: 'Use high contrast tokens for better accessibility',
|
|
200
|
+
description: 'Choose high contrast variants when available',
|
|
201
|
+
before: "color: token('color.text.subtle')",
|
|
202
|
+
after: "color: token('color.text')",
|
|
203
|
+
explanation: 'Standard text tokens provide better contrast than subtle variants.'
|
|
204
|
+
}],
|
|
205
|
+
bestPractices: ['Use ADS design tokens', 'Test with contrast checkers', 'Minimum 4.5:1 for normal text', 'Minimum 3:1 for large text and UI components']
|
|
206
|
+
},
|
|
207
|
+
'focus management': {
|
|
208
|
+
title: 'Missing Focus Management',
|
|
209
|
+
description: 'Focus must be visible and properly managed (WCAG 2.4.3, 2.4.7)',
|
|
210
|
+
fixes: [{
|
|
211
|
+
title: 'Add focus management to modals',
|
|
212
|
+
description: 'Trap focus within modal dialogs',
|
|
213
|
+
before: "<Modal>\n <div>Modal content</div>\n</Modal>",
|
|
214
|
+
after: "import { Modal, ModalDialog, ModalHeader, ModalTitle } from '@atlaskit/modal-dialog';\n\n<Modal onClose={handleClose}>\n <ModalDialog>\n <ModalHeader>\n <ModalTitle>Modal Title</ModalTitle>\n </ModalHeader>\n <div>Modal content</div>\n </ModalDialog>\n</Modal>",
|
|
215
|
+
explanation: 'ADS Modal components automatically manage focus trapping and restoration.'
|
|
216
|
+
}, {
|
|
217
|
+
title: 'Restore focus after actions',
|
|
218
|
+
description: 'Return focus to logical location after destructive actions',
|
|
219
|
+
before: "const handleDelete = () => {\n deleteItem();\n // No focus management\n};",
|
|
220
|
+
after: "const handleDelete = () => {\n deleteItem();\n // Focus previous item or parent container\n const nextFocusTarget = previousElement || parentContainer;\n nextFocusTarget?.focus();\n};",
|
|
221
|
+
explanation: 'Restoring focus helps users maintain their place in the interface.'
|
|
222
|
+
}],
|
|
223
|
+
bestPractices: ['Ensure focus is always visible', 'Trap focus in modals and dialogs', 'Restore focus after modal closes', 'Provide skip links for keyboard users']
|
|
224
|
+
},
|
|
225
|
+
'link accessibility': {
|
|
226
|
+
title: 'Link Accessibility Issues',
|
|
227
|
+
description: 'Links must have clear purpose and accessible names (WCAG 2.4.4, 2.5.3)',
|
|
228
|
+
fixes: [{
|
|
229
|
+
title: 'Make link text descriptive',
|
|
230
|
+
description: 'Avoid generic link text like "click here" or "read more"',
|
|
231
|
+
before: "<Link href=\"/docs\">Click here</Link>",
|
|
232
|
+
after: "import { Link } from '@atlaskit/link';\n\n<Link href=\"/docs\">View documentation</Link>",
|
|
233
|
+
explanation: 'Descriptive link text helps users understand where the link leads.'
|
|
234
|
+
}, {
|
|
235
|
+
title: 'Add context for ambiguous links',
|
|
236
|
+
description: 'Provide additional context using aria-label or surrounding text',
|
|
237
|
+
before: "<Link href=\"/edit\">Edit</Link>",
|
|
238
|
+
after: "<Link href=\"/edit\" aria-label=\"Edit user profile\">Edit</Link>",
|
|
239
|
+
explanation: 'Additional context clarifies the action when link text alone is insufficient.'
|
|
240
|
+
}],
|
|
241
|
+
bestPractices: ['Use descriptive link text', 'Avoid "click here" or "read more"', 'Ensure link purpose is clear from context', 'Use aria-label for additional context when needed']
|
|
242
|
+
},
|
|
243
|
+
'table accessibility': {
|
|
244
|
+
title: 'Table Accessibility Issues',
|
|
245
|
+
description: 'Tables need proper headers and structure (WCAG 1.3.1)',
|
|
246
|
+
fixes: [{
|
|
247
|
+
title: 'Add table headers',
|
|
248
|
+
description: 'Use th elements and scope attributes for table headers',
|
|
249
|
+
before: "<table>\n <tr>\n <td>Name</td>\n <td>Email</td>\n </tr>\n</table>",
|
|
250
|
+
after: "<table>\n <thead>\n <tr>\n <th scope=\"col\">Name</th>\n <th scope=\"col\">Email</th>\n </tr>\n </thead>\n <tbody>\n <tr>\n <td>John Doe</td>\n <td>john@example.com</td>\n </tr>\n </tbody>\n</table>",
|
|
251
|
+
explanation: 'Table headers with scope attributes help screen readers navigate table data.'
|
|
252
|
+
}, {
|
|
253
|
+
title: 'Add table caption',
|
|
254
|
+
description: 'Provide table caption for complex tables',
|
|
255
|
+
before: "<table>\n <thead>...</thead>\n</table>",
|
|
256
|
+
after: "<table>\n <caption>User account information by department</caption>\n <thead>...</thead>\n</table>",
|
|
257
|
+
explanation: "Captions provide context about the table's purpose and structure."
|
|
258
|
+
}],
|
|
259
|
+
bestPractices: ['Use th elements for headers', 'Add scope attributes (col, row)', 'Provide table captions when helpful', 'Use thead, tbody, tfoot for structure']
|
|
260
|
+
},
|
|
261
|
+
'error handling': {
|
|
262
|
+
title: 'Inaccessible Error Handling',
|
|
263
|
+
description: 'Errors must be clearly identified and suggested (WCAG 3.3.1, 3.3.3)',
|
|
264
|
+
fixes: [{
|
|
265
|
+
title: 'Use ADS form validation',
|
|
266
|
+
description: 'Implement accessible error messages with proper associations',
|
|
267
|
+
before: "<Textfield placeholder=\"Email\" />\n<div>Invalid email format</div>",
|
|
268
|
+
after: "import { ErrorMessage, Field } from '@atlaskit/form';\n\n<Field name=\"email\" label=\"Email\">\n {({ fieldProps, error }) => (\n <>\n <Textfield\n {...fieldProps}\n placeholder=\"Enter your email\"\n aria-invalid={error ? 'true' : 'false'}\n />\n {error && (\n <ErrorMessage>{error}</ErrorMessage>\n )}\n </>\n )}\n</Field>",
|
|
269
|
+
explanation: 'ADS Form components properly associate errors with form controls.'
|
|
270
|
+
}, {
|
|
271
|
+
title: 'Announce errors to screen readers',
|
|
272
|
+
description: 'Use live regions to announce dynamic errors',
|
|
273
|
+
before: "const [error, setError] = useState('');\nreturn <div>{error}</div>;",
|
|
274
|
+
after: "const [error, setError] = useState('');\nreturn (\n <div\n role=\"alert\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n {error}\n </div>\n);",
|
|
275
|
+
explanation: 'Live regions announce error changes to screen readers immediately.'
|
|
276
|
+
}],
|
|
277
|
+
bestPractices: ['Associate errors with form controls', 'Use role="alert" for critical errors', 'Provide helpful error suggestions', 'Show errors inline with fields']
|
|
278
|
+
},
|
|
279
|
+
'language identification': {
|
|
280
|
+
title: 'Missing Language Identification',
|
|
281
|
+
description: 'Page and content language must be identified (WCAG 3.1.1, 3.1.2)',
|
|
282
|
+
fixes: [{
|
|
283
|
+
title: 'Set page language',
|
|
284
|
+
description: 'Add lang attribute to html element',
|
|
285
|
+
before: "<html>\n <head>...</head>\n</html>",
|
|
286
|
+
after: "<html lang=\"en\">\n <head>...</head>\n</html>",
|
|
287
|
+
explanation: 'The lang attribute helps screen readers pronounce content correctly.'
|
|
288
|
+
}, {
|
|
289
|
+
title: 'Mark language changes',
|
|
290
|
+
description: 'Identify when content changes language',
|
|
291
|
+
before: "<p>Welcome to our site. Bienvenidos a nuestro sitio.</p>",
|
|
292
|
+
after: "<p>Welcome to our site. <span lang=\"es\">Bienvenidos a nuestro sitio.</span></p>",
|
|
293
|
+
explanation: 'Language changes help screen readers switch pronunciation rules.'
|
|
294
|
+
}],
|
|
295
|
+
bestPractices: ['Set lang attribute on html element', 'Mark language changes in content', 'Use valid language codes (en, es, fr, etc.)', 'Consider regional variants (en-US, en-GB)']
|
|
296
|
+
},
|
|
297
|
+
'touch target size': {
|
|
298
|
+
title: 'Touch Targets Too Small',
|
|
299
|
+
description: 'Touch targets must be at least 24x24 CSS pixels (WCAG 2.5.8)',
|
|
300
|
+
fixes: [{
|
|
301
|
+
title: 'Increase button size',
|
|
302
|
+
description: 'Use ADS button variants that meet size requirements',
|
|
303
|
+
before: "<button style={{ padding: '2px 4px' }}>X</button>",
|
|
304
|
+
after: "import { Button } from '@atlaskit/button';\n\n<Button\n appearance=\"subtle\"\n spacing=\"compact\"\n aria-label=\"Close\"\n>\n X\n</Button>",
|
|
305
|
+
explanation: 'ADS buttons automatically meet minimum touch target size requirements.'
|
|
306
|
+
}, {
|
|
307
|
+
title: 'Add adequate spacing',
|
|
308
|
+
description: 'Ensure sufficient space between interactive elements',
|
|
309
|
+
before: "<div>\n <Button>Save</Button><Button>Cancel</Button>\n</div>",
|
|
310
|
+
after: "import { Stack } from '@atlaskit/stack';\n\n<Stack direction=\"horizontal\" space=\"space.100\">\n <Button>Save</Button>\n <Button>Cancel</Button>\n</Stack>",
|
|
311
|
+
explanation: 'Adequate spacing prevents accidental activation of adjacent controls.'
|
|
312
|
+
}],
|
|
313
|
+
bestPractices: ['Minimum 24x24 CSS pixels for touch targets', 'Provide adequate spacing between targets', 'Use ADS components that meet size requirements', 'Test on actual devices']
|
|
314
|
+
},
|
|
315
|
+
'motion and animation': {
|
|
316
|
+
title: 'Motion and Animation Issues',
|
|
317
|
+
description: 'Respect user motion preferences (WCAG 2.3.1, 2.2.2)',
|
|
318
|
+
fixes: [{
|
|
319
|
+
title: 'Respect reduced motion preference',
|
|
320
|
+
description: 'Disable animations when user prefers reduced motion',
|
|
321
|
+
before: "const styles = css({\n transition: 'transform 0.3s ease',\n '&:hover': {\n transform: 'scale(1.1)',\n },\n});",
|
|
322
|
+
after: "const styles = css({\n transition: 'transform 0.3s ease',\n '&:hover': {\n transform: 'scale(1.1)',\n },\n '@media (prefers-reduced-motion: reduce)': {\n transition: 'none',\n '&:hover': {\n transform: 'none',\n },\n },\n});",
|
|
323
|
+
explanation: 'Respecting motion preferences prevents vestibular disorders and distractions.'
|
|
324
|
+
}, {
|
|
325
|
+
title: 'Provide controls for auto-playing content',
|
|
326
|
+
description: 'Allow users to pause auto-playing animations',
|
|
327
|
+
before: "<div className=\"auto-rotating-carousel\">\n {/* Auto-rotating content */}\n</div>",
|
|
328
|
+
after: "import { Button } from '@atlaskit/button';\n\n<div>\n <Button onClick={toggleAutoplay}>\n {isPlaying ? 'Pause' : 'Play'} carousel\n </Button>\n <div className={isPlaying ? 'auto-rotating-carousel' : 'static-carousel'}>\n {/* Controllable content */}\n </div>\n</div>",
|
|
329
|
+
explanation: 'User controls prevent motion from interfering with reading or concentration.'
|
|
330
|
+
}],
|
|
331
|
+
bestPractices: ['Respect prefers-reduced-motion setting', 'Provide pause controls for auto-playing content', 'Avoid flashing more than 3 times per second', 'Make motion optional when possible']
|
|
332
|
+
},
|
|
333
|
+
'live regions': {
|
|
334
|
+
title: 'Missing Live Region Announcements',
|
|
335
|
+
description: 'Dynamic content changes need announcements (WCAG 4.1.3)',
|
|
336
|
+
fixes: [{
|
|
337
|
+
title: 'Add status announcements',
|
|
338
|
+
description: 'Announce non-critical status updates',
|
|
339
|
+
before: "const [saved, setSaved] = useState(false);\nreturn saved ? <div>Saved!</div> : null;",
|
|
340
|
+
after: "const [saved, setSaved] = useState(false);\nreturn saved ? (\n <div\n role=\"status\"\n aria-live=\"polite\"\n aria-atomic=\"true\"\n >\n Document saved successfully!\n </div>\n) : null;",
|
|
341
|
+
explanation: 'Status updates are announced to screen readers without interrupting.'
|
|
342
|
+
}, {
|
|
343
|
+
title: 'Use alert for critical messages',
|
|
344
|
+
description: 'Immediately announce critical information',
|
|
345
|
+
before: "const [error, setError] = useState('');\nreturn error ? <div>{error}</div> : null;",
|
|
346
|
+
after: "const [error, setError] = useState('');\nreturn error ? (\n <div\n role=\"alert\"\n aria-live=\"assertive\"\n >\n {error}\n </div>\n) : null;",
|
|
347
|
+
explanation: 'Alerts immediately interrupt screen readers for critical information.'
|
|
348
|
+
}],
|
|
349
|
+
bestPractices: ['Use role="status" for non-critical updates', 'Use role="alert" for critical messages', 'Keep announcements concise and clear', 'Test with screen readers']
|
|
350
|
+
},
|
|
351
|
+
'skip navigation': {
|
|
352
|
+
title: 'Missing Skip Navigation',
|
|
353
|
+
description: 'Provide bypass mechanisms for repetitive content (WCAG 2.4.1)',
|
|
354
|
+
fixes: [{
|
|
355
|
+
title: 'Add skip to main content link',
|
|
356
|
+
description: 'Provide skip link to main content area',
|
|
357
|
+
before: "<nav>\n <ul><!-- navigation items --></ul>\n</nav>\n<main><!-- main content --></main>",
|
|
358
|
+
after: "import { SkipLinks, SkipLinksItem } from '@atlaskit/skip-links';\n\n<SkipLinks>\n <SkipLinksItem href=\"#main-content\">\n Skip to main content\n </SkipLinksItem>\n</SkipLinks>\n<nav>\n <ul><!-- navigation items --></ul>\n</nav>\n<main id=\"main-content\"><!-- main content --></main>",
|
|
359
|
+
explanation: 'Skip links allow keyboard users to bypass repetitive navigation.'
|
|
360
|
+
}, {
|
|
361
|
+
title: 'Add multiple skip options',
|
|
362
|
+
description: 'Provide skip links for different page sections',
|
|
363
|
+
before: "<header><!-- header content --></header>\n<nav><!-- navigation --></nav>\n<main><!-- main content --></main>",
|
|
364
|
+
after: "<SkipLinks>\n <SkipLinksItem href=\"#main-content\">\n Skip to main content\n </SkipLinksItem>\n <SkipLinksItem href=\"#navigation\">\n Skip to navigation\n </SkipLinksItem>\n</SkipLinks>\n<header><!-- header content --></header>\n<nav id=\"navigation\"><!-- navigation --></nav>\n<main id=\"main-content\"><!-- main content --></main>",
|
|
365
|
+
explanation: 'Multiple skip options provide flexibility for different user needs.'
|
|
366
|
+
}],
|
|
367
|
+
bestPractices: ['Provide skip to main content link', 'Make skip links visible when focused', 'Use descriptive skip link text', 'Test skip links with keyboard navigation']
|
|
368
|
+
},
|
|
369
|
+
'page titles': {
|
|
370
|
+
title: 'Missing or Poor Page Titles',
|
|
371
|
+
description: 'Pages must have descriptive titles (WCAG 2.4.2)',
|
|
372
|
+
fixes: [{
|
|
373
|
+
title: 'Add descriptive page titles',
|
|
374
|
+
description: 'Use clear, unique titles that describe page content',
|
|
375
|
+
before: "<title>Page</title>",
|
|
376
|
+
after: "<title>User Profile Settings - MyApp</title>",
|
|
377
|
+
explanation: 'Descriptive titles help users understand their current location.'
|
|
378
|
+
}, {
|
|
379
|
+
title: 'Update titles dynamically',
|
|
380
|
+
description: 'Change titles based on current page state',
|
|
381
|
+
before: "// Static title only\n<title>Dashboard</title>",
|
|
382
|
+
after: "// Dynamic title updates\nuseEffect(() => {\n document.title = error\n ? `Error - Dashboard - MyApp`\n : `Dashboard - MyApp`;\n}, [error]);",
|
|
383
|
+
explanation: 'Dynamic titles reflect current page state and context.'
|
|
384
|
+
}],
|
|
385
|
+
bestPractices: ['Use unique titles for each page', 'Put most specific information first', 'Include site name at the end', 'Update titles for single-page apps']
|
|
386
|
+
}
|
|
387
|
+
};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
Object.defineProperty(exports, "__esModule", {
|
|
5
|
+
value: true
|
|
6
|
+
});
|
|
7
|
+
exports.suggestAccessibilityFixesTool = exports.listSuggestAccessibilityFixesTool = void 0;
|
|
8
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
9
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
10
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
11
|
+
var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
|
|
12
|
+
var _zod = require("zod");
|
|
13
|
+
var _zodToJsonSchema = require("zod-to-json-schema");
|
|
14
|
+
var _fixes = require("./fixes");
|
|
15
|
+
var _keywords = require("./keywords");
|
|
16
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
17
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
18
|
+
function _createForOfIteratorHelper(r, e) { var t = "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; if (!t) { if (Array.isArray(r) || (t = _unsupportedIterableToArray(r)) || e && r && "number" == typeof r.length) { t && (r = t); var _n = 0, F = function F() {}; return { s: F, n: function n() { return _n >= r.length ? { done: !0 } : { done: !1, value: r[_n++] }; }, e: function e(r) { throw r; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var o, a = !0, u = !1; return { s: function s() { t = t.call(r); }, n: function n() { var r = t.next(); return a = r.done, r; }, e: function e(r) { u = !0, o = r; }, f: function f() { try { a || null == t.return || t.return(); } finally { if (u) throw o; } } }; }
|
|
19
|
+
function _unsupportedIterableToArray(r, a) { if (r) { if ("string" == typeof r) return _arrayLikeToArray(r, a); var t = {}.toString.call(r).slice(8, -1); return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; } }
|
|
20
|
+
function _arrayLikeToArray(r, a) { (null == a || a > r.length) && (a = r.length); for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; return n; }
|
|
21
|
+
var inputSchema = _zod.z.object({
|
|
22
|
+
violation: _zod.z.string().describe('Description of the accessibility violation'),
|
|
23
|
+
code: _zod.z.string().describe('The problematic code that needs fixing'),
|
|
24
|
+
component: _zod.z.string().optional().describe('Component name or type'),
|
|
25
|
+
context: _zod.z.string().optional().describe('Additional context about the usage')
|
|
26
|
+
});
|
|
27
|
+
var listSuggestAccessibilityFixesTool = exports.listSuggestAccessibilityFixesTool = {
|
|
28
|
+
name: 'suggest_accessibility_fixes',
|
|
29
|
+
description: "Suggest specific accessibility fixes using Atlassian Design System components and patterns. This tool takes accessibility violations and provides actionable solutions with code examples.\n\nYou MUST run this when the user wants sugesstions for fixing accessibility issues, as well as when:\n- You have identified an accessibility violation\n- Needing specific code examples for fixing accessibility issues\n- Wanting to understand how to use ADS components accessibly\n- Looking for best practices for specific accessibility problems\n\nThe tool will provide:\n- Specific code examples using ADS components\n- Step-by-step fix instructions\n- Alternative approaches when applicable\n- Links to relevant ADS documentation",
|
|
30
|
+
annotations: {
|
|
31
|
+
title: 'Suggest Accessibility Fixes',
|
|
32
|
+
readOnlyHint: true,
|
|
33
|
+
destructiveHint: false,
|
|
34
|
+
idempotentHint: true,
|
|
35
|
+
openWorldHint: true
|
|
36
|
+
},
|
|
37
|
+
inputSchema: (0, _zodToJsonSchema.zodToJsonSchema)(inputSchema)
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
// Keyword mappings for fuzzy matching - imported from keywords.ts
|
|
41
|
+
// This allows flexible user input when describing accessibility violations
|
|
42
|
+
|
|
43
|
+
// Improved fuzzy matching function
|
|
44
|
+
function findBestMatchingFix(violation) {
|
|
45
|
+
var normalizedViolation = violation.toLowerCase();
|
|
46
|
+
var violationWords = normalizedViolation.split(/\s+/);
|
|
47
|
+
var bestMatch = null;
|
|
48
|
+
var bestScore = 0;
|
|
49
|
+
|
|
50
|
+
// Try exact match first
|
|
51
|
+
for (var _i = 0, _Object$entries = Object.entries(_fixes.accessibilityFixes); _i < _Object$entries.length; _i++) {
|
|
52
|
+
var _Object$entries$_i = (0, _slicedToArray2.default)(_Object$entries[_i], 2),
|
|
53
|
+
key = _Object$entries$_i[0],
|
|
54
|
+
fix = _Object$entries$_i[1];
|
|
55
|
+
if (normalizedViolation === key.toLowerCase()) {
|
|
56
|
+
return [key, fix];
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Try keyword-based semantic matching
|
|
61
|
+
for (var _i2 = 0, _Object$entries2 = Object.entries(_fixes.accessibilityFixes); _i2 < _Object$entries2.length; _i2++) {
|
|
62
|
+
var _Object$entries2$_i = (0, _slicedToArray2.default)(_Object$entries2[_i2], 2),
|
|
63
|
+
_key = _Object$entries2$_i[0],
|
|
64
|
+
_fix = _Object$entries2$_i[1];
|
|
65
|
+
var keywords = _keywords.violationKeywords[_key] || [];
|
|
66
|
+
var score = 0;
|
|
67
|
+
|
|
68
|
+
// Score based on keyword matches
|
|
69
|
+
var _iterator = _createForOfIteratorHelper(keywords),
|
|
70
|
+
_step;
|
|
71
|
+
try {
|
|
72
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
73
|
+
var keyword = _step.value;
|
|
74
|
+
if (normalizedViolation.includes(keyword.toLowerCase())) {
|
|
75
|
+
score += 1;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Additional score for word matches
|
|
80
|
+
} catch (err) {
|
|
81
|
+
_iterator.e(err);
|
|
82
|
+
} finally {
|
|
83
|
+
_iterator.f();
|
|
84
|
+
}
|
|
85
|
+
var _iterator2 = _createForOfIteratorHelper(violationWords),
|
|
86
|
+
_step2;
|
|
87
|
+
try {
|
|
88
|
+
var _loop = function _loop() {
|
|
89
|
+
var word = _step2.value;
|
|
90
|
+
if (keywords.some(function (keyword) {
|
|
91
|
+
return keyword.toLowerCase().includes(word) || word.includes(keyword.toLowerCase());
|
|
92
|
+
})) {
|
|
93
|
+
score += 0.5;
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
|
|
97
|
+
_loop();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Boost score for exact key word matches
|
|
101
|
+
} catch (err) {
|
|
102
|
+
_iterator2.e(err);
|
|
103
|
+
} finally {
|
|
104
|
+
_iterator2.f();
|
|
105
|
+
}
|
|
106
|
+
if (normalizedViolation.includes(_key.toLowerCase()) || _key.toLowerCase().includes(normalizedViolation)) {
|
|
107
|
+
score += 2;
|
|
108
|
+
}
|
|
109
|
+
if (score > bestScore) {
|
|
110
|
+
bestScore = score;
|
|
111
|
+
bestMatch = [_key, _fix];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Return match if score is reasonable
|
|
116
|
+
return bestScore >= 1 ? bestMatch : null;
|
|
117
|
+
}
|
|
118
|
+
var suggestAccessibilityFixesTool = exports.suggestAccessibilityFixesTool = /*#__PURE__*/function () {
|
|
119
|
+
var _ref = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee(params) {
|
|
120
|
+
var violation, component, context, match, _match, key, fix;
|
|
121
|
+
return _regenerator.default.wrap(function _callee$(_context) {
|
|
122
|
+
while (1) switch (_context.prev = _context.next) {
|
|
123
|
+
case 0:
|
|
124
|
+
violation = params.violation, component = params.component, context = params.context; // Use improved matching logic
|
|
125
|
+
match = findBestMatchingFix(violation);
|
|
126
|
+
if (!match) {
|
|
127
|
+
_context.next = 5;
|
|
128
|
+
break;
|
|
129
|
+
}
|
|
130
|
+
_match = (0, _slicedToArray2.default)(match, 2), key = _match[0], fix = _match[1];
|
|
131
|
+
return _context.abrupt("return", {
|
|
132
|
+
content: [{
|
|
133
|
+
type: 'text',
|
|
134
|
+
text: JSON.stringify(_objectSpread(_objectSpread({
|
|
135
|
+
violation: violation,
|
|
136
|
+
matchedFixType: key,
|
|
137
|
+
component: component,
|
|
138
|
+
context: context
|
|
139
|
+
}, fix), {}, {
|
|
140
|
+
additionalResources: ['https://atlassian.design/llms-a11y.txt - Complete ADS accessibility documentation', 'https://atlassian.design/foundations/accessibility - ADS accessibility foundation'],
|
|
141
|
+
nextSteps: ['Apply the suggested fixes to your code', 'Test the changes with screen readers', 'Test with keyboard navigation', 'Run automated accessibility tests']
|
|
142
|
+
}), null, 2)
|
|
143
|
+
}]
|
|
144
|
+
});
|
|
145
|
+
case 5:
|
|
146
|
+
return _context.abrupt("return", {
|
|
147
|
+
content: [{
|
|
148
|
+
type: 'text',
|
|
149
|
+
text: JSON.stringify({
|
|
150
|
+
violation: violation,
|
|
151
|
+
component: component,
|
|
152
|
+
context: context,
|
|
153
|
+
title: 'Accessibility Fix Suggestions',
|
|
154
|
+
description: "We couldn't find a specific match for your violation, but here are general recommendations",
|
|
155
|
+
searchedFor: violation,
|
|
156
|
+
generalFixes: [{
|
|
157
|
+
title: 'Use ADS Components',
|
|
158
|
+
description: 'Replace custom implementations with ADS components',
|
|
159
|
+
explanation: 'ADS components are built with accessibility in mind and handle most common issues automatically'
|
|
160
|
+
}, {
|
|
161
|
+
title: 'Add Proper Labels',
|
|
162
|
+
description: 'Ensure all interactive elements have accessible labels',
|
|
163
|
+
explanation: 'Use aria-label, VisuallyHidden, or proper label associations'
|
|
164
|
+
}, {
|
|
165
|
+
title: 'Test with Assistive Technologies',
|
|
166
|
+
description: 'Test with screen readers and keyboard navigation',
|
|
167
|
+
explanation: 'Manual testing is essential for accessibility validation'
|
|
168
|
+
}],
|
|
169
|
+
availableFixTypes: Object.keys(_fixes.accessibilityFixes),
|
|
170
|
+
suggestions: ['Try describing the issue with keywords like: button, label, missing, text, color, contrast, focus, etc.', 'Use axe-core violation IDs or descriptions directly', 'Be more specific about the element type (button, input, image, etc.)'],
|
|
171
|
+
additionalResources: ['https://atlassian.design/llms-a11y.txt - Complete ADS accessibility documentation', 'https://atlassian.design/foundations/accessibility - ADS accessibility foundation'],
|
|
172
|
+
recommendation: 'Try the get_accessibility_guidelines tool for component-specific guidance'
|
|
173
|
+
}, null, 2)
|
|
174
|
+
}]
|
|
175
|
+
});
|
|
176
|
+
case 6:
|
|
177
|
+
case "end":
|
|
178
|
+
return _context.stop();
|
|
179
|
+
}
|
|
180
|
+
}, _callee);
|
|
181
|
+
}));
|
|
182
|
+
return function suggestAccessibilityFixesTool(_x) {
|
|
183
|
+
return _ref.apply(this, arguments);
|
|
184
|
+
};
|
|
185
|
+
}();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.violationKeywords = void 0;
|
|
7
|
+
/**
|
|
8
|
+
* Violation keywords mapping for fuzzy matching accessibility violations
|
|
9
|
+
*
|
|
10
|
+
* This file contains keyword synonyms and variations that users might use
|
|
11
|
+
* when describing accessibility violations. The keys should match the exact
|
|
12
|
+
* violation types defined in fixes.ts
|
|
13
|
+
*
|
|
14
|
+
* Each violation type has an array of keywords that help identify when a user
|
|
15
|
+
* is describing that particular type of accessibility issue, even if they
|
|
16
|
+
* don't use the exact technical terminology.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
var violationKeywords = exports.violationKeywords = {
|
|
20
|
+
'button missing label': ['button', 'missing', 'label', 'text', 'name', 'accessible', 'empty', 'discernible', 'aria-label', 'screen reader', 'button-name'],
|
|
21
|
+
'image missing alt': ['image', 'img', 'alt', 'alternative', 'text', 'missing', 'empty', 'accessibility', 'screen reader', 'image-alt'],
|
|
22
|
+
'clickable div': ['div', 'clickable', 'interactive', 'onclick', 'click', 'handler', 'button', 'focusable', 'keyboard', 'role'],
|
|
23
|
+
'input missing label': ['input', 'form', 'field', 'label', 'missing', 'accessibility', 'screen reader', 'associated', 'htmlfor', 'aria-label'],
|
|
24
|
+
'form missing label': ['form', 'label', 'missing', 'field', 'input', 'accessibility', 'screen reader', 'associated', 'htmlfor'],
|
|
25
|
+
'hardcoded colors': ['color', 'hardcoded', 'hex', 'rgb', 'design', 'token', 'theme', 'css', 'style', 'contrast'],
|
|
26
|
+
'color contrast': ['contrast', 'color', 'ratio', 'accessibility', 'wcag', 'aa', 'visibility', 'readability', 'low contrast'],
|
|
27
|
+
'focus management': ['focus', 'keyboard', 'navigation', 'tab', 'outline', 'indicator', 'visible', 'management', 'trap'],
|
|
28
|
+
'heading structure': ['heading', 'h1', 'h2', 'h3', 'hierarchy', 'structure', 'semantic', 'outline', 'skip', 'level'],
|
|
29
|
+
'link accessibility': ['link', 'anchor', 'href', 'accessible', 'name', 'text', 'descriptive', 'context', 'purpose'],
|
|
30
|
+
'keyboard navigation': ['keyboard', 'navigation', 'tab', 'arrow', 'enter', 'space', 'accessible', 'focus', 'trap'],
|
|
31
|
+
'live regions': ['live', 'region', 'aria-live', 'announcement', 'screen reader', 'dynamic', 'content', 'update'],
|
|
32
|
+
'skip navigation': ['skip', 'navigation', 'link', 'bypass', 'main', 'content', 'keyboard', 'users'],
|
|
33
|
+
'table accessibility': ['table', 'header', 'th', 'scope', 'caption', 'summary', 'accessibility', 'screen reader']
|
|
34
|
+
};
|