@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.
Files changed (47) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +62 -0
  3. package/corpus/web/react/components/accordion.basic.md +157 -0
  4. package/corpus/web/react/components/button.basic.md +91 -0
  5. package/corpus/web/react/components/button.toggle.md +109 -0
  6. package/corpus/web/react/components/carousel.dots.md +376 -0
  7. package/corpus/web/react/components/carousel.thumbnails.md +395 -0
  8. package/corpus/web/react/components/collection-row.basic.md +179 -0
  9. package/corpus/web/react/components/combobox.autocomplete.md +293 -0
  10. package/corpus/web/react/components/dialog.modal.md +202 -0
  11. package/corpus/web/react/components/dialog.nonmodal.md +184 -0
  12. package/corpus/web/react/components/disclosure.basic.md +97 -0
  13. package/corpus/web/react/components/grid.channel-guide.md +453 -0
  14. package/corpus/web/react/components/link.basic.md +105 -0
  15. package/corpus/web/react/components/listbox.basic.md +263 -0
  16. package/corpus/web/react/components/menu.basic.md +294 -0
  17. package/corpus/web/react/components/menu.menubar.md +296 -0
  18. package/corpus/web/react/components/navigation-menu.basic.md +349 -0
  19. package/corpus/web/react/components/navigation-menu.dropdown.md +220 -0
  20. package/corpus/web/react/components/select.basic.md +318 -0
  21. package/corpus/web/react/components/select.native.md +108 -0
  22. package/corpus/web/react/components/switch.basic.md +151 -0
  23. package/corpus/web/react/components/toast.basic.md +112 -0
  24. package/corpus/web/react/components/tooltip.basic.md +139 -0
  25. package/corpus/web/react/global/global_rules.md +292 -0
  26. package/corpus/web/react/patterns.json +946 -0
  27. package/dist/config.js +35 -0
  28. package/dist/contracts/v1/types.js +6 -0
  29. package/dist/http.js +103 -0
  30. package/dist/index.js +8 -0
  31. package/dist/mcp/createServer.js +122 -0
  32. package/dist/mcp/response.js +7 -0
  33. package/dist/mcp/server.js +15 -0
  34. package/dist/repo/cache.js +27 -0
  35. package/dist/repo/globalRules.js +304 -0
  36. package/dist/repo/index.js +178 -0
  37. package/dist/repo/paths.js +25 -0
  38. package/dist/repo/sections.js +166 -0
  39. package/dist/smoke/smoke-remote-mcp.js +43 -0
  40. package/dist/telemetry.js +15 -0
  41. package/dist/telemetryWrap.js +48 -0
  42. package/dist/tools/getGlobalRules.js +33 -0
  43. package/dist/tools/getPattern.js +54 -0
  44. package/dist/tools/listPatterns.js +33 -0
  45. package/dist/utils/fs.js +18 -0
  46. package/dist/utils/hash.js +4 -0
  47. package/package.json +68 -0
