From beacon
Proactive accessible design guidance and pattern library. Use BEFORE writing UI code — when the user is designing a page layout, choosing a component, picking colors, planning navigation, creating a form, building a modal, or making any UI/UX decision. Triggers on "design accessible", "a11y design", "accessible component", "inclusive design", "accessible pattern", "how should I design", "what's the accessible way to", "design guide", "accessible UI", "accessible UX", or when the user describes a UI they want to build and you recognize accessibility implications. Even if they don't mention accessibility — if they're designing UI, this skill helps them do it right from the start. Also use when the user asks for accessible color palettes, typography systems, or layout patterns. This is the "before you code" skill; a11y-advisor is the "while you code" skill; a11y-audit is the "after you code" skill.
How this command is triggered — by the user, by Claude, or both
Slash command
/beacon:guideFiles this command reads when invoked
The summary Claude sees in its command listing — used to decide when to auto-load this command
# Accessible Design Guide Guide the user toward accessible design decisions **before code is written**. This is not an auditor or a linter — it is a design advisor that helps teams build inclusively from the start, so there is less to fix later. ## When This Skill Activates This skill is for the **design phase** — before or during UI planning. The three a11y skills form a pipeline: | Phase | Skill | Role | |-------|-------|------| | Design | **a11y-design-guide** (this) | Proactive guidance, pattern selection | | Development | a11y-advisor | Real-time checks on code edits | | Review | a...
Guide the user toward accessible design decisions before code is written. This is not an auditor or a linter — it is a design advisor that helps teams build inclusively from the start, so there is less to fix later.
This skill is for the design phase — before or during UI planning. The three a11y skills form a pipeline:
| Phase | Skill | Role |
|---|---|---|
| Design | a11y-design-guide (this) | Proactive guidance, pattern selection |
| Development | a11y-advisor | Real-time checks on code edits |
| Review | a11y-audit | Structured audit with scoring |
If the user is already writing code, a11y-advisor handles it. If they want a full audit, a11y-audit handles it. This skill is for "I'm about to build X — how should I design it?"
<button>, <dialog>, <details> accessible. Use them.This is a pattern catalog with strong defaults — but no pattern catalog can replace the people you are designing for. Use this guide as a launchpad, not as the destination. Pair recommendations with real-user input where it matters, and tell the user when you are operating at the edge of what the guide can know.
This guide ships strong defaults, but several dimensions of real-world accessibility are better confirmed by the people building and using the product:
alt="image" passes axe; it tells a screen-reader user nothing.When operating in these spaces, say so. "I am suggesting a conventional accessible pattern; your context may need adaptation" beats projecting false confidence.
Ordered by real-user impact, not by ease of measurement:
/beacon:inspect on candidate pages to catch the machine-detectable subset (~30–40% of WCAG criteria). Useful as a baseline, not as a completion certificate.When recommending a pattern, briefly acknowledge the boundary in plain language:
Avoid framing guidance as "the accessible answer" when it is more accurately "the conventional accessible default". The difference matters: the former projects false closure; the latter invites verification.
When the user's question carries a flawed assumption, reframe rather than just answering:
| User says | Better reframe |
|---|---|
| "Is this WCAG-compliant?" | "Does this work for the users you need to reach? WCAG is a floor, not a ceiling — meeting it doesn't yet mean meeting your users." |
| "Just give me the accessible way to do X" | "Here is the conventional accessible pattern; here is what's worth verifying with real users before locking it in." |
| "We can audit later" | "Patterns chosen now shape what gets to be audited later. Decisions at this stage are roughly 10× cheaper to revise than after build — worth getting the framing right now." |
| "We don't have disabled users on our team" | "Then we are designing for an imagined user. Including disabled testers — even one paid session — before lock-in is one of the highest-leverage interventions available." |
| "Our overlay widget handles a11y" | "Accessibility overlays (AccessiBe, UserWay, EqualWeb) have a poor track record with real assistive-tech users and are the primary cause of recent ADA lawsuits against the sites that installed them. Native semantic implementation is more reliable." |
| "Will dark mode help accessibility?" | "Dark mode is a UX preference, not a WCAG requirement. The accessibility question is whether your light mode meets contrast — and whether your dark mode (if you ship one) is fully implemented, since half-implemented dark mode causes real performance pain when browsers force-dark." |
For context on where this guide sits in the overall workflow:
| Phase | Skill | What it does | What it cannot do |
|---|---|---|---|
| Design (this) | /beacon:guide | Pattern recommendations, defaults, scaffolds | Predict your specific users, context, edge cases |
| Development | /beacon:advisor | Real-time checks on code edits via PostToolUse hook | Verify runtime behaviour, dynamic interactions |
| Review | /beacon:inspect | Tier 1–3 audit, scoring, jurisdiction-aware WCAG context | Detect ~60–70% of WCAG criteria that need humans |
| Real-world | (out of skill scope) | Real disabled-user testing, hired disabled team members, iterative pre-launch consultation | (this is what closes the gap the three skills can't) |
When the user finishes a design consultation with this skill, suggest the next phase rather than implying the work is done. Example closer:
"These patterns give you an accessible starting structure. Once code exists,
/beacon:advisorwill check it as you edit;/beacon:inspectwill score it before ship. The piece these skills cannot replace is talking to one or two disabled users about your actual flow — even a single session reshapes decisions in ways tools cannot."
When the user describes what they want to build, respond with:
Format:
## Accessible [Component Name]
### Recommended Approach
[Description of the pattern and why it works]
### Who Benefits
- [Disability category]: [How they interact with this component]
- [Situation]: [How this helps beyond disability]
### Anti-Patterns to Avoid
- [Anti-pattern]: [Why it fails and who it excludes]
### Semantic Structure
[HTML scaffold with ARIA only where native elements aren't sufficient]
### Design Checklist
- [ ] [Concrete, verifiable check item]
When the user asks for a specific pattern, provide the full accessible implementation from the pattern catalog below. Always include:
When the user is setting up a design system or starting a new project, offer to establish accessible foundations:
prefers-reduced-motion built inSkip Link The first focusable element. Invisible until focused. Lets keyboard users bypass repetitive navigation.
<a href="#main-content" class="skip-link">Skip to main content</a>
<!-- ... nav ... -->
<main id="main-content" tabindex="-1">
Responsive Nav (Hamburger)
<nav aria-label="Main"> wraps everything<button aria-expanded="false" aria-controls="nav-menu"><ul> inside <nav>, not a separate overlayaria-current="page" on the linkBreadcrumb
<nav aria-label="Breadcrumb">
<ol>
<li><a href="/">Home</a></li>
<li><a href="/products">Products</a></li>
<li><a href="/products/shoes" aria-current="page">Shoes</a></li>
</ol>
</nav>
Text Input
<div class="field">
<label for="email">Email address</label>
<input id="email" type="email" autocomplete="email"
aria-describedby="email-hint" required>
<p id="email-hint" class="hint">We will never share your email.</p>
</div>
Error State
<div class="field field--error">
<label for="email">Email address</label>
<input id="email" type="email" aria-invalid="true"
aria-describedby="email-error" required>
<p id="email-error" class="error" role="alert">
Please enter a valid email address (e.g., [email protected]).
</p>
</div>
Selection (Use Radio Group, Not <select>)
<select> is hostile to elderly users, low-vision users, and motor-impaired users. For < 7 options, always use radio buttons or a segmented control.
<fieldset>
<legend>Shipping method</legend>
<label><input type="radio" name="shipping" value="standard"> Standard (5-7 days)</label>
<label><input type="radio" name="shipping" value="express"> Express (2-3 days)</label>
<label><input type="radio" name="shipping" value="overnight"> Overnight</label>
</fieldset>
Modal Dialog
<dialog id="confirm-dialog" aria-labelledby="dialog-title">
<h2 id="dialog-title">Confirm deletion</h2>
<p>This action cannot be undone.</p>
<div class="dialog-actions">
<button type="button" data-action="cancel">Cancel</button>
<button type="button" data-action="confirm" autofocus>Delete</button>
</div>
</dialog>
Key behaviors:
<dialog> with .showModal() — handles focus trap, backdrop, Escape to closeautofocusClickable Card
<article class="card">
<img src="..." alt="[Descriptive alt text]" loading="lazy">
<h3><a href="/article/123">Article Title</a></h3>
<p>Summary text...</p>
<span class="meta" aria-label="Published March 15, 2026">Mar 15</span>
</article>
The link is inside the heading. The entire card can be made clickable via CSS (card:has(a:hover)) without wrapping everything in an <a> (which would make the entire card text announced as a single link).
Icon Button
<button type="button" aria-label="Close">
<svg aria-hidden="true" ...>[icon]</svg>
</button>
Toggle Button
<button type="button" aria-pressed="false" onclick="toggleDarkMode(this)">
<svg aria-hidden="true" ...>[moon icon]</svg>
Dark mode
</button>
Data Table
<table>
<caption>Q1 2026 Sales by Region</caption>
<thead>
<tr>
<th scope="col">Region</th>
<th scope="col">Revenue</th>
<th scope="col">Growth</th>
</tr>
</thead>
<tbody>
<tr>
<th scope="row">Asia Pacific</th>
<td>$1.2M</td>
<td>+15%</td>
</tr>
</tbody>
</table>
Loading State
<div aria-live="polite" aria-busy="true">
<p>Loading results...</p>
</div>
When loaded:
<div aria-live="polite" aria-busy="false">
<p>12 results found.</p>
<!-- results -->
</div>
Toast / Notification
<div role="status" aria-live="polite" class="toast">
Settings saved successfully.
</div>
For errors, use role="alert" (implicitly aria-live="assertive").
| Element | Ratio | Example |
|---|---|---|
| Body text | >= 4.5:1 | #595959 on #ffffff = 7.0:1 |
| Large text (>= 18pt or >= 14pt bold) | >= 3:1 | #767676 on #ffffff = 4.5:1 |
| UI components (borders, icons) | >= 3:1 | Focus rings, form borders |
| Placeholder text | >= 4.5:1 | NOT the default browser gray |
Use CSS custom properties that swap per theme. Never hardcode color: white or color: #fff.
:root {
--color-text: #1a1a2e;
--color-bg: #ffffff;
--color-surface: #f5f5f5;
--color-accent: #0066cc;
}
[data-theme="dark"] {
--color-text: #e8e8e8;
--color-bg: #1a1a2e;
--color-surface: #16213e;
--color-accent: #4fc3f7;
}
Cross-test prefers-contrast: more with BOTH themes. Android Bold text triggers high contrast mode — --color-text: #333 on a dark background becomes invisible.
When the user asks for a color palette, verify every combination:
body {
font-size: clamp(1rem, 0.9rem + 0.5vw, 1.125rem); /* 16-18px */
line-height: 1.6;
letter-spacing: 0.01em;
word-spacing: 0.05em;
}
h1 { font-size: clamp(1.75rem, 1.5rem + 1.25vw, 2.5rem); }
h2 { font-size: clamp(1.375rem, 1.2rem + 0.875vw, 1.875rem); }
h3 { font-size: clamp(1.125rem, 1rem + 0.625vw, 1.5rem); }
max-width: 65ch)text-align: justify creates uneven spacing, hard for dyslexic users)/* Respect user preference */
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after {
animation-duration: 0.01ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.01ms !important;
scroll-behavior: auto !important;
}
}
transition: all with CSS custom properties (causes visual lag during theme switches)transition: opacity 0.2s, transform 0.2s/* Minimum interactive target size */
button, a, input, select, [role="button"] {
min-height: 44px; /* iOS guideline */
min-width: 44px;
}
/* WCAG 2.5.8 minimum: 24x24 CSS pixels */
/* But 44x44 is the recommended sweet spot */
For stacked buttons (e.g., fixed-position actions), maintain >= 24px gap between targets.
/* Grid that doesn't overflow at narrow viewports */
.grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
gap: 1.5rem;
}
Key rule: use minmax(min(Npx, 100%), 1fr) instead of minmax(Npx, 1fr) — the latter overflows when the viewport is narrower than Npx, violating WCAG 1.4.10 (Reflow).
RWD and accessibility are deeply intertwined. A "responsive" site that becomes unusable on mobile for elderly users or breaks at 200% zoom is not truly responsive. Design for adaptation across devices, viewports, zoom levels, and user preferences.
<!-- CORRECT -->
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- WRONG — blocks user zoom (WCAG 1.4.4 violation) -->
<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no, maximum-scale=1">
Never disable zoom. Elderly and low-vision users depend on pinch-to-zoom as a last resort. iOS Safari ignores user-scalable=no since iOS 10, but Android Chrome still respects it.
Design mobile-first. Start with single-column layout, add complexity at wider viewports.
/* Mobile-first: base styles are for narrow screens */
.container { padding: 1rem; }
/* Tablet and up */
@media (min-width: 768px) {
.container { padding: 2rem; max-width: 720px; margin: 0 auto; }
}
/* Desktop */
@media (min-width: 1024px) {
.container { max-width: 960px; }
}
Important: the 320px width is the WCAG 1.4.10 (Reflow) test width — content must work here without horizontal scrolling. Test at this width explicitly.
<nav aria-label="Main">
<button aria-expanded="false" aria-controls="nav-menu"
class="nav-toggle" type="button">
<span class="visually-hidden">Menu</span>
<svg aria-hidden="true"><!-- hamburger icon --></svg>
</button>
<ul id="nav-menu" class="nav-list" role="list">
<li><a href="/" aria-current="page">Home</a></li>
<li><a href="/about">About</a></li>
<li><a href="/contact">Contact</a></li>
</ul>
</nav>
.nav-toggle { display: none; }
@media (max-width: 767px) {
.nav-toggle { display: flex; min-height: 44px; min-width: 44px; }
.nav-list { display: none; }
.nav-list[data-open="true"] { display: block; }
}
Keyboard behavior:
aria-expanded)/* Stack labels above inputs on mobile for easier tapping */
.form-field { display: flex; flex-direction: column; gap: 0.25rem; }
/* Side-by-side on wider screens if space allows */
@media (min-width: 768px) {
.form-field--inline { flex-direction: row; align-items: center; }
}
/* Proper input sizing for touch */
input, select, textarea {
font-size: 1rem; /* prevents iOS zoom on focus (< 16px triggers zoom) */
padding: 0.75rem;
min-height: 44px;
}
Mobile input hints — use inputmode to show the right keyboard:
| Field | type | inputmode | Keyboard |
|---|---|---|---|
| Phone | tel | tel | Phone dial pad |
email | email | @ and .com keys | |
| Amount | text | decimal | Number pad with decimal |
| Card number | text | numeric | Number pad |
| ZIP/Postal | text | numeric | Number pad |
| Search | search | search | Search key |
Tables on mobile need special handling. Two approaches:
Option A: Horizontal scroll (simpler, works for data-heavy tables)
<div class="table-wrapper" role="region" aria-label="User data" tabindex="0">
<table><!-- ... --></table>
</div>
.table-wrapper { overflow-x: auto; -webkit-overflow-scrolling: touch; }
The tabindex="0" and role="region" ensure keyboard users can scroll and screen readers announce the scrollable area.
Option B: Card reflow (better UX for simple tables)
@media (max-width: 767px) {
table, thead, tbody, tr, th, td { display: block; }
thead { position: absolute; clip: rect(0,0,0,0); }
td::before {
content: attr(data-label);
font-weight: 600;
display: inline-block;
width: 40%;
}
}
<picture>
<source srcset="hero-desktop.webp" media="(min-width: 1024px)">
<source srcset="hero-tablet.webp" media="(min-width: 768px)">
<img src="hero-mobile.webp" alt="[Descriptive text]"
loading="lazy" decoding="async"
width="800" height="450">
</picture>
Always include width and height to prevent layout shift (CLS). Use loading="lazy" for below-fold images.
/* Fluid spacing scale */
:root {
--space-xs: clamp(0.25rem, 0.2rem + 0.25vw, 0.5rem);
--space-sm: clamp(0.5rem, 0.4rem + 0.5vw, 0.75rem);
--space-md: clamp(1rem, 0.8rem + 1vw, 1.5rem);
--space-lg: clamp(1.5rem, 1.2rem + 1.5vw, 2.5rem);
--space-xl: clamp(2rem, 1.5rem + 2.5vw, 4rem);
}
body {
padding: env(safe-area-inset-top)
env(safe-area-inset-right)
env(safe-area-inset-bottom)
env(safe-area-inset-left);
}
/* Fixed bottom buttons need safe area */
.fixed-bottom-bar {
padding-bottom: calc(1rem + env(safe-area-inset-bottom));
}
WCAG requires content to work at 200% zoom (1.4.4) and at 320px equivalent width (1.4.10). Test both:
Performance is an accessibility issue — slow sites on weak connections disproportionately affect users in rural areas, developing countries, and elderly users with older devices.
loading="lazy" for images below the foldprefers-reduced-data for heavy mediadefer or type="module"<link rel="preconnect"> for critical third-party originsWhen designing responsive layouts:
inputmode set for appropriate mobile keyboardssrcset/sizesBeacon keeps design consultation notes and audit artifacts local unless the user explicitly shares them outside the plugin. Maintainer-run offline evaluation can improve future detectors, and users receive those improvements by updating Beacon.
Shared with a11y-advisor and a11y-audit:
../references/disabilities.md — Disability categories and global stats../references/patterns.md — Extended component patterns../references/wcag-quick.md — WCAG 2.2 by scenario../references/legal-brief.md — Legal quick referencenpx claudepluginhub chiehweihuang/beacon --plugin beacon/guideDisplays quick start guide for vibe-guide plugin, covering session commands, workflows, examples, troubleshooting, and created files.
/guideRuns interactive first-time user guide: diagnoses project setup (detects files like package.json/go.mod, CLAUDE.md, Git), provides situation-specific advice, suggests workflows, marks onboarding complete.
/guideScans project docs, reviews, and exit status for testany-eng workflow; outputs current stage, ready baselines, Mermaid DAG, and 1-3 next skill or downstream recommendations.
/guideLoads and executes Hypo-Workflow skill instructions for guided planning. Supports user-provided arguments and enforces disciplined question-asking for scope, safety, and architecture decisions.