codebyplan 1.13.49 → 1.13.50
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/dist/cli.js +2 -1
- package/package.json +1 -1
- package/templates/agents/cbp-round-executor.md +1 -6
- package/templates/agents/cbp-task-planner.md +2 -2
- package/templates/hooks/cbp-skill-context-guard.sh +52 -0
- package/templates/hooks/cbp-test-hooks.sh +144 -0
- package/templates/hooks/hooks.json +9 -0
- package/templates/rules/model-invocation-convention.md +40 -0
- package/templates/rules/parallel-waves.md +1 -1
- package/templates/rules/task-routing-recommendation.md +1 -1
- package/templates/settings.project.base.json +2 -1
- package/templates/skills/cbp-build-cc-settings/reference/cbp-permission-policy.md +42 -0
- package/templates/skills/cbp-clear-continue/SKILL.md +86 -0
- package/templates/skills/cbp-clear-prep/SKILL.md +121 -0
- package/templates/skills/cbp-round-start/SKILL.md +1 -1
- package/templates/skills/cbp-task-check/SKILL.md +12 -5
- package/templates/skills/cbp-task-complete/SKILL.md +9 -11
- package/templates/skills/cbp-task-complete/reference/checkpoint-done-branching.md +14 -21
- package/templates/skills/cbp-task-complete/reference/next-step-heuristic.md +4 -6
- package/templates/skills/cbp-task-testing/SKILL.md +9 -14
- package/templates/skills/cbp-frontend-a11y/SKILL.md +0 -108
- package/templates/skills/cbp-frontend-a11y/reference/aria-roles-states.md +0 -130
- package/templates/skills/cbp-frontend-a11y/reference/contrast-visual.md +0 -122
- package/templates/skills/cbp-frontend-a11y/reference/keyboard-patterns.md +0 -154
- package/templates/skills/cbp-frontend-a11y/reference/semantic-html.md +0 -111
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
# Keyboard Interaction Patterns Reference
|
|
2
|
-
|
|
3
|
-
Every interactive component must be fully operable with a keyboard alone. Mouse-only patterns are WCAG 2.1 AA failures (Success Criterion 2.1.1).
|
|
4
|
-
|
|
5
|
-
## Focus Management
|
|
6
|
-
|
|
7
|
-
### On Mount (Dialog / Modal / Drawer)
|
|
8
|
-
|
|
9
|
-
When a dialog, modal, or drawer opens:
|
|
10
|
-
|
|
11
|
-
1. Move focus to the FIRST interactive element inside (or to the dialog container if no interactive element exists — ensure `tabindex="0"` on the container in that case)
|
|
12
|
-
2. For modals with a clear primary action: move focus to the primary action button
|
|
13
|
-
3. For forms: move focus to the first form field
|
|
14
|
-
|
|
15
|
-
```jsx
|
|
16
|
-
// Using useEffect + ref
|
|
17
|
-
const modalRef = useRef<HTMLDivElement>(null);
|
|
18
|
-
|
|
19
|
-
useEffect(() => {
|
|
20
|
-
if (isOpen) {
|
|
21
|
-
// Focus the first focusable element inside
|
|
22
|
-
const firstFocusable = modalRef.current?.querySelector<HTMLElement>(
|
|
23
|
-
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
|
|
24
|
-
);
|
|
25
|
-
firstFocusable?.focus();
|
|
26
|
-
}
|
|
27
|
-
}, [isOpen]);
|
|
28
|
-
```
|
|
29
|
-
|
|
30
|
-
### On Close (Dialog / Modal / Drawer)
|
|
31
|
-
|
|
32
|
-
When a dialog, modal, or drawer closes:
|
|
33
|
-
|
|
34
|
-
1. Return focus to the element that TRIGGERED the opening (save a ref to it before opening)
|
|
35
|
-
2. If the trigger no longer exists (deleted row), focus the nearest logical element
|
|
36
|
-
|
|
37
|
-
```jsx
|
|
38
|
-
const triggerRef = useRef<HTMLButtonElement>(null);
|
|
39
|
-
|
|
40
|
-
const handleClose = () => {
|
|
41
|
-
setIsOpen(false);
|
|
42
|
-
// Return focus to trigger
|
|
43
|
-
triggerRef.current?.focus();
|
|
44
|
-
};
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
## Tab Order
|
|
48
|
-
|
|
49
|
-
- Tab order must follow the logical reading/interaction order — typically top-left to bottom-right
|
|
50
|
-
- Never use `tabindex > 0` — it creates a separate tab order before natural DOM order, confusing all keyboard users
|
|
51
|
-
- `tabindex="0"` adds a non-interactive element to natural tab order (use sparingly — prefer interactive elements)
|
|
52
|
-
- `tabindex="-1"` removes from natural tab order but allows programmatic `.focus()` (correct for modal containers)
|
|
53
|
-
|
|
54
|
-
## Esc Key
|
|
55
|
-
|
|
56
|
-
Any overlay, popover, dropdown, or modal MUST close on `Escape`:
|
|
57
|
-
|
|
58
|
-
```jsx
|
|
59
|
-
useEffect(() => {
|
|
60
|
-
const handleKeyDown = (e: KeyboardEvent) => {
|
|
61
|
-
if (e.key === 'Escape') {
|
|
62
|
-
onClose();
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
if (isOpen) {
|
|
66
|
-
document.addEventListener('keydown', handleKeyDown);
|
|
67
|
-
return () => document.removeEventListener('keydown', handleKeyDown);
|
|
68
|
-
}
|
|
69
|
-
}, [isOpen, onClose]);
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
## Arrow Key Navigation
|
|
73
|
-
|
|
74
|
-
For composite widgets (menu, listbox, radio group, tabs, grid), arrow keys navigate BETWEEN items — Tab moves focus OUT of the widget entirely.
|
|
75
|
-
|
|
76
|
-
| Widget | Keys |
|
|
77
|
-
|--------|------|
|
|
78
|
-
| Menu / dropdown | `↑`/`↓` between items, `Home`/`End` to first/last |
|
|
79
|
-
| Tabs | `←`/`→` between tabs (horizontal) or `↑`/`↓` (vertical) |
|
|
80
|
-
| Listbox | `↑`/`↓` between options, `Home`/`End` |
|
|
81
|
-
| Grid | `↑`/`↓`/`←`/`→` between cells |
|
|
82
|
-
| Radio group | `↑`/`↓` or `←`/`→` between radios; selection follows focus |
|
|
83
|
-
|
|
84
|
-
## Focus Traps (Modal)
|
|
85
|
-
|
|
86
|
-
While a modal is open, Tab and Shift+Tab must cycle WITHIN the modal — not escape to the page behind:
|
|
87
|
-
|
|
88
|
-
```jsx
|
|
89
|
-
const FOCUSABLE_SELECTORS =
|
|
90
|
-
'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
91
|
-
|
|
92
|
-
const handleTabKey = (e: KeyboardEvent) => {
|
|
93
|
-
const focusableEls = Array.from(
|
|
94
|
-
modalRef.current?.querySelectorAll<HTMLElement>(FOCUSABLE_SELECTORS) ?? []
|
|
95
|
-
);
|
|
96
|
-
const first = focusableEls[0];
|
|
97
|
-
const last = focusableEls[focusableEls.length - 1];
|
|
98
|
-
|
|
99
|
-
if (e.shiftKey && document.activeElement === first) {
|
|
100
|
-
e.preventDefault();
|
|
101
|
-
last.focus();
|
|
102
|
-
} else if (!e.shiftKey && document.activeElement === last) {
|
|
103
|
-
e.preventDefault();
|
|
104
|
-
first.focus();
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
Library alternative: `focus-trap-react` handles this pattern with accessibility compliance. If it is already in `package.json`, use it.
|
|
110
|
-
|
|
111
|
-
## Type-Ahead (Listbox / Menu)
|
|
112
|
-
|
|
113
|
-
When a user types a character while focus is inside a listbox or menu, focus jumps to the first item whose label starts with that character. Implement only when `role="listbox"` or `role="menu"` is used with custom keyboard handling — native `<select>` has this built in.
|
|
114
|
-
|
|
115
|
-
## Enter and Space Activation
|
|
116
|
-
|
|
117
|
-
| Element | Enter | Space |
|
|
118
|
-
|---------|-------|-------|
|
|
119
|
-
| `<button>` | Activates | Activates |
|
|
120
|
-
| `<a href>` | Follows link | Scrolls page (native browser) |
|
|
121
|
-
| `<input type="checkbox">` | n/a | Toggles |
|
|
122
|
-
| `role="button"` | Must activate | Must activate |
|
|
123
|
-
| `role="menuitem"` | Activates | Activates |
|
|
124
|
-
|
|
125
|
-
For custom `role="button"` on a non-button element:
|
|
126
|
-
```jsx
|
|
127
|
-
<div
|
|
128
|
-
role="button"
|
|
129
|
-
tabIndex={0}
|
|
130
|
-
onKeyDown={(e) => {
|
|
131
|
-
if (e.key === 'Enter' || e.key === ' ') {
|
|
132
|
-
e.preventDefault();
|
|
133
|
-
handleActivate();
|
|
134
|
-
}
|
|
135
|
-
}}
|
|
136
|
-
onClick={handleActivate}
|
|
137
|
-
>
|
|
138
|
-
Custom button
|
|
139
|
-
</div>
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Prefer `<button>` — it handles Enter/Space natively and avoids this boilerplate.
|
|
143
|
-
|
|
144
|
-
## Skip Links
|
|
145
|
-
|
|
146
|
-
For pages with repeated navigation (header nav present on every page), provide a skip-to-main-content link as the first focusable element:
|
|
147
|
-
|
|
148
|
-
```jsx
|
|
149
|
-
<a href="#main-content" className={styles.skipLink}>
|
|
150
|
-
Skip to main content
|
|
151
|
-
</a>
|
|
152
|
-
```
|
|
153
|
-
|
|
154
|
-
Visible on focus (via CSS), hidden otherwise. The `<main id="main-content">` is the target.
|
|
@@ -1,111 +0,0 @@
|
|
|
1
|
-
# Semantic HTML Reference
|
|
2
|
-
|
|
3
|
-
Use native HTML semantics first. ARIA is a gap-filler for when no native element meets the need — not a replacement.
|
|
4
|
-
|
|
5
|
-
## Landmark Roles
|
|
6
|
-
|
|
7
|
-
| Element | Role | Notes |
|
|
8
|
-
|---------|------|-------|
|
|
9
|
-
| `<main>` | `main` | Exactly ONE per page |
|
|
10
|
-
| `<nav>` | `navigation` | Multiple allowed; each needs an `aria-label` |
|
|
11
|
-
| `<header>` | `banner` (at page scope) | In a sectioning element it becomes generic |
|
|
12
|
-
| `<footer>` | `contentinfo` (at page scope) | |
|
|
13
|
-
| `<aside>` | `complementary` | |
|
|
14
|
-
| `<section>` | `region` (only when it has an `aria-labelledby`) | Without label, it's generic |
|
|
15
|
-
|
|
16
|
-
Anti-pattern: `<div role="main">` — use `<main>` instead.
|
|
17
|
-
|
|
18
|
-
## Heading Hierarchy
|
|
19
|
-
|
|
20
|
-
- One `<h1>` per page — the page title or primary content heading
|
|
21
|
-
- Never skip levels: `h1 → h2 → h3`, not `h1 → h3`
|
|
22
|
-
- Headings convey structure, not visual size — use CSS for size; use the correct level for hierarchy
|
|
23
|
-
- Empty headings (`<h2></h2>`) violate WCAG 2.4.6
|
|
24
|
-
|
|
25
|
-
## `<button>` vs `<a>`
|
|
26
|
-
|
|
27
|
-
| Use | Element |
|
|
28
|
-
|-----|---------|
|
|
29
|
-
| Triggers an action (submit, open modal, toggle) | `<button>` |
|
|
30
|
-
| Navigates to a URL | `<a href="...">` |
|
|
31
|
-
| Downloads a file | `<a href="..." download>` |
|
|
32
|
-
|
|
33
|
-
Anti-patterns:
|
|
34
|
-
- `<div onClick={doAction}>` — not keyboard accessible; use `<button>`
|
|
35
|
-
- `<button onClick={() => router.push('/path')}>` — use `<a href="/path">`; or `<Link href="/path">` in Next.js
|
|
36
|
-
- `<a href="#">` with an onClick — use `<button>` if no real URL
|
|
37
|
-
|
|
38
|
-
## Form Elements
|
|
39
|
-
|
|
40
|
-
```html
|
|
41
|
-
<!-- Correct: explicit association -->
|
|
42
|
-
<label htmlFor="email-input">Email</label>
|
|
43
|
-
<input id="email-input" type="email" name="email" />
|
|
44
|
-
|
|
45
|
-
<!-- Correct: implicit wrapping -->
|
|
46
|
-
<label>
|
|
47
|
-
Email
|
|
48
|
-
<input type="email" name="email" />
|
|
49
|
-
</label>
|
|
50
|
-
|
|
51
|
-
<!-- Anti-pattern: no association -->
|
|
52
|
-
<span>Email</span>
|
|
53
|
-
<input type="email" name="email" />
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
For fieldsets with radio/checkbox groups:
|
|
57
|
-
```html
|
|
58
|
-
<fieldset>
|
|
59
|
-
<legend>Preferred contact method</legend>
|
|
60
|
-
<label><input type="radio" name="contact" value="email" /> Email</label>
|
|
61
|
-
<label><input type="radio" name="contact" value="phone" /> Phone</label>
|
|
62
|
-
</fieldset>
|
|
63
|
-
```
|
|
64
|
-
|
|
65
|
-
## Lists
|
|
66
|
-
|
|
67
|
-
Use `<ul>/<ol>/<li>` for lists of items — screen readers announce item count and position.
|
|
68
|
-
|
|
69
|
-
Anti-pattern: `<div class="list"><div class="item">...</div></div>` — no count/position announced.
|
|
70
|
-
|
|
71
|
-
Exception: `list-style: none` on `<ul>` removes list semantics in Safari/VoiceOver. Add `role="list"` when list-style is suppressed:
|
|
72
|
-
```jsx
|
|
73
|
-
<ul role="list" style={{ listStyle: 'none' }}>
|
|
74
|
-
```
|
|
75
|
-
|
|
76
|
-
## Tables
|
|
77
|
-
|
|
78
|
-
For data tables (not layout):
|
|
79
|
-
```html
|
|
80
|
-
<table>
|
|
81
|
-
<caption>Monthly sales by region</caption>
|
|
82
|
-
<thead>
|
|
83
|
-
<tr>
|
|
84
|
-
<th scope="col">Region</th>
|
|
85
|
-
<th scope="col">Sales</th>
|
|
86
|
-
</tr>
|
|
87
|
-
</thead>
|
|
88
|
-
<tbody>
|
|
89
|
-
<tr>
|
|
90
|
-
<th scope="row">North</th>
|
|
91
|
-
<td>$12,000</td>
|
|
92
|
-
</tr>
|
|
93
|
-
</tbody>
|
|
94
|
-
</table>
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
`<caption>` is the table's accessible name. `scope="col"/"row"` associates headers with cells.
|
|
98
|
-
|
|
99
|
-
Never use `<table>` for visual layout — use CSS Grid or Flexbox.
|
|
100
|
-
|
|
101
|
-
## Anti-Pattern Quick-Reference
|
|
102
|
-
|
|
103
|
-
| Anti-pattern | Fix |
|
|
104
|
-
|-------------|-----|
|
|
105
|
-
| `<div onClick>` | `<button>` or `<a>` |
|
|
106
|
-
| `<span onClick>` | `<button>` |
|
|
107
|
-
| `<img>` missing `alt` | Add `alt="description"` or `alt=""` for decorative |
|
|
108
|
-
| `<input>` missing label | Add `<label htmlFor>` or `aria-label` |
|
|
109
|
-
| `<h1>` used for styling | Use CSS; pick correct heading level |
|
|
110
|
-
| `<table>` for layout | Use CSS Grid/Flexbox |
|
|
111
|
-
| Empty `<button>` (icon only) | Add `aria-label="Close"` |
|