@@ -0,0 +1,151 @@
1
+ ---
2
+ id: switch.basic
3
+ title: Switch
4
+ stack: web/react
5
+ status: beta
6
+ latest_version: 0.3.0
7
+ tags: [switch, form, settings, on-off, form-control, toggle]
8
+ aliases: [toggle switch, preference toggle]
9
+ summary: Two-state on/off control representing a persistent setting. Uses role="switch" with aria-checked, or native checkbox semantics when applicable.
10
+ ---
11
+
12
+ # Switch
13
+
14
+ Pattern ID: `switch.basic`
15
+
16
+ Two-state on/off control representing a persistent setting. Uses `role="switch"` with `aria-checked`, or native checkbox semantics when applicable.
17
+
18
+ ## Use When
19
+ - Use when a control represents a persistent binary setting that remains on or off beyond the current interaction (e.g., "Enable notifications", "Dark mode").
20
+ - Use when the setting takes effect immediately when toggled, without requiring form submission.
21
+ - Use when the control reflects the current state of a system or application preference.
22
+
23
+ ## Do Not Use When
24
+ - Do not use when the control triggers an in-place action or transient feature toggle within the current context (use `button.toggle`).
25
+ - Do not use when the choice is a value submitted with a form rather than a setting that takes effect immediately (use `checkbox.basic`).
26
+ - Do not use when selecting one or more options from a set of related choices (use `checkbox.group`).
27
+ - Do not use when more than two states are required (use `button.toggle`).
28
+
29
+ ## Must Haves
30
+ - The switch has `role="switch"`.
31
+ - The switch has an associated visible text label.
32
+ - The switch has an accessible name that describes its action. Often this is worded to be true when the switch is set to "on" (e.g., "Enable notifications").
33
+ - The accessible name is equivalent to the visible text label.
34
+ - When additional context is needed beyond the visible text, add it via `aria-label`, `aria-labelledby`, or offscreen text (i.e., `.sr-only`). The visible text appears at the start of the accessible name.
35
+ - When on, the switch has `aria-checked="true"`. When off, the switch has `aria-checked="false"`.
36
+ - If the switch is implemented as `input[type="checkbox"]`, use the native `checked` attribute instead of `aria-checked`.
37
+ - The switch must be focusable:
38
+ - Native input (i.e., `input[type="checkbox"]`) is focusable by default.
39
+ - Non-native elements must include `tabIndex="0"`.
40
+ - Keyboard:
41
+ - Tab/Shift+Tab moves focus to the switch.
42
+ - Space toggles the switch.
43
+ - Enter toggles the switch.
44
+ - Exception: If using native input `input[type="checkbox"]`, then only Space toggles the switch, not Enter.
45
+ - If multiple switches are presented as a labeled set:
46
+ - Use `fieldset` with `legend`, or
47
+ - Wrap in `role="group"` with `aria-labelledby`.
48
+ - If additional descriptive static text is relevant to a switch or switch group, associate it using `aria-describedby`.
49
+ - Ensure a visible focus state (e.g., a 2px solid outline offset by 1-2px) around the switch control.
50
+
51
+ ## Customizable
52
+ - The base element: `<div role="switch">` (as in the golden pattern), `<button role="switch">`, or `<input type="checkbox" role="switch">`. Native elements reduce the keyboard and focus wiring that must be hand-rolled.
53
+ - A native HTML switch control (`<input type="checkbox" switch>`) is emerging in browsers (WebKit ships an implementation) and may become the preferred base once support is broad.
54
+ - Whether the visual design resembles a sliding switch.
55
+ - Whether the accessible name is contained within the switch or referenced externally.
56
+ - Whether state text ("On"/"Off") is visually displayed.
57
+ - Whether multiple switches may be grouped.
58
+
59
+ ## Don'ts
60
+ - Do not use a switch for non-setting actions.
61
+ - Do not omit `aria-checked` when using `div` or `button` with `role="switch"`.
62
+ - Do not use both `checked` and `aria-checked` on `input[type="checkbox"]`.
63
+ - Do not use a switch to trigger actions; use it only for persistent on/off settings.
64
+
65
+ ## Golden Pattern
66
+
67
+ Structural reference for AI coding assistants — semantics, focus, and keyboard behavior. Styling, copy, and demo data are illustrative.
68
+
69
+ ```jsx
70
+ "use client";
71
+
72
+ export function SwitchDemo() {
73
+ const [notifications, setNotifications] = useState(false);
74
+
75
+ function toggle() {
76
+ setNotifications((v) => !v);
77
+ }
78
+
79
+ return (
80
+ <div>
81
+ {/*
82
+ This example uses a <div>.
83
+ The same pattern may also be implemented using:
84
+ - <button role="switch">, or
85
+ - <input type="checkbox" role="switch">
86
+ */}
87
+
88
+ <div
89
+ role="switch"
90
+ aria-checked={notifications ? "true" : "false"}
91
+ tabIndex={0}
92
+ aria-labelledby="sw-label"
93
+ onClick={toggle}
94
+ onKeyDown={(e) => {
95
+ if (e.key === " " || e.key === "Enter") {
96
+ e.preventDefault();
97
+ toggle();
98
+ }
99
+ }}
100
+ >
101
+ <span id="sw-label">Notifications</span>
102
+ <span aria-hidden="true">
103
+ {notifications ? "On" : "Off"}
104
+ </span>
105
+ </div>
106
+
107
+ <fieldset>
108
+ <legend>Playback Settings</legend>
109
+ <p id="playback-desc">
110
+ These settings apply to all videos.
111
+ </p>
112
+
113
+ <div
114
+ role="switch"
115
+ aria-checked="true"
116
+ tabIndex={0}
117
+ aria-labelledby="autoplay-label"
118
+ aria-describedby="playback-desc"
119
+ >
120
+ <span id="autoplay-label">Autoplay</span>
121
+ <span aria-hidden="true">On</span>
122
+ </div>
123
+
124
+ <div
125
+ role="switch"
126
+ aria-checked="false"
127
+ tabIndex={0}
128
+ aria-labelledby="captions-label"
129
+ aria-describedby="playback-desc"
130
+ >
131
+ <span id="captions-label">Always show captions</span>
132
+ <span aria-hidden="true">Off</span>
133
+ </div>
134
+ </fieldset>
135
+ </div>
136
+ );
137
+ }
138
+ ```
139
+
140
+ ## Acceptance Checks
141
+
142
+ - Keyboard
143
+ - Tab moves focus to each switch.
144
+ - Space toggles state.
145
+ - Enter toggles state.
146
+ - Focus remains on the switch after toggling.
147
+ - Screen Reader
148
+ - Switch is announced with its accessible name and role ("switch").
149
+ - State is announced correctly as on/off.
150
+ - Group label is announced when using `fieldset/legend` or `role="group"`.
151
+ - Additional descriptive text is announced when associated via `aria-describedby`.
@@ -0,0 +1,112 @@
1
+ ---
2
+ id: toast.basic
3
+ title: Toast
4
+ stack: web/react
5
+ status: beta
6
+ latest_version: 0.1.0
7
+ tags: [toast, notification, status, live-region]
8
+ aliases: [notification, toast, transient message, status message]
9
+ summary: Temporary, non-blocking status message announced via live region. May include optional dismiss control. Disappears automatically.
10
+ ---
11
+
12
+ # Toast
13
+
14
+ Pattern ID: `toast.basic`
15
+
16
+ Temporary, non-blocking status message announced via live region. May include optional dismiss control. Disappears automatically.
17
+
18
+ ## Use When
19
+
20
+ - Use when presenting a temporary, non-blocking status message.
21
+ - Use when the message confirms an action (e.g., "Saved", "Added to watchlist").
22
+ - Use when the message is text-only and contains no required actions, except for (at most) a dismiss button.
23
+
24
+ ## Do Not Use When
25
+ - Do not use when the message requires user acknowledgment, moves keyboard focus, or blocks background interaction (use `dialog`).
26
+ - Do not use when the message is urgent or must interrupt the user immediately (use `dialog.alert`).
27
+ - Do not use when the message includes required actions or interactive controls beyond simple dismissal (use `snackbar`).
28
+
29
+ ## Must Haves
30
+
31
+ - The live region container must be present in the DOM when the page/view loads.
32
+ - The toast message must be announced via `role="status"`, or an equivalent such as `aria-live="polite"` and `aria-atomic="true"`.
33
+ - When a toast is triggered, its message text must be injected into the existing live region container.
34
+ - The toast must not move focus automatically when it appears.
35
+ - The toast must disappear automatically (recommended ~5 seconds).
36
+ - The live region text must be cleared when the toast dismisses to avoid stale messages being discovered later.
37
+ - If a dismiss button is present, it must not steal focus when the toast appears.
38
+
39
+ ## Customizable
40
+
41
+ - Visual placement (top-right, bottom-center, etc.).
42
+ - Duration (within reasonable non-disruptive bounds).
43
+ - Whether a dismiss button is included.
44
+ - Styling (color, elevation, animation).
45
+
46
+ ## Don'ts
47
+
48
+ - Do not mount/unmount the live region container based on toast visibility.
49
+ - Do not move focus into the toast when it appears.
50
+ - Do not use `role="alertdialog"`.
51
+ - The toast must not contain buttons or elements that require user action (use `snackbar` or `dialog` instead).
52
+
53
+ ## Golden Pattern
54
+
55
+ Structural reference for AI coding assistants — semantics, focus, and keyboard behavior. Styling, copy, and demo data are illustrative.
56
+
57
+ ```jsx
58
+ "use client";
59
+
60
+ const ToastContext = createContext(() => {});
61
+
62
+ export function ToastProvider({ children }) {
63
+ const [message, setMessage] = useState("");
64
+ const timeoutRef = useRef(null);
65
+
66
+ const showToast = useCallback((text) => {
67
+ if (timeoutRef.current) window.clearTimeout(timeoutRef.current);
68
+ setMessage(text);
69
+ timeoutRef.current = window.setTimeout(() => setMessage(""), 5000);
70
+ }, []);
71
+
72
+ return (
73
+ <ToastContext.Provider value={showToast}>
74
+ {children}
75
+
76
+ {/* Live region is always mounted */}
77
+ <div role="status" aria-atomic="true">
78
+ {message}
79
+ </div>
80
+ </ToastContext.Provider>
81
+ );
82
+ }
83
+
84
+ export function useToast() {
85
+ return useContext(ToastContext);
86
+ }
87
+
88
+ /** Demo */
89
+ export function ToastDemo() {
90
+ const toast = useToast();
91
+
92
+ return (
93
+ <div>
94
+ <button type="button" onClick={() => toast("Saved successfully.")}>
95
+ Save
96
+ </button>
97
+ </div>
98
+ );
99
+ }
100
+ ```
101
+
102
+ ## Acceptance Checks
103
+
104
+ - Keyboard
105
+ - Trigger a toast via keyboard.
106
+ - Focus remains on the triggering control.
107
+ - No focus is moved into the toast.
108
+ - Screen Reader
109
+ - When triggered, the message is announced once as a polite status update.
110
+ - The message is not announced repeatedly.
111
+ - After the toast auto-dismisses, the live region is cleared (stale text is not discoverable later).
112
+ - Triggering a new toast replaces the previous announcement.
@@ -0,0 +1,139 @@
1
+ ---
2
+ id: tooltip.basic
3
+ title: Tooltip
4
+ stack: web/react
5
+ status: beta
6
+ latest_version: 0.1.0
7
+ tags: [tooltip, aria-describedby, hover, focus, supplementary-label]
8
+ aliases: [tooltip, hint, help bubble, info bubble, hover text, popup label, title text, describedby]
9
+ summary: A brief supplementary text label shown on hover and focus of its trigger; uses role="tooltip" referenced by aria-describedby, and is hoverable, dismissible, and persistent.
10
+ ---
11
+
12
+ # Tooltip
13
+
14
+ Pattern ID: `tooltip.basic`
15
+
16
+ A brief supplementary text label shown on hover and focus of its trigger; uses `role="tooltip"` referenced by `aria-describedby`, and is hoverable, dismissible, and persistent.
17
+
18
+ ## Use When
19
+
20
+ - Use when a brief plain-text label supplements a control (e.g., clarifying an icon-only button, a short hint on a form field).
21
+ - Use when the content is non-essential supplementary text.
22
+ - Use when the label should appear on both pointer hover and keyboard focus of the trigger.
23
+
24
+ ## Do Not Use When
25
+
26
+ - Do not use when the content is interactive or rich, containing links or buttons (use `dialog.nonmodal`).
27
+ - Do not use when the content is a status or confirmation message (use `toast.basic`).
28
+ - Do not use when the label would be the control's only accessible name; give the control a real accessible name via a visible label or `aria-label` instead (use `button.basic`).
29
+ - Do not use when the content is a menu of choices (use `menu.basic`).
30
+
31
+ ## Must Haves
32
+
33
+ - The tooltip bubble is an element with `role="tooltip"` and a stable `id`.
34
+ - The trigger references the tooltip with `aria-describedby` pointing to the tooltip's `id`.
35
+ - The tooltip supplements the accessible name, it does not replace it. The trigger has its own accessible name (e.g., visible text or `aria-label`).
36
+ - The tooltip appears on both pointer hover and keyboard focus of the trigger.
37
+ - Esc dismisses the tooltip whether it was opened by pointer hover or by keyboard focus, and does not move pointer hover or keyboard focus (dismissible).
38
+ - Bind the Esc handler at the document level, not only on the trigger, so it works when the tooltip is shown by hover and the trigger does not hold focus.
39
+ - When the trigger has keyboard focus, the trigger keeps focus after dismissal.
40
+ - The tooltip remains visible while the pointer is over the trigger or over the tooltip, and while the trigger has focus (hoverable and persistent).
41
+ - Track hover and focus as independent conditions and keep the tooltip open while either is active, so a stray pointer movement does not hide it while the trigger still has keyboard focus.
42
+ - The tooltip is not focusable and contains plain text only, with no interactive content.
43
+ - Ensure a visible focus state (e.g., a 2px solid outline offset by 1-2px) around the trigger.
44
+
45
+ ## Customizable
46
+
47
+ - `aria-describedby` associates the tooltip as a supplementary description. When the tooltip text is intended to be the control's accessible name (rare), `aria-labelledby` may be used instead of `aria-describedby`.
48
+ - Positioning relative to the trigger and an optional show delay are at the engineer's discretion, as long as the tooltip remains hoverable, dismissible, and persistent.
49
+ - The tooltip may be delivered via scripting or via the native `popover` attribute.
50
+
51
+ ## Don'ts
52
+
53
+ - Do not place interactive content (links, buttons, form controls) in a tooltip.
54
+ - Do not rely on the native `title` attribute as the tooltip; it is not keyboard-accessible and is announced inconsistently.
55
+ - Do not make the tooltip vanish when the pointer moves from the trigger toward the tooltip; it must be hoverable.
56
+ - Do not show the tooltip on hover only; it must also appear on keyboard focus.
57
+ - Do not hide the tooltip on `onMouseLeave` while the trigger still has keyboard focus; hover and focus must be tracked independently.
58
+ - Do not use `role="menu"` on the tooltip.
59
+ - Do not leave the tooltip visible after the trigger has lost focus and the pointer has left both the trigger and the tooltip.
60
+
61
+ ## Golden Pattern
62
+
63
+ Structural reference for AI coding assistants — semantics, focus, and keyboard behavior. Styling, copy, and demo data are illustrative.
64
+
65
+ ```jsx
66
+ "use client";
67
+
68
+ export function TooltipDemo() {
69
+ // Hover and focus are tracked as independent conditions. The tooltip stays
70
+ // open while either is active (persistent), so a stray pointer movement
71
+ // cannot hide it while the trigger still holds keyboard focus.
72
+ const [hovered, setHovered] = useState(false);
73
+ const [focused, setFocused] = useState(false);
74
+ const open = hovered || focused;
75
+ const tooltipId = "tooltip-watchlist";
76
+
77
+ // Dismissible (WCAG 1.4.13): Esc closes the tooltip without moving pointer
78
+ // hover or keyboard focus. The listener is on the document, not the trigger,
79
+ // because a hover-only tooltip has no focused element to receive the keydown.
80
+ useEffect(() => {
81
+ if (!open) return;
82
+ const onKeyDown = (e) => {
83
+ if (e.key === "Escape") {
84
+ setHovered(false);
85
+ setFocused(false);
86
+ }
87
+ };
88
+ document.addEventListener("keydown", onKeyDown);
89
+ return () => document.removeEventListener("keydown", onKeyDown);
90
+ }, [open]);
91
+
92
+ return (
93
+ // Wrapping the trigger and the bubble in one hover region keeps the tooltip
94
+ // open while the pointer moves from the trigger onto the bubble (hoverable).
95
+ <span
96
+ style={{ position: "relative", display: "inline-block" }}
97
+ onMouseEnter={() => setHovered(true)}
98
+ onMouseLeave={() => setHovered(false)}
99
+ >
100
+ {/* The trigger carries its own accessible name; the tooltip only supplements it. */}
101
+ <button
102
+ type="button"
103
+ aria-label="Add to watchlist"
104
+ aria-describedby={tooltipId}
105
+ onFocus={() => setFocused(true)}
106
+ onBlur={() => setFocused(false)}
107
+ onClick={() => alert("Added to watchlist")}
108
+ >
109
+ <span aria-hidden="true">[icon]</span>
110
+ </button>
111
+
112
+ {/* Plain-text bubble, not focusable, referenced by aria-describedby. */}
113
+ {open && (
114
+ <span
115
+ role="tooltip"
116
+ id={tooltipId}
117
+ style={{ position: "absolute", top: "100%", left: 0 }}
118
+ >
119
+ Adds this title to your watchlist.
120
+ </span>
121
+ )}
122
+ </span>
123
+ );
124
+ }
125
+ ```
126
+
127
+ ## Acceptance Checks
128
+
129
+ - Keyboard
130
+ - A visible focus indicator is present on the trigger when reached by keyboard.
131
+ - Focusing the trigger shows the tooltip.
132
+ - Esc dismisses the tooltip while focus stays on the trigger.
133
+ - A stray pointer movement over and off the trigger does not hide the tooltip while the trigger still has focus.
134
+ - Pointer
135
+ - Hovering the trigger shows the tooltip.
136
+ - The pointer can move onto the tooltip without dismissing it.
137
+ - Pressing Esc while the tooltip is shown by hover dismisses it without moving the pointer.
138
+ - Screen Reader
139
+ - The tooltip text is announced as the trigger's description via `aria-describedby`.