@atlaskit/ads-mcp 0.2.5 → 0.3.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 +11 -0
- package/README.md +57 -1
- package/dist/cjs/index.js +9 -2
- 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/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 +9 -2
- package/dist/es2019/instructions.js +8 -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/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 +9 -2
- 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/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/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/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 +9 -3
|
@@ -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
|
+
};
|
package/dist/es2019/index.js
CHANGED
|
@@ -3,10 +3,13 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
|
|
|
3
3
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
4
4
|
import { CallToolRequestSchema, ListToolsRequestSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
5
5
|
import { instructions } from './instructions';
|
|
6
|
+
import { analyzeAccessibilityTool, analyzeLocalhostAccessibilityTool, listAnalyzeAccessibilityTool, listAnalyzeLocalhostAccessibilityTool } from './tools/analyze-accessibility';
|
|
7
|
+
import { getAccessibilityGuidelinesTool, listGetAccessibilityGuidelinesTool } from './tools/get-accessibility-guidelines';
|
|
6
8
|
import { getComponentDetailsTool, listGetComponentDetailsTool } from './tools/get-component-details';
|
|
7
9
|
import { getComponentsTool, listGetComponentsTool } from './tools/get-components';
|
|
8
10
|
import { getIconsTool, listGetIconsTool } from './tools/get-icons';
|
|
9
11
|
import { getTokensTool, listGetTokensTool } from './tools/get-tokens';
|
|
12
|
+
import { listSuggestAccessibilityFixesTool, suggestAccessibilityFixesTool } from './tools/suggest-accessibility-fixes';
|
|
10
13
|
|
|
11
14
|
// eslint-disable-next-line import/no-extraneous-dependencies -- this uses require because not all node versions this package supports use the same import assertions/attributes
|
|
12
15
|
const pkgJson = require('@atlaskit/ads-mcp/package.json');
|
|
@@ -22,14 +25,18 @@ const server = new Server({
|
|
|
22
25
|
});
|
|
23
26
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
24
27
|
return {
|
|
25
|
-
tools: [listGetTokensTool, listGetComponentsTool, listGetComponentDetailsTool, listGetIconsTool]
|
|
28
|
+
tools: [listGetTokensTool, listGetComponentsTool, listGetComponentDetailsTool, listGetIconsTool, listAnalyzeAccessibilityTool, listAnalyzeLocalhostAccessibilityTool, listGetAccessibilityGuidelinesTool, listSuggestAccessibilityFixesTool]
|
|
26
29
|
};
|
|
27
30
|
});
|
|
28
31
|
const callTools = {
|
|
29
32
|
get_tokens: getTokensTool,
|
|
30
33
|
get_components: getComponentsTool,
|
|
31
34
|
get_component_details: getComponentDetailsTool,
|
|
32
|
-
get_icons: getIconsTool
|
|
35
|
+
get_icons: getIconsTool,
|
|
36
|
+
analyze_accessibility: analyzeAccessibilityTool,
|
|
37
|
+
analyze_localhost_accessibility: analyzeLocalhostAccessibilityTool,
|
|
38
|
+
get_accessibility_guidelines: getAccessibilityGuidelinesTool,
|
|
39
|
+
suggest_accessibility_fixes: suggestAccessibilityFixesTool
|
|
33
40
|
};
|
|
34
41
|
|
|
35
42
|
// Handle tool execution
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
export const instructions = `
|
|
2
2
|
You are an expert in the Atlassian Design System (aka ADS). You are able to answer questions about the design system and provide guidance on what offerings to use when building user interfaces.
|
|
3
3
|
|
|
4
|
-
You
|
|
4
|
+
You have special expertise in accessibility and can help ensure that interfaces built with ADS components are accessible to all users. You can analyze code for accessibility violations, provide specific fix suggestions, and offer guidance on accessibility best practices.
|
|
5
|
+
|
|
6
|
+
You are able to use the provided tools to help answer your questions, but may also access https://atlassian.design/llms.txt, https://atlassian.design/llms-a11y.txt, or https://atlassian.design/ directly for deeper research and information.
|
|
7
|
+
|
|
8
|
+
Accessibility Tools Available:
|
|
9
|
+
- analyze_accessibility: Analyze React component code for accessibility violations
|
|
10
|
+
- get_accessibility_guidelines: Get specific accessibility guidelines and best practices
|
|
11
|
+
- suggest_accessibility_fixes: Get specific fix suggestions for accessibility violations
|
|
5
12
|
`;
|