@tamsensedev/runner 0.13.0 → 0.18.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 (82) hide show
  1. package/LICENSE +5 -5
  2. package/README.md +61 -57
  3. package/dist/cli.js +646 -315
  4. package/dist/cli.js.map +1 -1
  5. package/dist/recorder.cjs.map +1 -1
  6. package/library/.claude-plugin/plugin.json +5 -5
  7. package/library/THIRD_PARTY_NOTICES.md +123 -123
  8. package/library/agents/code-architect.md +67 -67
  9. package/library/agents/code-explorer.md +61 -61
  10. package/library/agents/code-reviewer.md +73 -73
  11. package/library/agents/code-simplifier.md +62 -62
  12. package/library/agents/design-critic.md +52 -52
  13. package/library/agents/second-opinion.md +64 -64
  14. package/library/agents/security-auditor.md +86 -86
  15. package/library/agents/silent-failure-hunter.md +70 -70
  16. package/library/agents/test-coverage-analyzer.md +78 -78
  17. package/library/agents/test-runner.md +48 -48
  18. package/library/agents/ui-tester.md +50 -50
  19. package/library/licenses/Apache-2.0.txt +202 -202
  20. package/library/skills/accessibility/SKILL.md +144 -144
  21. package/library/skills/accessibility/references/wcag-checks.md +168 -168
  22. package/library/skills/addressing-review-feedback/SKILL.md +109 -109
  23. package/library/skills/app-screens/SKILL.md +96 -96
  24. package/library/skills/app-screens/references/shell-kpis-charts.md +108 -108
  25. package/library/skills/app-screens/references/states-density-phone.md +54 -54
  26. package/library/skills/app-screens/references/tables-forms-settings.md +104 -104
  27. package/library/skills/avoiding-generic-ai-design/SKILL.md +92 -92
  28. package/library/skills/avoiding-generic-ai-design/references/tells.md +112 -112
  29. package/library/skills/clean-commits/SKILL.md +85 -85
  30. package/library/skills/design-review/SKILL.md +107 -107
  31. package/library/skills/design-review/references/critique-passes.md +101 -101
  32. package/library/skills/design-review/references/screenshot-procedure.md +121 -121
  33. package/library/skills/differential-security-review/LICENSE +427 -427
  34. package/library/skills/differential-security-review/SKILL.md +105 -105
  35. package/library/skills/differential-security-review/references/adversarial.md +115 -115
  36. package/library/skills/differential-security-review/references/methodology.md +145 -145
  37. package/library/skills/differential-security-review/references/patterns.md +108 -108
  38. package/library/skills/false-positive-check/LICENSE +427 -427
  39. package/library/skills/false-positive-check/SKILL.md +95 -95
  40. package/library/skills/false-positive-check/references/bug-class-verification.md +128 -128
  41. package/library/skills/false-positive-check/references/deep-verification.md +113 -113
  42. package/library/skills/false-positive-check/references/evidence-templates.md +93 -93
  43. package/library/skills/false-positive-check/references/false-positive-patterns.md +122 -122
  44. package/library/skills/false-positive-check/references/gate-reviews.md +36 -36
  45. package/library/skills/false-positive-check/references/standard-verification.md +80 -80
  46. package/library/skills/focused-implementation/SKILL.md +104 -104
  47. package/library/skills/implementation-plan/SKILL.md +66 -66
  48. package/library/skills/interface-design/SKILL.md +144 -144
  49. package/library/skills/interface-design/references/craft-defaults.md +130 -130
  50. package/library/skills/interface-design/references/new-direction.md +62 -62
  51. package/library/skills/interface-design/references/writing.md +52 -52
  52. package/library/skills/interface-polish/SKILL.md +103 -103
  53. package/library/skills/interface-polish/references/detail-rules.md +154 -154
  54. package/library/skills/interface-polish/references/worst-case-data.md +158 -158
  55. package/library/skills/motion-design/SKILL.md +106 -106
  56. package/library/skills/motion-design/references/recipes.md +243 -243
  57. package/library/skills/postgres-performance/SKILL.md +97 -97
  58. package/library/skills/postgres-performance/references/rules.md +265 -265
  59. package/library/skills/project-orientation/SKILL.md +77 -77
  60. package/library/skills/reviewing-changes/SKILL.md +183 -183
  61. package/library/skills/safe-database-changes/SKILL.md +111 -111
  62. package/library/skills/safe-database-changes/references/backfills.md +75 -75
  63. package/library/skills/safe-database-changes/references/migration-tools.md +69 -69
  64. package/library/skills/safe-database-changes/references/postgres-ddl.md +199 -199
  65. package/library/skills/security-review/SKILL.md +132 -132
  66. package/library/skills/security-review/references/checks-by-area.md +107 -107
  67. package/library/skills/security-review/references/do-not-report.md +86 -86
  68. package/library/skills/spec-writing/SKILL.md +144 -144
  69. package/library/skills/systematic-debugging/SKILL.md +130 -130
  70. package/library/skills/systematic-debugging/references/flaky-tests.md +51 -51
  71. package/library/skills/systematic-debugging/references/root-cause-tracing.md +55 -55
  72. package/library/skills/task-interview/SKILL.md +84 -84
  73. package/library/skills/test-driven-development/SKILL.md +116 -116
  74. package/library/skills/test-driven-development/references/writing-good-tests.md +85 -85
  75. package/library/skills/ui-testing/SKILL.md +102 -102
  76. package/library/skills/ui-testing/references/headless-check.md +295 -295
  77. package/library/skills/ui-testing/references/local-stand.md +51 -51
  78. package/library/skills/ui-testing/references/playwright-patterns.md +134 -134
  79. package/library/skills/ui-testing/references/recording.md +61 -61
  80. package/library/skills/verification-before-completion/SKILL.md +113 -113
  81. package/library/skills/web-interface-guidelines/SKILL.md +202 -202
  82. package/package.json +46 -46
