/validating-accessibility
Use this skill when creating, modifying, or reviewing any .tsx component in apps/web, even if the user doesn't mention "accessibility." Covers semantic HTML, aria labels, navigation landmarks, forms, dialogs, and keyboard navigation. Trigger on: adding buttons, links, toggles,
$ npx -y skills add tambo-ai/tambo --skill validating-accessibility --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/validating-accessibility
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use this skill when creating, modifying, or reviewing any .tsx component in apps/web, even if the user doesn't mention "accessibility." Covers semantic HTML, aria labels, navigation landmarks, forms, dialogs, and keyboard navigation. Trigger on: adding buttons, links, toggles,
SKILL.md
validating-accessibility.SKILL.mdname: validating-accessibility
description: >-
Use this skill when creating, modifying, or reviewing any .tsx component in apps/web, even if
the user doesn't mention "accessibility." Covers semantic HTML, aria labels, navigation landmarks,
forms, dialogs, and keyboard navigation. Trigger on: adding buttons, links, toggles, icons, or
any interactive element; building or editing forms; adding dialogs or modals; reviewing UI code.
Includes inline verification patterns for scanning violations. Not for styling or layout changes
that don't involve interactive elements.
metadata:
internal: true
Accessibility Checklist
Every UI component in `apps/web` must meet these standards. No partial compliance.
Gotchas
- **`role="button"` divs may exist in the codebase** -- fix them when touching affected files. `<TableHead>` elements with `role="button"` for sortable columns are acceptable.
- **Nested interactive elements** -- when replacing a `<div role="button">` that contains a child `<button>` (e.g., a copy button inside a collapsible toggle), do not just swap the outer div to `<button>`. That creates invalid nested buttons. Instead, restructure into sibling elements: a toggle `<button>` and a separate action `<button>` side by side in a flex container.
- **Standalone inputs outside react-hook-form need manual ID pairing** -- use `useId()` with `htmlFor`/`id`. The shadcn `<FormField>` handles this automatically, but raw `<Input>` does not.
- **AlertDialog vs Dialog** -- use `AlertDialog` for destructive confirmations (requires `AlertDialogTitle` + `AlertDialogDescription`). Use `Dialog` for content/forms. Never build custom modal overlays.
- **Icon-only buttons without `aria-label`** are common in new code. Every icon-only button needs one, and it must include context: `Delete API key ${keyName}`, not just "Delete".
Semantic HTML
Use native elements. Never recreate `<button>` behavior with `<div role="button">` + keyboard handlers.
| Interaction | Element | | ---------------- | ------------------------------------------------------ | | Clickable action | `<button>` or `<Button>` from `@/components/ui/button` | | Navigation link | `<Link>` (Next.js) or `<a>` | | Navigation group | `<nav>` with descriptive `aria-label` | | Item list | `<ul>`/`<ol>` + `<li>` | | Section heading | `<h1>`-`<h6>` in order, never skip levels |
Aria Labels
Every interactive element without visible text needs `aria-label` with both action AND target:
<Button size="icon" aria-label={`Delete API key ${keyName}`}>
<Trash2 className="h-4 w-4" />
</Button>
<Switch aria-label={`${enabled ? "Disable" : "Enable"} skill ${skillName}`} />Prefer state-aware labels ("Copied!" vs "Copy"). Buttons with visible text skip `aria-label`.
**Reference implementations:** `copy-button.tsx` (state-aware), `context-attachment-badge.tsx` (contextual remove), `thread-table-header.tsx` (sort state) -- all in `apps/web/components/`.
Navigation Landmarks
Wrap navigation groups in `<nav>` with a unique `aria-label` per region on the page.
Forms
Use shadcn Form components from `@/components/ui/form` (`FormField`, `FormItem`, `FormLabel`, `FormControl`, `FormMessage`). They handle ID generation, label association, `aria-describedby`, and `aria-invalid` automatically.
For standalone inputs outside react-hook-form, pair `useId()` with `htmlFor`/`id`. Never use placeholder as label substitute.
Keyboard Navigation
- Only `tabIndex={0}` or `tabIndex={-1}` (never positive values)
- Never remove focus outlines
- Prefer `<button>` over manual Enter/Space handlers
Verification
Scan `apps/web/components` for common violations. For each check, grep for the pattern and fix any matches found.
Check 1: `role="button"` on non-button elements
Search for `role="button"` in `.tsx` files. Flag `<div` or `<span` elements with this attribute; they should be `<button>` or `<Button>` instead. `<TableHead>` elements with `role="button"` for sortable columns are acceptable.
**Pattern:** `role="button"`
Check 2: `<div onClick>` patterns
Search for `<div` elements with `onClick` handlers. These should use `<button>` instead for proper keyboard support.
**Pattern:** `<div[^>]*onClick`
Check 3: Positive tabIndex values
Search for `tabIndex` with values greater than 0. Only `tabIndex={0}` and `tabIndex={-1}` are allowed.
**Pattern:** `tabIndex={[1-9]`
Check 4: Icon buttons missing aria-label
Search for `size="icon"` in `.tsx` files. For each match, check surrounding lines (5-10 above and below) for `aria-label` on the same `<Button>` element or an `sr-only` span. Flag buttons that have neither.
**Pattern:** `size="icon"` without nearby `aria-label`
Manual checks
These cannot be detected by pattern matching:
- [ ] Form inputs have associated `<label>` elements
- [ ] Navigation groups use `<nav>` with unique `aria-label`
- [ ] Dialogs use Radix-based components (AlertDialog or Dialog)
- [ ] Focus outlines intact
Read more
name: validating-accessibility description: >- Use this skill when creating, modifying, or reviewing any .tsx component in apps/web, even if the user doesn't mention "accessibility." Covers semantic HTML, aria labels, navigation landmarks, forms, dialogs, and keyboard navigation. Trigger on: adding buttons, links, toggles, icons, or any interactive element; building or editing forms; adding dialogs or modals; reviewing UI code. Includes inline verification patterns for scanning violations. Not for styling or layout changes that don't involve interactive elements. metadata: internal: true
Accessibility Checklist
Every UI component in `apps/web` must meet these standards. No partial compliance.
Gotchas
- **`role="button"` divs may exist in the codebase** -- fix them when touching affected files. `<TableHead>` elements with `role="button"` for sortable columns are acceptable.
- **Nested interactive elements** -- when replacing a `<div role="button">` that contains a child `<button>` (e.g., a copy button inside a collapsible toggle), do not just swap the outer div to `<button>`. That creates invalid nested buttons. Instead, restructure into sibling elements: a toggle `<button>` and a separate action `<button>` side by side in a flex container.
- **Standalone inputs outside react-hook-form need manual ID pairing** -- use `useId()` with `htmlFor`/`id`. The shadcn `<FormField>` handles this automatically, but raw `<Input>` does not.
- **AlertDialog vs Dialog** -- use `AlertDialog` for destructive confirmations (requires `AlertDialogTitle` + `AlertDialogDescription`). Use `Dialog` for content/forms. Never build custom modal overlays.
- **Icon-only buttons without `aria-label`** are common in new code. Every icon-only button needs one, and it must include context: `Delete API key ${keyName}`, not just "Delete".
Semantic HTML
Use native elements. Never recreate `<button>` behavior with `<div role="button">` + keyboard handlers.
| Interaction | Element | | ---------------- | ------------------------------------------------------ | | Clickable action | `<button>` or `<Button>` from `@/components/ui/button` | | Navigation link | `<Link>` (Next.js) or `<a>` | | Navigation group | `<nav>` with descriptive `aria-label` | | Item list | `<ul>`/`<ol>` + `<li>` | | Section heading | `<h1>`-`<h6>` in order, never skip levels |
Aria Labels
Every interactive element without visible text needs `aria-label` with both action AND target:
<Button size="icon" aria-label={`Delete API key ${keyName}`}>
<Trash2 className="h-4 w-4" />
</Button>
<Switch aria-label={`${enabled ? "Disable" : "Enable"} skill ${skillName}`} />Prefer state-aware labels ("Copied!" vs "Copy"). Buttons with visible text skip `aria-label`.
**Reference implementations:** `copy-button.tsx` (state-aware), `context-attachment-badge.tsx` (contextual remove), `thread-table-header.tsx` (sort state) -- all in `apps/web/components/`.
Navigation Landmarks
Wrap navigation groups in `<nav>` with a unique `aria-label` per region on the page.
Forms
Use shadcn Form components from `@/components/ui/form` (`FormField`, `FormItem`, `FormLabel`, `FormControl`, `FormMessage`). They handle ID generation, label association, `aria-describedby`, and `aria-invalid` automatically.
For standalone inputs outside react-hook-form, pair `useId()` with `htmlFor`/`id`. Never use placeholder as label substitute.
Keyboard Navigation
- Only `tabIndex={0}` or `tabIndex={-1}` (never positive values)
- Never remove focus outlines
- Prefer `<button>` over manual Enter/Space handlers
Verification
Scan `apps/web/components` for common violations. For each check, grep for the pattern and fix any matches found.
Check 1: `role="button"` on non-button elements
Search for `role="button"` in `.tsx` files. Flag `<div` or `<span` elements with this attribute; they should be `<button>` or `<Button>` instead. `<TableHead>` elements with `role="button"` for sortable columns are acceptable.
**Pattern:** `role="button"`
Check 2: `<div onClick>` patterns
Search for `<div` elements with `onClick` handlers. These should use `<button>` instead for proper keyboard support.
**Pattern:** `<div[^>]*onClick`
Check 3: Positive tabIndex values
Search for `tabIndex` with values greater than 0. Only `tabIndex={0}` and `tabIndex={-1}` are allowed.
**Pattern:** `tabIndex={[1-9]`
Check 4: Icon buttons missing aria-label
Search for `size="icon"` in `.tsx` files. For each match, check surrounding lines (5-10 above and below) for `aria-label` on the same `<Button>` element or an `sr-only` span. Flag buttons that have neither.
**Pattern:** `size="icon"` without nearby `aria-label`
Manual checks
These cannot be detected by pattern matching:
- [ ] Form inputs have associated `<label>` elements
- [ ] Navigation groups use `<nav>` with unique `aria-label`
- [ ] Dialogs use Radix-based components (AlertDialog or Dialog)
- [ ] Focus outlines intact
Repo: tambo-ai/tambo
Other skills on tambo.
- /ai-sdk-model-manager
Manages AI SDK model configurations - updates packages, identifies missing models, adds new models with research, and updates documentation
Open skill - /api-resource-lifecycle
Guides CRUD operations for API resources with cascading dependencies, descriptive validation, and orphan prevention. Use when adding delete/remove operations, creating validation logic, building resources that depend on other resources, or when the user mentions "cascade
Open skill - /building-settings-ui
Use this skill when adding or modifying settings UI in Tambo Cloud. Covers where a new settings section belongs (Agent tab vs Settings tab), and the component patterns used across both pages (card layout, toasts, confirmation dialogs, destructive styling, save behavior
Open skill - /compound-components
Creates unstyled compound components that separate business logic from styles. Use when building headless UI primitives, creating component libraries, implementing Radix-style namespaced components, or when the user mentions "compound components", "headless", "unstyled",
Open skill - /creating-styled-wrappers
Creates styled wrapper components that compose headless/base compound components. Use when refactoring styled components to use base primitives, implementing opinionated design systems on top of headless components, or when the user mentions "use base components", "compose
Open skill - /building-with-tambo
Integrates Tambo into existing React apps — detects tech stack, installs @tambo-ai/react, wires TamboProvider, registers components with Zod schemas, and sets up tools/context. Use when adding AI-powered generative UI to an existing codebase. Triggers on "add Tambo", "integrate
Open skill

