deer-sense
npx skills add https://github.com/autumnsgrove/groveengine --skill deer-sense
Agent 安装分布
Skill 文档
Deer Sense ð¦
The deer moves through the forest with heightened awareness. It hears what others missâa twig snapping underfoot, a bird’s warning call. It notices what blocks the path. The deer guides the herd around danger, ensuring everyone can travel safely. In the digital forest, the deer senses barriers that stop some wanderers from finding their way.
When to Activate
- User asks to “check accessibility” or “a11y audit”
- User says “make this accessible” or “screen reader test”
- User calls
/deer-senseor mentions deer/accessibility - Building new UI components
- Reviewing existing interfaces
- After visual redesigns
- Before major releases
- When accessibility complaints arise
Pair with: chameleon-adapt for accessible UI implementation
The Sense
LISTEN â SCAN â TEST â GUIDE â PROTECT
â â² â² â â
Hear Look for Validate Teach Ensure
Needs Barriers Paths Good Inclusion
Phase 1: LISTEN
The deer’s ears twitch, hearing what others miss…
Understand accessibility needs:
Who Are We Building For?
| Disability Type | Assistive Technology | What They Need |
|---|---|---|
| Visual | Screen readers, magnification | Alt text, semantic HTML, focus indicators |
| Motor | Keyboard, switch controls | Keyboard navigation, large touch targets |
| Cognitive | Simplified interfaces | Clear language, consistent patterns |
| Auditory | Captions, visual indicators | Transcripts, visual alerts |
WCAG Levels:
- A â Essential (must have)
- AA â Ideal (Grove standard)
- AAA â Enhanced (when possible)
Grove Standard: WCAG 2.1 AA
Common Barriers:
Visual Barriers:
- Poor color contrast
- Missing alt text
- Keyboard traps
- No focus indicators
Motor Barriers:
- Small touch targets (<44px)
- No keyboard support
- Time limits too short
- Required precision
Cognitive Barriers:
- Confusing navigation
- Missing error explanations
- Inconsistent patterns
- Too much information at once
Output: Accessibility requirements defined for this project
Phase 2: SCAN
The deer’s eyes scan the forest floor, spotting what blocks the path…
Automated accessibility scanning:
axe-core (Recommended):
# Install
npm install --save-dev @axe-core/cli
# Run on your site
npx axe https://localhost:5173 --tags wcag2aa
# Or in tests
npm install --save-dev @axe-core/puppeteer
Lighthouse CI:
# In CI pipeline
npx lighthouse https://yoursite.com \
--only-categories=accessibility \
--output=json
# Look for scores and specific failures
Svelte Accessibility Warnings:
<!-- Svelte warns about common issues -->
<!-- â Missing alt text -->
<img src="photo.jpg">
<!-- â
Descriptive alt -->
<img src="photo.jpg" alt="Sunset over the grove">
<!-- â No label -->
<input type="text">
<!-- â
Associated label -->
<label for="email">Email</label>
<input id="email" type="text">
<!-- â Click without keyboard -->
<div onclick={handleClick}>
<!-- â
Button with keyboard -->
<button onclick={handleClick}>
Common Issues to Check:
// a11y-checklist.ts
const checks = {
// Images
imagesHaveAlt: 'All <img> have descriptive alt text',
decorativeMarked: 'Decorative images have alt=""',
// Forms
labelsPresent: 'All inputs have associated labels',
errorsClear: 'Error messages explain how to fix',
requiredMarked: 'Required fields are indicated',
// Navigation
focusVisible: 'Focus indicators are visible',
skipLinks: 'Skip navigation links present',
headingOrder: 'Headings follow logical order (h1âh2âh3)',
// Color
contrastAA: 'Text contrast ratio >= 4.5:1',
contrastLarge: 'Large text contrast >= 3:1',
notColorOnly: 'Information not conveyed by color alone',
// Structure
landmarks: 'Page has main, nav, complementary landmarks',
langAttribute: '<html lang="en"> set correctly',
titlePresent: 'Each page has unique <title>'
};
Output: Automated scan results with specific violations
Phase 3: TEST
The deer tests each path, ensuring it can be traveled…
Manual accessibility testing:
Keyboard Navigation:
Test Checklist:
â¡ Tab through entire page - does order make sense?
â¡ Can you activate every button/link with Enter/Space?
â¡ Is there a visible focus indicator on everything?
â¡ Can you escape modals with Escape key?
â¡ No keyboard traps (can't Tab away)?
â¡ Skip link works (jumps past navigation)?
Screen Reader Testing:
# NVDA (Windows) - Free
# JAWS (Windows) - Industry standard
# VoiceOver (macOS) - Built-in
# TalkBack (Android) - Built-in
Screen Reader Checklist:
â¡ Page title announced on load
â¡ Headings navigate correctly (H key)
â¡ Landmarks listed (D key)
â¡ Images have descriptive text
â¡ Buttons say what they do
â¡ Form labels read correctly
â¡ Error messages announced
â¡ Status updates announced (aria-live)
Reduced Motion:
<script>
import { reducedMotion } from '$lib/stores/accessibility';
</script>
{#if !$reducedMotion}
<FallingLeavesLayer />
{:else}
<!-- Static version for reduced motion -->
<StaticLeaves />
{/if}
Zoom Testing:
Test at:
â¡ 100% (normal)
â¡ 150% (mild vision loss)
â¡ 200% (moderate vision loss)
â¡ 400% (severe vision loss)
Verify:
- No horizontal scrolling at 200%
- All content still visible
- No overlapping elements
Example Walkthrough Scenarios:
Scenario 1: Testing a Form with Conditional Fields
Setup: Multi-step registration form with conditional fields
1. Tab to email field
â Focus indicator visible?
â Label announced by screen reader?
2. Enter invalid email, Tab away
â Error message announced live?
â Error associated with field (aria-describedby)?
3. Check "Business account" checkbox
â New company name field appears
â Focus moves to new field OR stays logical?
â Screen reader announces new content?
4. Tab through to submit
â Skip link available to jump sections?
â Submit button clearly labeled?
â Can activate with Enter AND Space?
Scenario 2: Testing a Data Table with Sorting
Setup: Posts table with sortable columns
1. Navigate to table
â Table has caption or aria-label?
â Column headers use <th scope="col">?
â Row headers use <th scope="row">?
2. Tab to "Sort by date" column header
â Sort button focusable?
â Current sort direction announced (aria-sort)?
3. Activate sort with Enter
â Table updates without losing focus?
â Sort change announced (aria-live)?
â New sort direction reflected?
4. Navigate table with arrow keys (screen reader)
â Can move cell-by-cell?
â Row/column context announced?
Scenario 3: Testing a Modal Dialog
Setup: "Delete post" confirmation modal
1. Trigger modal from button
â Focus moves INTO modal?
â First focusable element receives focus?
â Modal has role="dialog" and aria-modal="true"?
â Modal has accessible name (aria-labelledby)?
2. Tab through modal
â Focus trapped inside modal?
â Tab wraps from last to first element?
â Shift+Tab works backwards?
3. Press Escape
â Modal closes?
â Focus returns to trigger button?
4. Click overlay/backdrop
â Modal closes (if intended behavior)?
â Or click does nothing (if modal is critical)?
Output: Manual testing complete with issue log
Phase 4: GUIDE
The deer guides the herd around obstacles, showing the clear path…
Fix accessibility issues:
Color Contrast:
<!-- Check contrast ratios -->
<!-- Use WebAIM contrast checker or similar -->
<!-- Bad: Light gray on white (fails) -->
<p class="text-gray-400">Subtle text</p>
<!-- Good: Darker gray (passes AA) -->
<p class="text-gray-600">Readable text</p>
<!-- Use the palette (already tested for contrast) -->
<p class="text-greens-grove">Brand text</p>
Semantic HTML:
<!-- â Div soup -->
<div class="card" onclick={handleClick}>
<div class="title">Heading</div>
<div class="text">Content</div>
</div>
<!-- â
Proper semantics -->
<article class="card">
<h2>Heading</h2>
<p>Content</p>
<button onclick={handleClick}>Action</button>
</article>
Focus Management:
<script>
let modalOpen = $state(false);
let modalRef: HTMLDivElement;
let previousFocus: Element;
function openModal() {
previousFocus = document.activeElement;
modalOpen = true;
// Focus first input when modal opens
tick().then(() => {
modalRef?.querySelector('input')?.focus();
});
}
function closeModal() {
modalOpen = false;
// Return focus to trigger button
previousFocus?.focus();
}
function handleKeydown(event: KeyboardEvent) {
if (event.key === 'Escape') closeModal();
}
</script>
{#if modalOpen}
<div
bind:this={modalRef}
role="dialog"
aria-modal="true"
aria-labelledby="modal-title"
onkeydown={handleKeydown}
>
<h2 id="modal-title">Modal Title</h2>
<!-- Content -->
</div>
{/if}
ARIA Labels:
<!-- When visible text isn't enough -->
<button aria-label="Close dialog">
<XIcon />
</button>
<!-- For icon-only buttons -->
<button aria-label="Add to favorites">
<HeartIcon />
</button>
<!-- Current state -->
<button aria-pressed={isExpanded}>
Expand section
</button>
<!-- Live regions for updates -->
<div aria-live="polite" aria-atomic="true">
{statusMessage}
</div>
Form Accessibility:
<form onsubmit={handleSubmit}>
<div>
<label for="email">Email Address *</label>
<input
id="email"
type="email"
required
aria-required="true"
aria-invalid={emailError ? 'true' : 'false'}
aria-describedby={emailError ? 'email-error' : undefined}
bind:value={email}
/>
{#if emailError}
<span id="email-error" role="alert" class="error">
{emailError}
</span>
{/if}
</div>
<button type="submit">Submit</button>
</form>
Output: Issues fixed with accessible implementations
Phase 5: PROTECT
The deer stands watch, ensuring the path stays clear…
Prevent future accessibility issues:
Automated Testing:
// vitest + @axe-core/puppeteer
import { test } from 'vitest';
import { AxePuppeteer } from '@axe-core/puppeteer';
import puppeteer from 'puppeteer';
test('page has no accessibility violations', async () => {
const browser = await puppeteer.launch();
const page = await browser.newPage();
await page.goto('http://localhost:5173');
const results = await new AxePuppeteer(page).analyze();
expect(results.violations).toEqual([]);
await browser.close();
});
ESLint Plugin:
npm install --save-dev eslint-plugin-jsx-a11y
# .eslintrc
{
"plugins": ["jsx-a11y"],
"extends": ["plugin:jsx-a11y/recommended"]
}
CI Integration:
# .github/workflows/a11y.yml
name: Accessibility Tests
on: [pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install dependencies
run: npm ci
- name: Build site
run: npm run build
- name: Run axe-core
run: npx axe http://localhost:4173 --exit
Documentation:
## Accessibility Standards
This project maintains WCAG 2.1 AA compliance:
### Requirements
- All images have alt text
- Color contrast minimum 4.5:1
- Keyboard navigable
- Screen reader tested
- Reduced motion respected
### Testing
- Automated: axe-core in CI
- Manual: Keyboard + screen reader
- Checklist: See a11y-checklist.md
### Tools
- axe DevTools browser extension
- WAVE evaluation tool
- Lighthouse accessibility audit
Final Report:
## ð¦ DEER SENSE AUDIT COMPLETE
### Pages Tested
- Home page
- Dashboard
- Settings
- Content editor
### Automated Results
- axe-core violations: 3 â 0
- Lighthouse score: 82 â 96
### Manual Testing
- â
Keyboard navigation: All paths work
- â
Screen reader (VoiceOver): Content readable
- â
200% zoom: Layout intact
- â
Reduced motion: Animations disabled
### Issues Fixed
1. Missing alt text on 12 images
2. Color contrast on secondary buttons
3. Missing form labels on search
4. Focus indicator on glass cards
### Ongoing Protection
- axe-core in CI pipeline
- ESLint jsx-a11y plugin enabled
- Accessibility checklist in docs
Output: Accessibility maintained with ongoing monitoring
Deer Rules
Awareness
Notice what others miss. Accessibility barriers hide in details.
Inclusion
Design for everyone. The forest is for all wanderers.
Persistence
Accessibility is ongoing, not one-time. Keep testing, keep improving.
Communication
Use sensory metaphors:
- “Listening for barriers…” (understanding needs)
- “Scanning the path…” (automated testing)
- “Testing the route…” (manual verification)
- “Guiding around obstacles…” (fixing issues)
Anti-Patterns
The deer does NOT:
- Skip testing with real assistive technology
- Rely only on automated tools (catch ~30% of issues)
- Use “accessibility overlays” (they don’t work)
- Ignore cognitive accessibility
- Treat a11y as an afterthought
Example Audit
User: “Audit the new dashboard for accessibility”
Deer flow:
-
ð¦ LISTEN â “Dashboard has complex data, charts, tables. Users: screen reader, keyboard, motor. WCAG AA target.”
-
ð¦ SCAN â “axe-core: 5 violations. Missing alt on charts, low contrast on metrics, no table headers.”
-
ð¦ TEST â “Keyboard: Can’t reach filter dropdowns. Screen reader: Tables not navigable. Zoom: Sidebar overlaps at 200%.”
-
ð¦ GUIDE â “Add aria-labels to charts, increase metric contrast, proper table markup, responsive sidebar, keyboard-accessible filters.”
-
ð¦ PROTECT â “axe-core in CI, component a11y tests, documentation updated.”
Quick Decision Guide
| Situation | Action |
|---|---|
| New component | Build with semantic HTML, test keyboard/screen reader |
| Color choice | Check contrast ratio (4.5:1 minimum) |
| Interactive element | Ensure keyboard accessible, visible focus |
| Image | Add descriptive alt text (or alt=”” if decorative) |
| Form | Associate labels, error messages, required indicators |
| Animation | Respect prefers-reduced-motion |
The forest welcomes all who seek it. Remove the barriers. ð¦