@@ -1,168 +1,168 @@
1
- # WCAG 2.2 checks, patterns and scripts
2
-
3
- Read this when you need the exact criterion for a finding, a correct markup pattern for a fix, a contrast calculation, or the keyboard pass for the headless check.
4
-
5
- ## Level A and AA criteria that matter most for web apps
6
-
7
- | Criterion | Level | What to check |
8
- |---|---|---|
9
- | 1.1.1 Non-text Content | A | Alt text on meaningful images, names on icon buttons, `alt=""` on decorative images |
10
- | 1.2.2 Captions (Prerecorded) | A | Videos with speech have captions |
11
- | 1.3.1 Info and Relationships | A | Headings, lists, tables, labels and groups exist in markup, not only in styling |
12
- | 1.3.2 Meaningful Sequence | A | DOM order matches reading order (CSS `order` and grid placement can break it) |
13
- | 1.3.5 Identify Input Purpose | AA | `autocomplete` on fields about the user |
14
- | 1.4.1 Use of Color | A | Colour is not the only way information is shown |
15
- | 1.4.3 Contrast (Minimum) | AA | 4.5:1 text, 3:1 large text (24 px, or 18.66 px bold) |
16
- | 1.4.4 Resize Text | AA | Usable at 200% zoom |
17
- | 1.4.10 Reflow | AA | No two-dimensional scrolling at 320 CSS px wide (except maps, tables, editors) |
18
- | 1.4.11 Non-text Contrast | AA | 3:1 for control boundaries, focus indicators, meaningful icons and graphics |
19
- | 1.4.12 Text Spacing | AA | No clipped text when line and letter spacing increase (avoid fixed heights on text boxes) |
20
- | 1.4.13 Content on Hover or Focus | AA | Tooltips and popovers can be dismissed (Escape), hovered and stay until dismissed |
21
- | 2.1.1 Keyboard | A | Every function works with the keyboard |
22
- | 2.1.2 No Keyboard Trap | A | Focus can always move away |
23
- | 2.1.4 Character Key Shortcuts | A | Single-key shortcuts can be turned off or only work when a component has focus |
24
- | 2.2.1 Timing Adjustable | A | Time limits can be extended |
25
- | 2.2.2 Pause, Stop, Hide | A | Moving or auto-updating content can be paused |
26
- | 2.3.1 Three Flashes | A | Nothing flashes more than three times per second |
27
- | 2.4.1 Bypass Blocks | A | Skip link or landmarks |
28
- | 2.4.2 Page Titled | A | Descriptive, unique titles per route |
29
- | 2.4.3 Focus Order | A | Order preserves meaning; dialogs and route changes manage focus |
30
- | 2.4.4 Link Purpose (In Context) | A | Link text says where it goes |
31
- | 2.4.6 Headings and Labels | AA | Headings and labels describe their content |
32
- | 2.4.7 Focus Visible | AA | A visible focus indicator |
33
- | 2.4.11 Focus Not Obscured (Minimum) | AA | Focused element not fully hidden by sticky or overlay content |
34
- | 2.5.1 Pointer Gestures | A | Multi-finger or path gestures have a single-pointer alternative |
35
- | 2.5.2 Pointer Cancellation | A | Actions fire on release, not on press |
36
- | 2.5.3 Label in Name | A | Accessible name contains the visible label |
37
- | 2.5.7 Dragging Movements | AA | Dragging has a single-pointer alternative |
38
- | 2.5.8 Target Size (Minimum) | AA | 24 by 24 CSS px, or enough spacing |
39
- | 3.1.1 Language of Page | A | `<html lang>` |
40
- | 3.1.2 Language of Parts | AA | `lang` on passages in another language |
41
- | 3.2.1 On Focus / 3.2.2 On Input | A | Focusing or changing a field does not navigate or submit unexpectedly |
42
- | 3.2.3 Consistent Navigation | AA | Navigation in the same order across pages |
43
- | 3.2.6 Consistent Help | A | Help links or contact options in the same place across pages |
44
- | 3.3.1 Error Identification | A | Errors described in text and tied to the field |
45
- | 3.3.2 Labels or Instructions | A | Labels and format hints |
46
- | 3.3.3 Error Suggestion | AA | Say how to fix it when known |
47
- | 3.3.4 Error Prevention (Legal, Financial, Data) | AA | Review, confirm or undo for important submissions |
48
- | 3.3.7 Redundant Entry | A | Do not ask again for data entered earlier in the same process |
49
- | 3.3.8 Accessible Authentication (Minimum) | AA | No memory or puzzle test without an alternative; allow paste and password managers |
50
- | 4.1.2 Name, Role, Value | A | Custom controls expose name, role and state |
51
- | 4.1.3 Status Messages | AA | Status updates announced without moving focus |
52
-
53
- Criteria 2.4.12, 2.4.13 and 3.3.9 are AAA and new in 2.2; 4.1.1 Parsing was removed in 2.2, so do not report it.
54
-
55
- ## Patterns
56
-
57
- **Visually hidden text** (for screen readers only):
58
-
59
- ```css
60
- .visually-hidden {
61
- position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
62
- overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
63
- }
64
- ```
65
-
66
- Use the project's existing utility if it has one (`sr-only`, `visually-hidden`, a component).
67
-
68
- **Dialog:** prefer native `<dialog>` opened with `showModal()`, which makes the rest of the page inert and closes on Escape. Give it a name (`aria-labelledby` pointing at its heading), move focus inside on open, and return focus to the trigger on close. A custom dialog needs `role="dialog"`, `aria-modal="true"`, a focus trap, Escape handling and an inert or `aria-hidden` background.
69
-
70
- **Skip link:**
71
-
72
- ```html
73
- <a class="skip-link" href="#main">Skip to main content</a>
74
- <main id="main" tabindex="-1">...</main>
75
- ```
76
-
77
- with CSS that keeps it off-screen until it receives focus.
78
-
79
- **Error summary on submit:** a heading like "2 problems need fixing" with links to each invalid field, focused after a failed submit; each field keeps its own inline error with `aria-describedby`.
80
-
81
- **Tabs:** `role="tablist"` containing `role="tab"` elements with `aria-selected` and `aria-controls`; panels with `role="tabpanel"` and `aria-labelledby`. Only the active tab has `tabindex="0"`; arrow keys move between tabs.
82
-
83
- **Disclosure and menus:** a button with `aria-expanded` that toggles the controlled region. Do not use `role="menu"` for site navigation; it promises application-style keyboard behaviour.
84
-
85
- **Live region:** render the container empty on page load, then change its text:
86
-
87
- ```html
88
- <div role="status" class="visually-hidden" id="announcer"></div>
89
- ```
90
-
91
- Clearing it before writing the same message again makes screen readers repeat it.
92
-
93
- **Reduced motion:**
94
-
95
- ```css
96
- @media (prefers-reduced-motion: reduce) {
97
- *, *::before, *::after {
98
- animation-duration: 0.01ms !important;
99
- animation-iteration-count: 1 !important;
100
- transition-duration: 0.01ms !important;
101
- scroll-behavior: auto !important;
102
- }
103
- }
104
- ```
105
-
106
- Or scope it to the animations the change adds, if the project handles motion per component.
107
-
108
- ## Contrast from design tokens
109
-
110
- When colours come from tokens or CSS variables, compute the ratio instead of guessing. Run with `node -e` or in a temp file:
111
-
112
- ```js
113
- function luminance(hex) {
114
- const value = hex.replace('#', '')
115
- const full = value.length === 3 ? value.split('').map(c => c + c).join('') : value
116
- const [r, g, b] = [0, 2, 4].map(i => parseInt(full.slice(i, i + 2), 16) / 255)
117
- .map(c => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4))
118
- return 0.2126 * r + 0.7152 * g + 0.0722 * b
119
- }
120
- function contrast(foreground, background) {
121
- const [light, dark] = [luminance(foreground), luminance(background)].sort((a, b) => b - a)
122
- return (light + 0.05) / (dark + 0.05)
123
- }
124
- console.log(contrast('#6b7280', '#ffffff').toFixed(2))
125
- ```
126
-
127
- Semi-transparent colours must be blended with their real background first. For dark mode, check both themes.
128
-
129
- ## Keyboard pass in a headless browser
130
-
131
- Use as the `steps` function of the `ui-testing` headless check. It records the first stops of the Tab order, whether each one shows a focus style, and whether Escape closes an open dialog. The focus-style test is a heuristic (it looks for an outline or box shadow); confirm doubtful cases on a screenshot.
132
-
133
- ```js
134
- async function steps(page) {
135
- const stops = []
136
- for (let i = 0; i < 25; i++) {
137
- await page.keyboard.press('Tab')
138
- const stop = await page.evaluate(() => {
139
- const el = document.activeElement
140
- if (!el || el === document.body) return null
141
- const style = getComputedStyle(el)
142
- const hasOutline = style.outlineStyle !== 'none' && parseFloat(style.outlineWidth) > 0
143
- const hasShadow = style.boxShadow && style.boxShadow !== 'none'
144
- const label = el.getAttribute('aria-label') || el.innerText || el.getAttribute('title') || el.getAttribute('name') || ''
145
- return {
146
- element: el.tagName.toLowerCase() + (el.getAttribute('role') ? `[role=${el.getAttribute('role')}]` : ''),
147
- name: label.trim().replace(/\s+/g, ' ').slice(0, 60),
148
- visibleFocus: Boolean(hasOutline || hasShadow),
149
- inViewport: (() => { const r = el.getBoundingClientRect(); return r.bottom > 0 && r.top < innerHeight })(),
150
- }
151
- })
152
- if (!stop) break
153
- stops.push(stop)
154
- }
155
- let escapeClosesDialog = null
156
- const dialog = page.locator('dialog[open], [role="dialog"]').first()
157
- if (await dialog.isVisible().catch(() => false)) {
158
- await dialog.locator('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])').first().focus().catch(() => {})
159
- await page.keyboard.press('Escape')
160
- escapeClosesDialog = !(await dialog.isVisible().catch(() => false))
161
- }
162
- return { stops, escapeClosesDialog }
163
- }
164
- ```
165
-
166
- Read the result: stops with an empty `name` are unnamed controls; `visibleFocus: false` needs a screenshot check; the order should follow the visual layout; stops that repeat in a loop inside one component suggest a trap. To test a dialog, open it in `steps` first (click its trigger), then run the same loop and check focus stays inside it and returns to the trigger after Escape.
167
-
168
- Adapted from addyosmani/web-quality-skills `skills/accessibility/references/WCAG.md` and `A11Y-PATTERNS.md` (MIT, commit afa8da942115f2961fdbfa80807ea0b232ff6c00, Copyright (c) 2026 Addy Osmani). Changed: limited to A and AA criteria relevant to web apps with "what to check" notes, patterns condensed, contrast and keyboard-pass scripts added.
1
+ # WCAG 2.2 checks, patterns and scripts
2
+
3
+ Read this when you need the exact criterion for a finding, a correct markup pattern for a fix, a contrast calculation, or the keyboard pass for the headless check.
4
+
5
+ ## Level A and AA criteria that matter most for web apps
6
+
7
+ | Criterion | Level | What to check |
8
+ |---|---|---|
9
+ | 1.1.1 Non-text Content | A | Alt text on meaningful images, names on icon buttons, `alt=""` on decorative images |
10
+ | 1.2.2 Captions (Prerecorded) | A | Videos with speech have captions |
11
+ | 1.3.1 Info and Relationships | A | Headings, lists, tables, labels and groups exist in markup, not only in styling |
12
+ | 1.3.2 Meaningful Sequence | A | DOM order matches reading order (CSS `order` and grid placement can break it) |
13
+ | 1.3.5 Identify Input Purpose | AA | `autocomplete` on fields about the user |
14
+ | 1.4.1 Use of Color | A | Colour is not the only way information is shown |
15
+ | 1.4.3 Contrast (Minimum) | AA | 4.5:1 text, 3:1 large text (24 px, or 18.66 px bold) |
16
+ | 1.4.4 Resize Text | AA | Usable at 200% zoom |
17
+ | 1.4.10 Reflow | AA | No two-dimensional scrolling at 320 CSS px wide (except maps, tables, editors) |
18
+ | 1.4.11 Non-text Contrast | AA | 3:1 for control boundaries, focus indicators, meaningful icons and graphics |
19
+ | 1.4.12 Text Spacing | AA | No clipped text when line and letter spacing increase (avoid fixed heights on text boxes) |
20
+ | 1.4.13 Content on Hover or Focus | AA | Tooltips and popovers can be dismissed (Escape), hovered and stay until dismissed |
21
+ | 2.1.1 Keyboard | A | Every function works with the keyboard |
22
+ | 2.1.2 No Keyboard Trap | A | Focus can always move away |
23
+ | 2.1.4 Character Key Shortcuts | A | Single-key shortcuts can be turned off or only work when a component has focus |
24
+ | 2.2.1 Timing Adjustable | A | Time limits can be extended |
25
+ | 2.2.2 Pause, Stop, Hide | A | Moving or auto-updating content can be paused |
26
+ | 2.3.1 Three Flashes | A | Nothing flashes more than three times per second |
27
+ | 2.4.1 Bypass Blocks | A | Skip link or landmarks |
28
+ | 2.4.2 Page Titled | A | Descriptive, unique titles per route |
29
+ | 2.4.3 Focus Order | A | Order preserves meaning; dialogs and route changes manage focus |
30
+ | 2.4.4 Link Purpose (In Context) | A | Link text says where it goes |
31
+ | 2.4.6 Headings and Labels | AA | Headings and labels describe their content |
32
+ | 2.4.7 Focus Visible | AA | A visible focus indicator |
33
+ | 2.4.11 Focus Not Obscured (Minimum) | AA | Focused element not fully hidden by sticky or overlay content |
34
+ | 2.5.1 Pointer Gestures | A | Multi-finger or path gestures have a single-pointer alternative |
35
+ | 2.5.2 Pointer Cancellation | A | Actions fire on release, not on press |
36
+ | 2.5.3 Label in Name | A | Accessible name contains the visible label |
37
+ | 2.5.7 Dragging Movements | AA | Dragging has a single-pointer alternative |
38
+ | 2.5.8 Target Size (Minimum) | AA | 24 by 24 CSS px, or enough spacing |
39
+ | 3.1.1 Language of Page | A | `<html lang>` |
40
+ | 3.1.2 Language of Parts | AA | `lang` on passages in another language |
41
+ | 3.2.1 On Focus / 3.2.2 On Input | A | Focusing or changing a field does not navigate or submit unexpectedly |
42
+ | 3.2.3 Consistent Navigation | AA | Navigation in the same order across pages |
43
+ | 3.2.6 Consistent Help | A | Help links or contact options in the same place across pages |
44
+ | 3.3.1 Error Identification | A | Errors described in text and tied to the field |
45
+ | 3.3.2 Labels or Instructions | A | Labels and format hints |
46
+ | 3.3.3 Error Suggestion | AA | Say how to fix it when known |
47
+ | 3.3.4 Error Prevention (Legal, Financial, Data) | AA | Review, confirm or undo for important submissions |
48
+ | 3.3.7 Redundant Entry | A | Do not ask again for data entered earlier in the same process |
49
+ | 3.3.8 Accessible Authentication (Minimum) | AA | No memory or puzzle test without an alternative; allow paste and password managers |
50
+ | 4.1.2 Name, Role, Value | A | Custom controls expose name, role and state |
51
+ | 4.1.3 Status Messages | AA | Status updates announced without moving focus |
52
+
53
+ Criteria 2.4.12, 2.4.13 and 3.3.9 are AAA and new in 2.2; 4.1.1 Parsing was removed in 2.2, so do not report it.
54
+
55
+ ## Patterns
56
+
57
+ **Visually hidden text** (for screen readers only):
58
+
59
+ ```css
60
+ .visually-hidden {
61
+ position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
62
+ overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
63
+ }
64
+ ```
65
+
66
+ Use the project's existing utility if it has one (`sr-only`, `visually-hidden`, a component).
67
+
68
+ **Dialog:** prefer native `<dialog>` opened with `showModal()`, which makes the rest of the page inert and closes on Escape. Give it a name (`aria-labelledby` pointing at its heading), move focus inside on open, and return focus to the trigger on close. A custom dialog needs `role="dialog"`, `aria-modal="true"`, a focus trap, Escape handling and an inert or `aria-hidden` background.
69
+
70
+ **Skip link:**
71
+
72
+ ```html
73
+ <a class="skip-link" href="#main">Skip to main content</a>
74
+ <main id="main" tabindex="-1">...</main>
75
+ ```
76
+
77
+ with CSS that keeps it off-screen until it receives focus.
78
+
79
+ **Error summary on submit:** a heading like "2 problems need fixing" with links to each invalid field, focused after a failed submit; each field keeps its own inline error with `aria-describedby`.
80
+
81
+ **Tabs:** `role="tablist"` containing `role="tab"` elements with `aria-selected` and `aria-controls`; panels with `role="tabpanel"` and `aria-labelledby`. Only the active tab has `tabindex="0"`; arrow keys move between tabs.
82
+
83
+ **Disclosure and menus:** a button with `aria-expanded` that toggles the controlled region. Do not use `role="menu"` for site navigation; it promises application-style keyboard behaviour.
84
+
85
+ **Live region:** render the container empty on page load, then change its text:
86
+
87
+ ```html
88
+ <div role="status" class="visually-hidden" id="announcer"></div>
89
+ ```
90
+
91
+ Clearing it before writing the same message again makes screen readers repeat it.
92
+
93
+ **Reduced motion:**
94
+
95
+ ```css
96
+ @media (prefers-reduced-motion: reduce) {
97
+ *, *::before, *::after {
98
+ animation-duration: 0.01ms !important;
99
+ animation-iteration-count: 1 !important;
100
+ transition-duration: 0.01ms !important;
101
+ scroll-behavior: auto !important;
102
+ }
103
+ }
104
+ ```
105
+
106
+ Or scope it to the animations the change adds, if the project handles motion per component.
107
+
108
+ ## Contrast from design tokens
109
+
110
+ When colours come from tokens or CSS variables, compute the ratio instead of guessing. Run with `node -e` or in a temp file:
111
+
112
+ ```js
113
+ function luminance(hex) {
114
+ const value = hex.replace('#', '')
115
+ const full = value.length === 3 ? value.split('').map(c => c + c).join('') : value
116
+ const [r, g, b] = [0, 2, 4].map(i => parseInt(full.slice(i, i + 2), 16) / 255)
117
+ .map(c => (c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4))
118
+ return 0.2126 * r + 0.7152 * g + 0.0722 * b
119
+ }
120
+ function contrast(foreground, background) {
121
+ const [light, dark] = [luminance(foreground), luminance(background)].sort((a, b) => b - a)
122
+ return (light + 0.05) / (dark + 0.05)
123
+ }
124
+ console.log(contrast('#6b7280', '#ffffff').toFixed(2))
125
+ ```
126
+
127
+ Semi-transparent colours must be blended with their real background first. For dark mode, check both themes.
128
+
129
+ ## Keyboard pass in a headless browser
130
+
131
+ Use as the `steps` function of the `ui-testing` headless check. It records the first stops of the Tab order, whether each one shows a focus style, and whether Escape closes an open dialog. The focus-style test is a heuristic (it looks for an outline or box shadow); confirm doubtful cases on a screenshot.
132
+
133
+ ```js
134
+ async function steps(page) {
135
+ const stops = []
136
+ for (let i = 0; i < 25; i++) {
137
+ await page.keyboard.press('Tab')
138
+ const stop = await page.evaluate(() => {
139
+ const el = document.activeElement
140
+ if (!el || el === document.body) return null
141
+ const style = getComputedStyle(el)
142
+ const hasOutline = style.outlineStyle !== 'none' && parseFloat(style.outlineWidth) > 0
143
+ const hasShadow = style.boxShadow && style.boxShadow !== 'none'
144
+ const label = el.getAttribute('aria-label') || el.innerText || el.getAttribute('title') || el.getAttribute('name') || ''
145
+ return {
146
+ element: el.tagName.toLowerCase() + (el.getAttribute('role') ? `[role=${el.getAttribute('role')}]` : ''),
147
+ name: label.trim().replace(/\s+/g, ' ').slice(0, 60),
148
+ visibleFocus: Boolean(hasOutline || hasShadow),
149
+ inViewport: (() => { const r = el.getBoundingClientRect(); return r.bottom > 0 && r.top < innerHeight })(),
150
+ }
151
+ })
152
+ if (!stop) break
153
+ stops.push(stop)
154
+ }
155
+ let escapeClosesDialog = null
156
+ const dialog = page.locator('dialog[open], [role="dialog"]').first()
157
+ if (await dialog.isVisible().catch(() => false)) {
158
+ await dialog.locator('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])').first().focus().catch(() => {})
159
+ await page.keyboard.press('Escape')
160
+ escapeClosesDialog = !(await dialog.isVisible().catch(() => false))
161
+ }
162
+ return { stops, escapeClosesDialog }
163
+ }
164
+ ```
165
+
166
+ Read the result: stops with an empty `name` are unnamed controls; `visibleFocus: false` needs a screenshot check; the order should follow the visual layout; stops that repeat in a loop inside one component suggest a trap. To test a dialog, open it in `steps` first (click its trigger), then run the same loop and check focus stays inside it and returns to the trigger after Escape.
167
+
168
+ Adapted from addyosmani/web-quality-skills `skills/accessibility/references/WCAG.md` and `A11Y-PATTERNS.md` (MIT, commit afa8da942115f2961fdbfa80807ea0b232ff6c00, Copyright (c) 2026 Addy Osmani). Changed: limited to A and AA criteria relevant to web apps with "what to check" notes, patterns condensed, contrast and keyboard-pass scripts added.
@@ -1,109 +1,109 @@
1
- ---
2
- name: addressing-review-feedback
3
- description: Use when a code step is a rework with review comments to address: check each comment against the current code before acting, fix every valid one, explain with evidence any you do not apply, keep the reviewer's own commits, and report the outcome per comment.
4
- ---
5
-
6
- # Addressing review feedback
7
-
8
- A rework run gets the reviewer's comments and a branch that already has the earlier work, and maybe commits from the reviewer. Nobody will answer questions, and you cannot reply on GitHub. Your answer to the reviewer is the code plus the report.
9
-
10
- Review feedback is a set of technical claims to check, not orders to apply blindly and not opinions to argue with. Verify, then act.
11
-
12
- ## 1. Get the current state
13
-
14
- In each repository:
15
-
16
- ```
17
- git log --oneline origin/<base>..HEAD
18
- git diff --stat origin/<base>...HEAD
19
- git status
20
- ```
21
-
22
- - The current branch is the source of truth. Line numbers in the comments may refer to an older version; find the code by content, not by number.
23
- - Identify commits that are not from the earlier agent run (different author, or messages like "review fixes"). These are the reviewer's own changes. Keep them: do not revert, rewrite or "clean up" them unless a comment explicitly asks for that.
24
- - Never rewrite history: no rebase, amend, reset or force push. Add new commits on top.
25
-
26
- ## 2. List every comment
27
-
28
- Read all comments before changing anything. Items can depend on each other, and a later comment can change how you read an earlier one.
29
-
30
- Number them and restate each as a concrete technical requirement:
31
-
32
- ```
33
- C1 "this will blow up for guests" -> getCart() must handle a null user (src/cart.ts, getCart)
34
- C2 "use the existing money helper" -> replace manual rounding with formatMoney() in Invoice.vue
35
- C3 "add a test for the empty state" -> component test: no items shows the empty message
36
- ```
37
-
38
- A comment that holds several requests becomes several items.
39
-
40
- ## 3. Verify each item against the code
41
-
42
- For each item, before editing:
43
-
44
- - Is the claim true in the current code? Reproduce it if it is a bug (a test, a quick run). It may already be fixed by a reviewer commit.
45
- - Would the suggested change break something else? Search for other callers, run the related tests.
46
- - Is there a reason the code is the way it is (a comment, a test, the spec, a platform constraint)?
47
- - Does the suggestion fit this codebase and its conventions?
48
- - For "do it properly" or "add X" requests, check whether X is used: search the code. Building an unused feature is not a fix.
49
-
50
- If a comment is unclear, choose the reading that best matches the reviewer's evident intent, the spec and the code. Write the reading you chose in the report. Do not stop and do not skip it.
51
-
52
- ## 4. Decide and act
53
-
54
- Each item ends in one of these states:
55
-
56
- | State | When |
57
- |---|---|
58
- | Fixed | The comment is valid; you changed the code and checked it. |
59
- | Already done | The current code already does it (say where, for example a reviewer commit). |
60
- | Fixed differently | The concern is valid but the suggested change is wrong here; you solved the concern another way. |
61
- | Not changed | The comment is wrong for this code, conflicts with the spec, or would break something. Give the evidence. |
62
- | Partly done | You could do only part of it (needs credentials, out of scope of the task). Say what is left. |
63
-
64
- Default to fixing. Use "Not changed" only with concrete evidence (file:line, test output, a spec quote), never because the change is inconvenient. When the reviewer and the spec disagree, follow the reviewer if the change is safe and within the task, and note the conflict.
65
-
66
- Order of work:
67
- 1. Blocking items: broken behaviour, security, data loss.
68
- 2. Simple items: naming, imports, small corrections.
69
- 3. Larger items: logic changes, refactors.
70
-
71
- Work one item at a time and run the related tests after each. Use `systematic-debugging` for bug reports and `test-driven-development` when a comment asks for new behaviour or reports a bug that a test could catch.
72
-
73
- Stay within the comments and what they directly imply. A rework is not the time for unrelated improvements; list those under "Noticed, not changed".
74
-
75
- ## 5. Verify everything and commit
76
-
77
- Run `verification-before-completion` across the whole branch, not only the lines you touched: a fix for one comment can break another part. Commit with `clean-commits`; one commit per comment or one commit for the whole round both work, as long as messages say what was addressed.
78
-
79
- ## 6. Report per comment
80
-
81
- Plain technical statements. No thanks, no "great catch", no "you're absolutely right": the reader wants to know what changed.
82
-
83
- ```
84
- Review comments
85
- C1 Fixed: getCart() returns an empty cart for guests (src/cart.ts:31), test "guest gets empty cart" added, failed before.
86
- C2 Fixed: Invoice.vue uses formatMoney() for totals and line prices.
87
- C3 Fixed: test "shows the empty message when there are no items" in Invoice.test.ts.
88
- C4 Not changed: the comment suggests removing the legacy date parser, but importInvoices() still calls it for CSV files (src/import/csv.ts:88); removing it breaks 4 tests in csv.test.ts.
89
- C5 Already done: reviewer commit 3f2a1c0 renamed the prop.
90
- C6 Unclear, read as "show the VAT column only for business customers" (matches spec rule 2): done in InvoiceTable.vue.
91
-
92
- Checks
93
- - pnpm test -- --run: 220 passed, 0 failed
94
- - pnpm typecheck: exit 0
95
- ```
96
-
97
- ## Common mistakes
98
-
99
- | Mistake | Instead |
100
- |---|---|
101
- | Applying every suggestion without checking it | Verify against the code first; reviewers can be wrong or work from an old version. |
102
- | Skipping a comment silently | Every comment appears in the report with a state. |
103
- | Arguing without evidence | Show the file:line, the test or the spec line. |
104
- | Reverting or rewriting the reviewer's commits | Keep them; build on top. |
105
- | Fixing items in a batch and testing once at the end | One item, then its tests. |
106
- | Fixing only the commented lines | Look for the same problem elsewhere in your change and fix it there too. |
107
- | Asking questions in the report and waiting | Decide, state the reading you chose, and finish. |
108
-
109
- Sources: obra/superpowers (MIT, commit bb92a77) receiving-code-review. Changed: rewritten for unattended reworks, every question path removed.
1
+ ---
2
+ name: addressing-review-feedback
3
+ description: Use when a code step is a rework with review comments to address: check each comment against the current code before acting, fix every valid one, explain with evidence any you do not apply, keep the reviewer's own commits, and report the outcome per comment.
4
+ ---
5
+
6
+ # Addressing review feedback
7
+
8
+ A rework run gets the reviewer's comments and a branch that already has the earlier work, and maybe commits from the reviewer. Nobody will answer questions, and you cannot reply on GitHub. Your answer to the reviewer is the code plus the report.
9
+
10
+ Review feedback is a set of technical claims to check, not orders to apply blindly and not opinions to argue with. Verify, then act.
11
+
12
+ ## 1. Get the current state
13
+
14
+ In each repository:
15
+
16
+ ```
17
+ git log --oneline origin/<base>..HEAD
18
+ git diff --stat origin/<base>...HEAD
19
+ git status
20
+ ```
21
+
22
+ - The current branch is the source of truth. Line numbers in the comments may refer to an older version; find the code by content, not by number.
23
+ - Identify commits that are not from the earlier agent run (different author, or messages like "review fixes"). These are the reviewer's own changes. Keep them: do not revert, rewrite or "clean up" them unless a comment explicitly asks for that.
24
+ - Never rewrite history: no rebase, amend, reset or force push. Add new commits on top.
25
+
26
+ ## 2. List every comment
27
+
28
+ Read all comments before changing anything. Items can depend on each other, and a later comment can change how you read an earlier one.
29
+
30
+ Number them and restate each as a concrete technical requirement:
31
+
32
+ ```
33
+ C1 "this will blow up for guests" -> getCart() must handle a null user (src/cart.ts, getCart)
34
+ C2 "use the existing money helper" -> replace manual rounding with formatMoney() in Invoice.vue
35
+ C3 "add a test for the empty state" -> component test: no items shows the empty message
36
+ ```
37
+
38
+ A comment that holds several requests becomes several items.
39
+
40
+ ## 3. Verify each item against the code
41
+
42
+ For each item, before editing:
43
+
44
+ - Is the claim true in the current code? Reproduce it if it is a bug (a test, a quick run). It may already be fixed by a reviewer commit.
45
+ - Would the suggested change break something else? Search for other callers, run the related tests.
46
+ - Is there a reason the code is the way it is (a comment, a test, the spec, a platform constraint)?
47
+ - Does the suggestion fit this codebase and its conventions?
48
+ - For "do it properly" or "add X" requests, check whether X is used: search the code. Building an unused feature is not a fix.
49
+
50
+ If a comment is unclear, choose the reading that best matches the reviewer's evident intent, the spec and the code. Write the reading you chose in the report. Do not stop and do not skip it.
51
+
52
+ ## 4. Decide and act
53
+
54
+ Each item ends in one of these states:
55
+
56
+ | State | When |
57
+ |---|---|
58
+ | Fixed | The comment is valid; you changed the code and checked it. |
59
+ | Already done | The current code already does it (say where, for example a reviewer commit). |
60
+ | Fixed differently | The concern is valid but the suggested change is wrong here; you solved the concern another way. |
61
+ | Not changed | The comment is wrong for this code, conflicts with the spec, or would break something. Give the evidence. |
62
+ | Partly done | You could do only part of it (needs credentials, out of scope of the task). Say what is left. |
63
+
64
+ Default to fixing. Use "Not changed" only with concrete evidence (file:line, test output, a spec quote), never because the change is inconvenient. When the reviewer and the spec disagree, follow the reviewer if the change is safe and within the task, and note the conflict.
65
+
66
+ Order of work:
67
+ 1. Blocking items: broken behaviour, security, data loss.
68
+ 2. Simple items: naming, imports, small corrections.
69
+ 3. Larger items: logic changes, refactors.
70
+
71
+ Work one item at a time and run the related tests after each. Use `systematic-debugging` for bug reports and `test-driven-development` when a comment asks for new behaviour or reports a bug that a test could catch.
72
+
73
+ Stay within the comments and what they directly imply. A rework is not the time for unrelated improvements; list those under "Noticed, not changed".
74
+
75
+ ## 5. Verify everything and commit
76
+
77
+ Run `verification-before-completion` across the whole branch, not only the lines you touched: a fix for one comment can break another part. Commit with `clean-commits`; one commit per comment or one commit for the whole round both work, as long as messages say what was addressed.
78
+
79
+ ## 6. Report per comment
80
+
81
+ Plain technical statements. No thanks, no "great catch", no "you're absolutely right": the reader wants to know what changed.
82
+
83
+ ```
84
+ Review comments
85
+ C1 Fixed: getCart() returns an empty cart for guests (src/cart.ts:31), test "guest gets empty cart" added, failed before.
86
+ C2 Fixed: Invoice.vue uses formatMoney() for totals and line prices.
87
+ C3 Fixed: test "shows the empty message when there are no items" in Invoice.test.ts.
88
+ C4 Not changed: the comment suggests removing the legacy date parser, but importInvoices() still calls it for CSV files (src/import/csv.ts:88); removing it breaks 4 tests in csv.test.ts.
89
+ C5 Already done: reviewer commit 3f2a1c0 renamed the prop.
90
+ C6 Unclear, read as "show the VAT column only for business customers" (matches spec rule 2): done in InvoiceTable.vue.
91
+
92
+ Checks
93
+ - pnpm test -- --run: 220 passed, 0 failed
94
+ - pnpm typecheck: exit 0
95
+ ```
96
+
97
+ ## Common mistakes
98
+
99
+ | Mistake | Instead |
100
+ |---|---|
101
+ | Applying every suggestion without checking it | Verify against the code first; reviewers can be wrong or work from an old version. |
102
+ | Skipping a comment silently | Every comment appears in the report with a state. |
103
+ | Arguing without evidence | Show the file:line, the test or the spec line. |
104
+ | Reverting or rewriting the reviewer's commits | Keep them; build on top. |
105
+ | Fixing items in a batch and testing once at the end | One item, then its tests. |
106
+ | Fixing only the commented lines | Look for the same problem elsewhere in your change and fix it there too. |
107
+ | Asking questions in the report and waiting | Decide, state the reading you chose, and finish. |
108
+
109
+ Sources: obra/superpowers (MIT, commit bb92a77) receiving-code-review. Changed: rewritten for unattended reworks, every question path removed.