@a11y-context/mcp-server 0.1.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/LICENSE +201 -0
- package/README.md +62 -0
- package/corpus/web/react/components/accordion.basic.md +157 -0
- package/corpus/web/react/components/button.basic.md +91 -0
- package/corpus/web/react/components/button.toggle.md +109 -0
- package/corpus/web/react/components/carousel.dots.md +376 -0
- package/corpus/web/react/components/carousel.thumbnails.md +395 -0
- package/corpus/web/react/components/collection-row.basic.md +179 -0
- package/corpus/web/react/components/combobox.autocomplete.md +293 -0
- package/corpus/web/react/components/dialog.modal.md +202 -0
- package/corpus/web/react/components/dialog.nonmodal.md +184 -0
- package/corpus/web/react/components/disclosure.basic.md +97 -0
- package/corpus/web/react/components/grid.channel-guide.md +453 -0
- package/corpus/web/react/components/link.basic.md +105 -0
- package/corpus/web/react/components/listbox.basic.md +263 -0
- package/corpus/web/react/components/menu.basic.md +294 -0
- package/corpus/web/react/components/menu.menubar.md +296 -0
- package/corpus/web/react/components/navigation-menu.basic.md +349 -0
- package/corpus/web/react/components/navigation-menu.dropdown.md +220 -0
- package/corpus/web/react/components/select.basic.md +318 -0
- package/corpus/web/react/components/select.native.md +108 -0
- package/corpus/web/react/components/switch.basic.md +151 -0
- package/corpus/web/react/components/toast.basic.md +112 -0
- package/corpus/web/react/components/tooltip.basic.md +139 -0
- package/corpus/web/react/global/global_rules.md +292 -0
- package/corpus/web/react/patterns.json +946 -0
- package/dist/config.js +35 -0
- package/dist/contracts/v1/types.js +6 -0
- package/dist/http.js +103 -0
- package/dist/index.js +8 -0
- package/dist/mcp/createServer.js +122 -0
- package/dist/mcp/response.js +7 -0
- package/dist/mcp/server.js +15 -0
- package/dist/repo/cache.js +27 -0
- package/dist/repo/globalRules.js +304 -0
- package/dist/repo/index.js +178 -0
- package/dist/repo/paths.js +25 -0
- package/dist/repo/sections.js +166 -0
- package/dist/smoke/smoke-remote-mcp.js +43 -0
- package/dist/telemetry.js +15 -0
- package/dist/telemetryWrap.js +48 -0
- package/dist/tools/getGlobalRules.js +33 -0
- package/dist/tools/getPattern.js +54 -0
- package/dist/tools/listPatterns.js +33 -0
- package/dist/utils/fs.js +18 -0
- package/dist/utils/hash.js +4 -0
- package/package.json +68 -0
|
@@ -0,0 +1,292 @@
|
|
|
1
|
+
---
|
|
2
|
+
|
|
3
|
+
id: "global_ruleset.baseline"
|
|
4
|
+
title: "Foundations"
|
|
5
|
+
slug: "/foundations"
|
|
6
|
+
stack: "web/react"
|
|
7
|
+
rule_set: "baseline"
|
|
8
|
+
status: "beta"
|
|
9
|
+
summary: "Baseline accessibility rules applied across most UI work."
|
|
10
|
+
cache_ttl_seconds: 86400
|
|
11
|
+
apply_policy:
|
|
12
|
+
instruction: "Apply all MUST rules that match the current change scope. If the task does not touch a scope, do not introduce unrelated changes."
|
|
13
|
+
scopes_in_order: ["utility", "page", "layout", "component", "style"]
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# Foundations
|
|
18
|
+
|
|
19
|
+
Foundations are the accessibility rules that aren't tied to a single component — utilities used across patterns (like `sr-only`), page-level structure (landmarks, headings, page titles), and visual fundamentals (text contrast, focus indicators). The rules below are the cross-cutting requirements every UI implementation should meet.
|
|
20
|
+
|
|
21
|
+
## Rule: Offscreen Text Utility (sr-only)
|
|
22
|
+
|
|
23
|
+
```yaml
|
|
24
|
+
id: global.sr-only
|
|
25
|
+
scope: [utility, component, style]
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### Must Haves
|
|
29
|
+
- Where a component's rules dictate the use of "offscreen text", then the snippet below must be included as a CSS class: `.sr-only`.
|
|
30
|
+
- Offscreen text may be used as an alternative to `aria-labelledby` or `aria-label`.
|
|
31
|
+
|
|
32
|
+
### Don'ts
|
|
33
|
+
- Do not hide offscreen text using `display: none` or `visibility: hidden` when it is needed for an accessible name.
|
|
34
|
+
|
|
35
|
+
### Snippets
|
|
36
|
+
```css
|
|
37
|
+
.sr-only {
|
|
38
|
+
clip: rect(1px,1px,1px,1px);
|
|
39
|
+
height: 1px;
|
|
40
|
+
overflow: hidden;
|
|
41
|
+
position: absolute;
|
|
42
|
+
white-space: nowrap;
|
|
43
|
+
width: 1px;
|
|
44
|
+
user-select: none;
|
|
45
|
+
-webkit-user-select: none;
|
|
46
|
+
-moz-user-select: none;
|
|
47
|
+
-ms-user-select: none;
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### Acceptance Checks
|
|
52
|
+
- Where offscreen text is implemented, it is not overridden by `aria-labelledby` or `aria-label`.
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## Rule: Page Title
|
|
57
|
+
|
|
58
|
+
```yaml
|
|
59
|
+
id: global.page-title
|
|
60
|
+
scope: [page]
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### Must Haves
|
|
64
|
+
- Each page (route/view/document) sets a descriptive page title that reflects the central topic of the current page.
|
|
65
|
+
- The page title begins with the name of the current page and is followed by the name of the site.
|
|
66
|
+
- Page name and site name should be separated using a clear visual character, such as a hyphen, emdash, or vertical pipe.
|
|
67
|
+
- Page name should be similar to the `<h1>` text on the page.
|
|
68
|
+
|
|
69
|
+
### Don'ts
|
|
70
|
+
- Do not leave the page title as a generic placeholder across routes.
|
|
71
|
+
|
|
72
|
+
### Acceptance Checks
|
|
73
|
+
- Browser tab title changes appropriately when navigating to the page.
|
|
74
|
+
- The browser tab title includes the page name and then the site name, with a clear separator between.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Rule: Landmarks
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
id: global.landmarks
|
|
82
|
+
scope: [page, layout]
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Must Haves
|
|
86
|
+
- When site or app navigation is present on a page (route/view/document), this is contained inside a `<nav>` (or `role="navigation"`) landmark, which is contained inside a `<header>` (or `role="banner"`) landmark.
|
|
87
|
+
- If a single set of navigation is present, then it should be labeled (e.g., `<nav aria-label="primary">`).
|
|
88
|
+
- If more than one set of navigation is present - e.g., primary and secondary or breadcrumbs - then each `navigation` landmark must be labeled (e.g., `<nav aria-label="primary">`, `<nav aria-label="secondary">`, `<nav aria-label="breadcrumbs">`).
|
|
89
|
+
- When a footer is present - i.e., a section at the bottom of the page with information relevant to the entire site, such as a sitemap or navigation links - this is contained inside a `<footer>` (or `role="contentinfo"`) landmark
|
|
90
|
+
- A `<main>` (or `role="main"`) landmark must always be present.
|
|
91
|
+
- The `main` landmark contains the dominant content of the page, which directly relates to or expands upon the central topic of the page, or the central functionality of an application.
|
|
92
|
+
- If site or app navigation is present at the top of the page or view (i.e., `header`), and/or a footer (`footer`) is present at the bottom of the page, the `main` landmark should wrap all content between `header` and `footer` content
|
|
93
|
+
- The `main` landmark must be a sibling of the `header` and/or `footer` containers.
|
|
94
|
+
- If the page contains complementary content - i.e., content that is only indirectly related to the page's main content - this is contained inside an `<aside>` (or `role="complementary"`) landmark
|
|
95
|
+
- The `aside` landmark is infrequently used and does not need to be present.
|
|
96
|
+
- If present, the `aside` landmark should be a sibling to the `main` landmark, and to the `header` and/or `footer`, if present.
|
|
97
|
+
|
|
98
|
+
### Don'ts
|
|
99
|
+
- Do not use multiple `main` landmarks on the same page.
|
|
100
|
+
- Do not wrap the `header`, `footer`, or `aside` inside the `main` landmark, or vice-versa: these should all be siblings.
|
|
101
|
+
|
|
102
|
+
### Acceptance Checks
|
|
103
|
+
- There is one `main` landmark present on every page.
|
|
104
|
+
- If there is site or app navigation present, this is contained inside a `nav` landmark, which is contained inside a `header` landmark, sibling to the `main`.
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Rule: Heading Structure
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
id: global.headings
|
|
112
|
+
scope: [page, layout]
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Must Haves
|
|
116
|
+
- Each page (route/view/document) has one level 1 heading, or `<h1>`, that identifies the main topic of the page.
|
|
117
|
+
- Headings are nested in a logical order according to the relationships of the content they introduce.
|
|
118
|
+
- An `<h2>` introduces a major section of the page.
|
|
119
|
+
- An `<h3>` introduces a subsection of the preceding `<h2>`, and so on.
|
|
120
|
+
- When closing a subsection and returning to a higher-level section, it is acceptable to return to a higher heading level.
|
|
121
|
+
- If text visually functions as a heading, it must be marked up as a heading element rather than only styled to look like one.
|
|
122
|
+
- If footer links are grouped under visible group titles (e.g., "Company", "Support", "Legal"), each group title is a level 2 heading (`<h2>`).
|
|
123
|
+
- Headings are descriptive of the content they introduce.
|
|
124
|
+
|
|
125
|
+
### Customizable
|
|
126
|
+
- Section headings use native HTML heading elements (`<h1>` through `<h6>`) to communicate the structure of the page.
|
|
127
|
+
- If native elements are unavailable, headings can be implemented with `role="heading"` and `aria-level`.
|
|
128
|
+
|
|
129
|
+
### Don'ts
|
|
130
|
+
- Do not use heading elements only to make text look larger or bolder.
|
|
131
|
+
- Do not skip heading levels when moving deeper into the hierarchy, such as going from `<h2>` directly to `<h4>`, unless closing a subsection and returning to a higher-level section.
|
|
132
|
+
- Do not use non-heading elements as visual-only section titles when they function as headings.
|
|
133
|
+
|
|
134
|
+
### Acceptance Checks
|
|
135
|
+
- Each page has one clear primary heading that identifies the main topic of the page.
|
|
136
|
+
- The heading structure forms a logical outline of the content.
|
|
137
|
+
- When heading depth increases, levels are not skipped.
|
|
138
|
+
- Headings are descriptive of the content they introduce.
|
|
139
|
+
- Footer link-group titles, when present, are marked up as `<h2>` headings.
|
|
140
|
+
- Text that appears visually to be a heading is programmatically marked up as a heading.
|
|
141
|
+
- No empty heading elements are present.
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## Rule: Text Contrast
|
|
146
|
+
|
|
147
|
+
```yaml
|
|
148
|
+
id: global.text-contrast
|
|
149
|
+
scope: [page, layout, component, style]
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Must Haves
|
|
153
|
+
- Text and images of text must have sufficient contrast against their background.
|
|
154
|
+
- Normal-size text must meet a contrast ratio of at least 4.5:1.
|
|
155
|
+
- Large-scale text (18pt regular, or 14pt bold) must meet a contrast ratio of at least 3:1.
|
|
156
|
+
|
|
157
|
+
### Don'ts
|
|
158
|
+
- Do not use color combinations for text that fall below 4.5:1 for normal text or 3:1 for large text.
|
|
159
|
+
|
|
160
|
+
### Acceptance Checks
|
|
161
|
+
- Normal text meets at least 4.5:1 contrast against its background.
|
|
162
|
+
- Large-scale text meets at least 3:1 contrast against its background.
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Rule: Non-text Contrast
|
|
167
|
+
|
|
168
|
+
```yaml
|
|
169
|
+
id: global.non-text-contrast
|
|
170
|
+
scope: [page, component, style]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Must Haves
|
|
174
|
+
- Visual information needed to identify a user interface component - such as control boundaries - must have a contrast ratio of at least 3:1 against adjacent colors.
|
|
175
|
+
- In other words, a component's border or background color must contrast at least 3:1 against the page's background color.
|
|
176
|
+
- Meaningful graphical objects must have a contrast ratio of at least 3:1 against adjacent colors when their appearance is needed to understand the content.
|
|
177
|
+
- This includes meaningful icons, simple charts, data marks, indicators, and other non-decorative graphics.
|
|
178
|
+
|
|
179
|
+
### Don'ts
|
|
180
|
+
- Do not use very low-contrast borders, outlines, or icons when they are necessary to identify a control or its current state.
|
|
181
|
+
|
|
182
|
+
### Acceptance Checks
|
|
183
|
+
- Visible UI controls and authored state indicators needed for perception meet at least 3:1 contrast against adjacent colors.
|
|
184
|
+
- Meaningful icons and other non-text graphics needed for understanding meet at least 3:1 contrast against adjacent colors.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Rule: Use of Color
|
|
189
|
+
|
|
190
|
+
```yaml
|
|
191
|
+
id: global.use-of-color
|
|
192
|
+
scope: [component, style]
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Must Haves
|
|
196
|
+
- Do not use color as the only visual means of conveying information, indicating an action, prompting a response, or distinguishing a visual element (WCAG 1.4.1).
|
|
197
|
+
- When a component renders a meaningful state visually (e.g., selected, active, current, invalid, pressed), that state is distinguishable by something in addition to color, such as an icon, checkmark, shape, underline, or text.
|
|
198
|
+
- This is the visual counterpart to exposing the state programmatically. A component may satisfy `aria-selected` yet still fail 1.4.1 if selection is shown by a background tint alone.
|
|
199
|
+
|
|
200
|
+
### Don'ts
|
|
201
|
+
- Do not signal selection, validity, current-ness, or availability by color alone.
|
|
202
|
+
|
|
203
|
+
### Acceptance Checks
|
|
204
|
+
- Every meaningful state the component renders is perceivable without relying on color (verify in grayscale).
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Rule: Focus Not Obscured
|
|
209
|
+
|
|
210
|
+
```yaml
|
|
211
|
+
id: global.focus-not-obscured
|
|
212
|
+
scope: [component, layout, style]
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Must Haves
|
|
216
|
+
- When a component receives keyboard focus, the focused element is not entirely hidden by author-created content such as sticky headers, sticky footers, or the component's own overlapping chrome (WCAG 2.4.11 Focus Not Obscured, Minimum).
|
|
217
|
+
- Components with their own sticky or floating sub-regions (e.g., a frozen header row or column) keep the focused element scrolled or offset into a visible area.
|
|
218
|
+
|
|
219
|
+
### Don'ts
|
|
220
|
+
- Do not let a sticky or floating region overlap and fully conceal the element that currently has keyboard focus.
|
|
221
|
+
|
|
222
|
+
### Acceptance Checks
|
|
223
|
+
- Tabbing or arrowing to any focusable element leaves at least part of that element and its focus indicator visible, not fully covered by sticky or overlapping content.
|
|
224
|
+
|
|
225
|
+
---
|
|
226
|
+
|
|
227
|
+
## Rule: Focus States
|
|
228
|
+
|
|
229
|
+
```yaml
|
|
230
|
+
id: global.focus-states
|
|
231
|
+
scope: [component, style]
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
### Must Haves
|
|
235
|
+
- Each keyboard-focusable user interface component must have a visible focus indicator when it receives keyboard focus.
|
|
236
|
+
- The focus indicator must have a contrast ratio of at least 3:1 against adjacent colors.
|
|
237
|
+
|
|
238
|
+
### Customizable
|
|
239
|
+
- The focus indicator may take different forms, such as an outline, border, background change, underline, or other visible treatment, provided that it clearly indicates which element currently has keyboard focus.
|
|
240
|
+
- Prefer styling keyboard focus with `:focus-visible` so the indicator is shown when users navigate by keyboard, not on mouse click.
|
|
241
|
+
- **Strongly recommended:** a two-layer focus style — a 2px solid ring floated 2px outside the element with a white box-shadow behind it — so the indicator remains visible against any surrounding surface (light, dark, image, or gradient). See the first snippet below.
|
|
242
|
+
- A simple solid outline offset from the element remains acceptable when the surrounding surfaces are known and the ring's 3:1 contrast against them is verified.
|
|
243
|
+
- Starting pattern: `outline: 2px solid ...`
|
|
244
|
+
- Starting offset: `outline-offset: 2px` (`1px` may be used where spacing is tighter).
|
|
245
|
+
- **Windows High Contrast Mode (forced-colors) support is required for any focus style.** Pair the primary style with a `@media (forced-colors: active)` override that uses the `Highlight` CSS system color and drops the box-shadow. See the shared override snippet below.
|
|
246
|
+
|
|
247
|
+
### Don'ts
|
|
248
|
+
- Do not use a focus indicator whose color blends into adjacent colors below the required 3:1 contrast ratio.
|
|
249
|
+
- Do not make the focus indicator so subtle that users cannot quickly identify which element currently has focus.
|
|
250
|
+
- Do not rely on hover-only styles as the only visible indicator of focus.
|
|
251
|
+
- Do not ship focus styles without a `forced-colors` override — Windows High Contrast Mode users will lose the custom colors and be left with browser defaults that may not match the design's contrast intent.
|
|
252
|
+
|
|
253
|
+
### Snippets
|
|
254
|
+
|
|
255
|
+
Strongly recommended two-layer focus ring. The `box-shadow` extends 4px in white around the element; the outline floats 2px outside the element edge, so the white halo sits between the element and the colored ring for surface-independent visibility:
|
|
256
|
+
|
|
257
|
+
```css
|
|
258
|
+
:focus-visible {
|
|
259
|
+
outline: 2px solid var(--focus-ring-color, #1a73e8);
|
|
260
|
+
outline-offset: 2px;
|
|
261
|
+
box-shadow: 0 0 0 4px #fff;
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Acceptable simpler alternative when the surrounding surfaces are known and contrast is verified:
|
|
266
|
+
|
|
267
|
+
```css
|
|
268
|
+
:focus-visible {
|
|
269
|
+
outline: 2px solid var(--focus-ring-color, #1a73e8);
|
|
270
|
+
outline-offset: 2px;
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Required forced-colors override — pair with either primary style above. `Highlight` maps to the OS-defined focus/selection color under Windows High Contrast Mode; `box-shadow: none` prevents doubled visuals under the system-managed palette:
|
|
275
|
+
|
|
276
|
+
```css
|
|
277
|
+
@media (forced-colors: active) {
|
|
278
|
+
:focus-visible {
|
|
279
|
+
outline: 2px solid Highlight;
|
|
280
|
+
outline-offset: 2px;
|
|
281
|
+
box-shadow: none;
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
```
|
|
285
|
+
|
|
286
|
+
### Acceptance Checks
|
|
287
|
+
- Every keyboard-focusable component shows a visible focus indicator when reached by keyboard navigation.
|
|
288
|
+
- The visible focus indicator remains present while the component has keyboard focus.
|
|
289
|
+
- The focus indicator has at least 3:1 contrast against adjacent colors.
|
|
290
|
+
- If a custom focus style is used, it is clearly visible and does not make focus harder to perceive than the default browser or platform behavior.
|
|
291
|
+
- Hover alone is not the only visible cue for the currently focused element.
|
|
292
|
+
- With Windows High Contrast Mode active (or emulated via a browser dev-tool forced-colors setting), the focus indicator remains clearly visible using the system `Highlight` color.
|