feat: add Conductor plugin for Context-Driven Development

Add comprehensive Conductor plugin implementing Context-Driven Development
methodology with tracks, specs, and phased implementation plans.

Components:
- 5 commands: setup, new-track, implement, status, revert
- 1 agent: conductor-validator
- 3 skills: context-driven-development, track-management, workflow-patterns
- 18 templates for project artifacts

Documentation updates:
- README.md: Updated counts (68 plugins, 100 agents, 110 skills, 76 tools)
- docs/plugins.md: Added Conductor to Workflows section
- docs/agents.md: Added conductor-validator agent
- docs/agent-skills.md: Added Conductor skills section

Also includes Prettier formatting across all project files.
This commit is contained in:
Seth Hobson
2026-01-15 17:38:21 -05:00
parent 87231b828d
commit f662524f9a
94 changed files with 11610 additions and 1728 deletions

View File

@@ -20,13 +20,13 @@ Practical guide to testing web applications with screen readers for comprehensiv
### 1. Major Screen Readers
| Screen Reader | Platform | Browser | Usage |
|---------------|----------|---------|-------|
| **VoiceOver** | macOS/iOS | Safari | ~15% |
| **NVDA** | Windows | Firefox/Chrome | ~31% |
| **JAWS** | Windows | Chrome/IE | ~40% |
| **TalkBack** | Android | Chrome | ~10% |
| **Narrator** | Windows | Edge | ~4% |
| Screen Reader | Platform | Browser | Usage |
| ------------- | --------- | -------------- | ----- |
| **VoiceOver** | macOS/iOS | Safari | ~15% |
| **NVDA** | Windows | Firefox/Chrome | ~31% |
| **JAWS** | Windows | Chrome/IE | ~40% |
| **TalkBack** | Android | Chrome | ~10% |
| **Narrator** | Windows | Edge | ~4% |
### 2. Testing Priority
@@ -44,11 +44,11 @@ Comprehensive Coverage:
### 3. Screen Reader Modes
| Mode | Purpose | When Used |
|------|---------|-----------|
| **Browse/Virtual** | Read content | Default reading |
| **Focus/Forms** | Interact with controls | Filling forms |
| **Application** | Custom widgets | ARIA applications |
| Mode | Purpose | When Used |
| ------------------ | ---------------------- | ----------------- |
| **Browse/Virtual** | Read content | Default reading |
| **Focus/Forms** | Interact with controls | Filling forms |
| **Application** | Custom widgets | ARIA applications |
## VoiceOver (macOS)
@@ -101,22 +101,26 @@ VO + Cmd + T Next table
## VoiceOver Testing Checklist
### Page Load
- [ ] Page title announced
- [ ] Main landmark found
- [ ] Skip link works
### Navigation
- [ ] All headings discoverable via rotor
- [ ] Heading levels logical (H1 → H2 → H3)
- [ ] Landmarks properly labeled
- [ ] Skip links functional
### Links & Buttons
- [ ] Link purpose clear
- [ ] Button actions described
- [ ] New window/tab announced
### Forms
- [ ] All labels read with inputs
- [ ] Required fields announced
- [ ] Error messages read
@@ -124,12 +128,14 @@ VO + Cmd + T Next table
- [ ] Focus moves to errors
### Dynamic Content
- [ ] Alerts announced immediately
- [ ] Loading states communicated
- [ ] Content updates announced
- [ ] Modals trap focus correctly
### Tables
- [ ] Headers associated with cells
- [ ] Table navigation works
- [ ] Complex tables have captions
@@ -151,11 +157,11 @@ VO + Cmd + T Next table
<div id="results" role="status" aria-live="polite">New results loaded</div>
<!-- Issue: Form error not read -->
<input type="email">
<input type="email" />
<span class="error">Invalid email</span>
<!-- Fix -->
<input type="email" aria-invalid="true" aria-describedby="email-error">
<input type="email" aria-invalid="true" aria-describedby="email-error" />
<span id="email-error" role="alert">Invalid email</span>
```
@@ -235,23 +241,27 @@ Watch for:
## NVDA Test Script
### Initial Load
1. Navigate to page
2. Let page finish loading
3. Press Insert + Down to read all
4. Note: Page title, main content identified?
### Landmark Navigation
1. Press D repeatedly
2. Check: All main areas reachable?
3. Check: Landmarks properly labeled?
### Heading Navigation
1. Press Insert + F7 → Headings
2. Check: Logical heading structure?
3. Press H to navigate headings
4. Check: All sections discoverable?
### Form Testing
1. Press F to find first form field
2. Check: Label read?
3. Fill in invalid data
@@ -260,12 +270,14 @@ Watch for:
6. Check: Focus moved to error?
### Interactive Elements
1. Tab through all interactive elements
2. Check: Each announces role and state
3. Activate buttons with Enter/Space
4. Check: Result announced?
### Dynamic Content
1. Trigger content update
2. Check: Change announced?
3. Open modal
@@ -345,10 +357,12 @@ Reading Controls (swipe up then right):
```html
<!-- Accessible modal structure -->
<div role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
aria-describedby="dialog-desc">
<div
role="dialog"
aria-modal="true"
aria-labelledby="dialog-title"
aria-describedby="dialog-desc"
>
<h2 id="dialog-title">Confirm Delete</h2>
<p id="dialog-desc">This action cannot be undone.</p>
<button>Cancel</button>
@@ -363,10 +377,10 @@ function openModal(modal) {
lastFocus = document.activeElement;
// Move focus to modal
modal.querySelector('h2').focus();
modal.querySelector("h2").focus();
// Trap focus
modal.addEventListener('keydown', trapFocus);
modal.addEventListener("keydown", trapFocus);
}
function closeModal(modal) {
@@ -375,9 +389,9 @@ function closeModal(modal) {
}
function trapFocus(e) {
if (e.key === 'Tab') {
if (e.key === "Tab") {
const focusable = modal.querySelectorAll(
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])'
'button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])',
);
const first = focusable[0];
const last = focusable[focusable.length - 1];
@@ -391,7 +405,7 @@ function trapFocus(e) {
}
}
if (e.key === 'Escape') {
if (e.key === "Escape") {
closeModal(modal);
}
}
@@ -411,12 +425,13 @@ function trapFocus(e) {
</div>
<!-- Progress updates -->
<div role="progressbar"
aria-valuenow="75"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Upload progress">
</div>
<div
role="progressbar"
aria-valuenow="75"
aria-valuemin="0"
aria-valuemax="100"
aria-label="Upload progress"
></div>
<!-- Log (additions only) -->
<div role="log" aria-live="polite" aria-relevant="additions">
@@ -428,53 +443,47 @@ function trapFocus(e) {
```html
<div role="tablist" aria-label="Product information">
<button role="tab"
id="tab-1"
aria-selected="true"
aria-controls="panel-1">
<button role="tab" id="tab-1" aria-selected="true" aria-controls="panel-1">
Description
</button>
<button role="tab"
id="tab-2"
aria-selected="false"
aria-controls="panel-2"
tabindex="-1">
<button
role="tab"
id="tab-2"
aria-selected="false"
aria-controls="panel-2"
tabindex="-1"
>
Reviews
</button>
</div>
<div role="tabpanel"
id="panel-1"
aria-labelledby="tab-1">
<div role="tabpanel" id="panel-1" aria-labelledby="tab-1">
Product description content...
</div>
<div role="tabpanel"
id="panel-2"
aria-labelledby="tab-2"
hidden>
<div role="tabpanel" id="panel-2" aria-labelledby="tab-2" hidden>
Reviews content...
</div>
```
```javascript
// Tab keyboard navigation
tablist.addEventListener('keydown', (e) => {
tablist.addEventListener("keydown", (e) => {
const tabs = [...tablist.querySelectorAll('[role="tab"]')];
const index = tabs.indexOf(document.activeElement);
let newIndex;
switch (e.key) {
case 'ArrowRight':
case "ArrowRight":
newIndex = (index + 1) % tabs.length;
break;
case 'ArrowLeft':
case "ArrowLeft":
newIndex = (index - 1 + tabs.length) % tabs.length;
break;
case 'Home':
case "Home":
newIndex = 0;
break;
case 'End':
case "End":
newIndex = tabs.length - 1;
break;
default:
@@ -494,17 +503,18 @@ tablist.addEventListener('keydown', (e) => {
function logAccessibleName(element) {
const computed = window.getComputedStyle(element);
console.log({
role: element.getAttribute('role') || element.tagName,
name: element.getAttribute('aria-label') ||
element.getAttribute('aria-labelledby') ||
element.textContent,
role: element.getAttribute("role") || element.tagName,
name:
element.getAttribute("aria-label") ||
element.getAttribute("aria-labelledby") ||
element.textContent,
state: {
expanded: element.getAttribute('aria-expanded'),
selected: element.getAttribute('aria-selected'),
checked: element.getAttribute('aria-checked'),
disabled: element.disabled
expanded: element.getAttribute("aria-expanded"),
selected: element.getAttribute("aria-selected"),
checked: element.getAttribute("aria-checked"),
disabled: element.disabled,
},
visible: computed.display !== 'none' && computed.visibility !== 'hidden'
visible: computed.display !== "none" && computed.visibility !== "hidden",
});
}
```
@@ -512,6 +522,7 @@ function logAccessibleName(element) {
## Best Practices
### Do's
- **Test with actual screen readers** - Not just simulators
- **Use semantic HTML first** - ARIA is supplemental
- **Test in browse and focus modes** - Different experiences
@@ -519,6 +530,7 @@ function logAccessibleName(element) {
- **Test keyboard only first** - Foundation for SR testing
### Don'ts
- **Don't assume one SR is enough** - Test multiple
- **Don't ignore mobile** - Growing user base
- **Don't test only happy path** - Test error states

View File

@@ -20,10 +20,10 @@ Comprehensive guide to auditing web content against WCAG 2.2 guidelines with act
### 1. WCAG Conformance Levels
| Level | Description | Required For |
|-------|-------------|--------------|
| **A** | Minimum accessibility | Legal baseline |
| **AA** | Standard conformance | Most regulations |
| Level | Description | Required For |
| ------- | ---------------------- | ----------------- |
| **A** | Minimum accessibility | Legal baseline |
| **AA** | Standard conformance | Most regulations |
| **AAA** | Enhanced accessibility | Specialized needs |
### 2. POUR Principles
@@ -61,10 +61,11 @@ Moderate:
### Perceivable (Principle 1)
```markdown
````markdown
## 1.1 Text Alternatives
### 1.1.1 Non-text Content (Level A)
- [ ] All images have alt text
- [ ] Decorative images have alt=""
- [ ] Complex images have long descriptions
@@ -72,33 +73,39 @@ Moderate:
- [ ] CAPTCHAs have alternatives
Check:
```html
<!-- Good -->
<img src="chart.png" alt="Sales increased 25% from Q1 to Q2">
<img src="decorative-line.png" alt="">
<img src="chart.png" alt="Sales increased 25% from Q1 to Q2" />
<img src="decorative-line.png" alt="" />
<!-- Bad -->
<img src="chart.png">
<img src="decorative-line.png" alt="decorative line">
<img src="chart.png" />
<img src="decorative-line.png" alt="decorative line" />
```
````
## 1.2 Time-based Media
### 1.2.1 Audio-only and Video-only (Level A)
- [ ] Audio has text transcript
- [ ] Video has audio description or transcript
### 1.2.2 Captions (Level A)
- [ ] All video has synchronized captions
- [ ] Captions are accurate and complete
- [ ] Speaker identification included
### 1.2.3 Audio Description (Level A)
- [ ] Video has audio description for visual content
## 1.3 Adaptable
### 1.3.1 Info and Relationships (Level A)
- [ ] Headings use proper tags (h1-h6)
- [ ] Lists use ul/ol/dl
- [ ] Tables have headers
@@ -106,38 +113,46 @@ Check:
- [ ] ARIA landmarks present
Check:
```html
<!-- Heading hierarchy -->
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
<h2>Another Section</h2>
<h2>Section</h2>
<h3>Subsection</h3>
<h2>Another Section</h2>
<!-- Table headers -->
<table>
<thead>
<tr><th scope="col">Name</th><th scope="col">Price</th></tr>
<tr>
<th scope="col">Name</th>
<th scope="col">Price</th>
</tr>
</thead>
</table>
```
### 1.3.2 Meaningful Sequence (Level A)
- [ ] Reading order is logical
- [ ] CSS positioning doesn't break order
- [ ] Focus order matches visual order
### 1.3.3 Sensory Characteristics (Level A)
- [ ] Instructions don't rely on shape/color alone
- [ ] "Click the red button" → "Click Submit (red button)"
## 1.4 Distinguishable
### 1.4.1 Use of Color (Level A)
- [ ] Color is not only means of conveying info
- [ ] Links distinguishable without color
- [ ] Error states not color-only
### 1.4.3 Contrast (Minimum) (Level AA)
- [ ] Text: 4.5:1 contrast ratio
- [ ] Large text (18pt+): 3:1 ratio
- [ ] UI components: 3:1 ratio
@@ -145,27 +160,32 @@ Check:
Tools: WebAIM Contrast Checker, axe DevTools
### 1.4.4 Resize Text (Level AA)
- [ ] Text resizes to 200% without loss
- [ ] No horizontal scrolling at 320px
- [ ] Content reflows properly
### 1.4.10 Reflow (Level AA)
- [ ] Content reflows at 400% zoom
- [ ] No two-dimensional scrolling
- [ ] All content accessible at 320px width
### 1.4.11 Non-text Contrast (Level AA)
- [ ] UI components have 3:1 contrast
- [ ] Focus indicators visible
- [ ] Graphical objects distinguishable
### 1.4.12 Text Spacing (Level AA)
- [ ] No content loss with increased spacing
- [ ] Line height 1.5x font size
- [ ] Paragraph spacing 2x font size
- [ ] Letter spacing 0.12x font size
- [ ] Word spacing 0.16x font size
```
````
### Operable (Principle 2)
@@ -183,9 +203,10 @@ Check:
// Custom button must be keyboard accessible
<div role="button" tabindex="0"
onkeydown="if(event.key === 'Enter' || event.key === ' ') activate()">
```
````
### 2.1.2 No Keyboard Trap (Level A)
- [ ] Focus can move away from all components
- [ ] Modal dialogs trap focus correctly
- [ ] Focus returns after modal closes
@@ -193,11 +214,13 @@ Check:
## 2.2 Enough Time
### 2.2.1 Timing Adjustable (Level A)
- [ ] Session timeouts can be extended
- [ ] User warned before timeout
- [ ] Option to disable auto-refresh
### 2.2.2 Pause, Stop, Hide (Level A)
- [ ] Moving content can be paused
- [ ] Auto-updating content can be paused
- [ ] Animations respect prefers-reduced-motion
@@ -214,12 +237,14 @@ Check:
## 2.3 Seizures and Physical Reactions
### 2.3.1 Three Flashes (Level A)
- [ ] No content flashes more than 3 times/second
- [ ] Flashing area is small (<25% viewport)
## 2.4 Navigable
### 2.4.1 Bypass Blocks (Level A)
- [ ] Skip to main content link present
- [ ] Landmark regions defined
- [ ] Proper heading structure
@@ -230,14 +255,17 @@ Check:
```
### 2.4.2 Page Titled (Level A)
- [ ] Unique, descriptive page titles
- [ ] Title reflects page content
### 2.4.3 Focus Order (Level A)
- [ ] Focus order matches visual order
- [ ] tabindex used correctly
### 2.4.4 Link Purpose (In Context) (Level A)
- [ ] Links make sense out of context
- [ ] No "click here" or "read more" alone
@@ -250,10 +278,12 @@ Check:
```
### 2.4.6 Headings and Labels (Level AA)
- [ ] Headings describe content
- [ ] Labels describe purpose
### 2.4.7 Focus Visible (Level AA)
- [ ] Focus indicator visible on all elements
- [ ] Custom focus styles meet contrast
@@ -265,9 +295,11 @@ Check:
```
### 2.4.11 Focus Not Obscured (Level AA) - WCAG 2.2
- [ ] Focused element not fully hidden
- [ ] Sticky headers don't obscure focus
```
````
### Understandable (Principle 3)
@@ -280,10 +312,12 @@ Check:
```html
<html lang="en">
```
````
### 3.1.2 Language of Parts (Level AA)
- [ ] Language changes marked
```html
<p>The French word <span lang="fr">bonjour</span> means hello.</p>
```
@@ -291,47 +325,56 @@ Check:
## 3.2 Predictable
### 3.2.1 On Focus (Level A)
- [ ] No context change on focus alone
- [ ] No unexpected popups on focus
### 3.2.2 On Input (Level A)
- [ ] No automatic form submission
- [ ] User warned before context change
### 3.2.3 Consistent Navigation (Level AA)
- [ ] Navigation consistent across pages
- [ ] Repeated components same order
### 3.2.4 Consistent Identification (Level AA)
- [ ] Same functionality = same label
- [ ] Icons used consistently
## 3.3 Input Assistance
### 3.3.1 Error Identification (Level A)
- [ ] Errors clearly identified
- [ ] Error message describes problem
- [ ] Error linked to field
```html
<input aria-describedby="email-error" aria-invalid="true">
<input aria-describedby="email-error" aria-invalid="true" />
<span id="email-error" role="alert">Please enter valid email</span>
```
### 3.3.2 Labels or Instructions (Level A)
- [ ] All inputs have visible labels
- [ ] Required fields indicated
- [ ] Format hints provided
### 3.3.3 Error Suggestion (Level AA)
- [ ] Errors include correction suggestion
- [ ] Suggestions are specific
### 3.3.4 Error Prevention (Level AA)
- [ ] Legal/financial forms reversible
- [ ] Data checked before submission
- [ ] User can review before submit
```
````
### Robust (Principle 4)
@@ -356,23 +399,21 @@ Check:
aria-labelledby="label">
</div>
<span id="label">Accept terms</span>
```
````
### 4.1.3 Status Messages (Level AA)
- [ ] Status updates announced
- [ ] Live regions used correctly
```html
<div role="status" aria-live="polite">
3 items added to cart
</div>
<div role="status" aria-live="polite">3 items added to cart</div>
<div role="alert" aria-live="assertive">
Error: Form submission failed
</div>
```
<div role="alert" aria-live="assertive">Error: Form submission failed</div>
```
````
## Automated Testing
```javascript
@@ -405,7 +446,7 @@ test('should have no accessibility violations', async ({ page }) => {
expect(results.violations).toHaveLength(0);
});
```
````
```bash
# CLI tools
@@ -420,28 +461,32 @@ lighthouse https://example.com --only-categories=accessibility
```html
<!-- Before -->
<input type="email" placeholder="Email">
<input type="email" placeholder="Email" />
<!-- After: Option 1 - Visible label -->
<label for="email">Email address</label>
<input id="email" type="email">
<input id="email" type="email" />
<!-- After: Option 2 - aria-label -->
<input type="email" aria-label="Email address">
<input type="email" aria-label="Email address" />
<!-- After: Option 3 - aria-labelledby -->
<span id="email-label">Email</span>
<input type="email" aria-labelledby="email-label">
<input type="email" aria-labelledby="email-label" />
```
### Fix: Insufficient Color Contrast
```css
/* Before: 2.5:1 contrast */
.text { color: #767676; }
.text {
color: #767676;
}
/* After: 4.5:1 contrast */
.text { color: #595959; }
.text {
color: #595959;
}
/* Or add background */
.text {
@@ -456,25 +501,25 @@ lighthouse https://example.com --only-categories=accessibility
// Make custom element keyboard accessible
class AccessibleDropdown extends HTMLElement {
connectedCallback() {
this.setAttribute('tabindex', '0');
this.setAttribute('role', 'combobox');
this.setAttribute('aria-expanded', 'false');
this.setAttribute("tabindex", "0");
this.setAttribute("role", "combobox");
this.setAttribute("aria-expanded", "false");
this.addEventListener('keydown', (e) => {
this.addEventListener("keydown", (e) => {
switch (e.key) {
case 'Enter':
case ' ':
case "Enter":
case " ":
this.toggle();
e.preventDefault();
break;
case 'Escape':
case "Escape":
this.close();
break;
case 'ArrowDown':
case "ArrowDown":
this.focusNext();
e.preventDefault();
break;
case 'ArrowUp':
case "ArrowUp":
this.focusPrevious();
e.preventDefault();
break;
@@ -487,6 +532,7 @@ class AccessibleDropdown extends HTMLElement {
## Best Practices
### Do's
- **Start early** - Accessibility from design phase
- **Test with real users** - Disabled users provide best feedback
- **Automate what you can** - 30-50% issues detectable
@@ -494,6 +540,7 @@ class AccessibleDropdown extends HTMLElement {
- **Document patterns** - Build accessible component library
### Don'ts
- **Don't rely only on automated testing** - Manual testing required
- **Don't use ARIA as first solution** - Native HTML first
- **Don't hide focus outlines** - Keyboard users need them