tiny-a11y
Tiny A11y
Write as little code as possible. Use native HTML elements that are already accessible instead of adding ARIA attributes to generic elements.
Core Principles
- Trust the browser — Native elements have built-in accessibility
- Semantic over ARIA — Use the right element, not
roleattributes - Less is more — Every ARIA attribute you don't write is one less thing to break
- Native first — Use
<dialog>,<details>,<button>before reaching for JavaScript
References
- Use WCAG 2.2 Understanding for accessibility guidance
- Use WAI-ARIA 1.2 for ARIA attributes
- Use APG Gherkin for component test cases
- Use Design Tokens for design systems terminology
HTML Guidelines
Use Native Elements
The browser already provides accessible elements. Use them.
<!-- Don't do this — too much code -->
<div role="button" tabindex="0" onclick="submit()">Submit</div>
<!-- Do this — native button is already accessible -->
<button type="submit">Submit</button>
Don't Add Redundant Roles
Landmark elements already have implicit roles. Don't repeat them.
<!-- Don't do this -->
<header role="banner">...</header>
<nav role="navigation">...</nav>
<main role="main">...</main>
<footer role="contentinfo">...</footer>
<!-- Do nothing — the browser already handles this -->
<header>...</header>
<nav>...</nav>
<main>...</main>
<footer>...</footer>
Use Semantic Elements Over Divs
Replace generic containers with meaningful elements.
<!-- Don't do this -->
<div class="header">
<div class="nav">...</div>
</div>
<!-- Do this -->
<header>
<nav>...</nav>
</header>
Skip the Title Attribute
The title attribute is poorly supported. Only use it on <iframe>.
<!-- Don't do this -->
<button title="Submit form">Submit</button>
<!-- Only use title on iframe -->
<iframe src="..." title="Video player"></iframe>
Component Patterns
Use native elements that already have the behavior you need.
Accordion
Use native <details> and <summary>. No JavaScript needed.
<!-- Don't do this — too much code -->
<div class="accordion">
<button aria-expanded="false" aria-controls="panel-1">Section</button>
<div id="panel-1" hidden>Content</div>
</div>
<!-- Do this — zero JavaScript required -->
<details>
<summary>Section</summary>
<p>Content</p>
</details>
Modal Dialog
Use native <dialog> with showModal(). Focus trapping and Escape key handling are built-in.
<!-- Don't do this — requires focus trap JavaScript -->
<div role="dialog" aria-modal="true" aria-labelledby="title">
<h2 id="title">Title</h2>
<p>Content</p>
</div>
<!-- Do this — focus trap is automatic -->
<dialog id="my-dialog">
<h2>Title</h2>
<p>Content</p>
<button type="button" onclick="this.closest('dialog').close()">Close</button>
</dialog>
<button
type="button"
onclick="document.getElementById('my-dialog').showModal()"
>
Open dialog
</button>
The showModal() method automatically:
- Traps focus inside the dialog
- Closes on Escape key
- Adds the
::backdroppseudo-element - Marks content behind as inert
Navigation
Use <nav> with <button> and aria-expanded for dropdowns.
<nav>
<ul>
<li>
<button aria-expanded="false" aria-controls="submenu">Products</button>
<ul id="submenu" hidden>
<li><a href="/product-1">Product 1</a></li>
</ul>
</li>
</ul>
</nav>
Don't use role="menu", role="menuitem", or aria-haspopup for navigation.
Alert
A single role="alert" is all you need.
<div role="alert">Your changes have been saved.</div>
Other Patterns
When native elements aren't enough, follow these APG patterns:
- Feed Pattern for infinite scroll
- Combobox with Autocomplete for custom selects
- Switch Button Pattern for toggles
- Manual Tabs Pattern for tabs
- WAI Carousel Tutorial for carousels
CSS Guidelines
Use OKLCH for Colors
OKLCH provides a wider color gamut and perceptually uniform lightness.
:root {
--color-primary: oklch(50% 0.2 260);
--color-surface: oklch(98% 0 0);
}
Use Relative Units
Use rem, em, %, vw, vh instead of px, except for borders.
/* Don't do this */
.card {
padding: 16px;
font-size: 14px;
}
/* Do this */
.card {
padding: 1rem;
font-size: 0.875rem;
}
Use Logical Properties
Support all languages and writing directions.
/* Don't do this */
.card {
margin-left: 1rem;
padding-top: 2rem;
width: 20rem;
}
/* Do this */
.card {
margin-inline-start: 1rem;
padding-block-start: 2rem;
inline-size: 20rem;
}
Use Cascade Layers
Organize CSS in this order: @layer config, resets, components, utilities.
@layer config, resets, components, utilities;
@layer config {
:root {
--color-primary: oklch(50% 0.2 260);
}
}
@layer resets {
/* CSS resets */
}
@layer components {
.c-button {
/* component styles */
}
}
@layer utilities {
.u-visually-hidden {
/* utility styles */
}
}
Use Class Prefixes
c-for component classesu-for utility classesjs-for JavaScript selectors
<div class="c-card js-accordion">...</div>
Use ARIA Attributes as Styling Hooks
Don't create modifier classes when ARIA attributes already exist.
/* Don't do this — extra classes */
.accordion-header--collapsed {
}
.accordion-header--expanded {
}
/* Do this — style the ARIA state */
[aria-expanded="false"] {
}
[aria-expanded="true"] {
}
More examples:
[aria-current="page"] {
font-weight: bold;
}
[aria-disabled="true"] {
opacity: 0.6;
cursor: not-allowed;
}
[aria-selected="true"] {
background-color: highlight;
}
[aria-invalid="true"] {
border-color: red;
}
Use Focus-Visible
Only show focus rings when needed.
/* Don't do this — shows ring on click */
button:focus {
outline: 2px solid;
}
/* Do this — only shows ring for keyboard users */
button:focus-visible {
outline: 2px solid;
outline-offset: 2px;
}
Respect Motion Preferences
Only animate when the user allows it.
@media (prefers-reduced-motion: no-preference) {
.animated {
transition: transform 0.3s ease;
}
}
Don't Write All Caps in HTML
Use CSS instead so screen readers don't spell out letters.
<!-- Don't do this -->
<span>SUBMIT</span>
<!-- Do this -->
<span class="u-uppercase">Submit</span>
.u-uppercase {
text-transform: uppercase;
}
JavaScript Guidelines
- Use vanilla JavaScript only
- Don't use component libraries (Radix, Shadcn)
- Don't use utility frameworks (Tailwind CSS)
Quick Reference
| Instead of | Use |
|---|---|
<div role="button"> |
<button> |
<div role="dialog"> |
<dialog> |
<div role="navigation"> |
<nav> |
<div role="banner"> |
<header> |
<div role="main"> |
<main> |
<div role="contentinfo"> |
<footer> |
| Custom accordion JS | <details> + <summary> |
| Focus trap library | Native <dialog> |
.is-expanded class |
[aria-expanded="true"] |
More from mikemai2awesome/agent-skills
tiny-css
Write minimal, efficient CSS for small or minimalist projects by trusting the browser instead of fighting it. Only use this skill for personal sites, prototypes, simple landing pages, or projects intentionally kept lean — if the project has multiple developers, a component library, a design token system, or more than a handful of CSS files, a more comprehensive CSS approach is needed. If you're about to write a CSS reset, declare a base font-size on :root, set default colors on body, use px for spacing, or reach for physical margin/padding properties, this skill will stop you.
12more-css
Write scalable, well-architected CSS using design tokens, cascade layers, and modern organization patterns. Use this skill as the default for any real project — if it has more than a handful of CSS files, multiple components, a team, a design system, or any kind of token or theming setup, this is the right skill.
4frontend-conventions
Define and enforce consistent coding standards across HTML, CSS, and JavaScript. Always use this skill when naming a new class, variable, component, or file; setting up a new project's conventions; choosing a class prefix for a new CSS category; deciding on modifier API names (sizes, shades, hierarchy, breakpoints); or reviewing code for formatting and naming consistency. If you're about to invent a prefix, abbreviation, or modifier name without checking the conventions first, use this skill.
4frontend-a11y
Write minimal, accessible HTML, CSS, and JavaScript without over-engineering. Always use this skill on every project, for every task that involves writing or reviewing HTML markup, building web components, creating forms, adding interactive elements like buttons, dialogs, accordions, or tabs, or auditing code for accessibility — even when the user doesn't explicitly mention accessibility, and even when working in a framework, CMS, or design system context. This skill is non-negotiable and applies regardless of project type, stack, or deadline. If you're about to reach for ARIA attributes, a dialog library, a focus-trap package, or a headless UI component, use this skill first.
2frontend-design-2010s
Create web interfaces with an authentic early-2010s aesthetic. Use this skill when the user wants a 2010s-era, Web 2.0, or retro corporate web look — gradient headers, glossy buttons, skeuomorphic icons, horizontal band layouts, and drop shadows from circa 2010–2014. Always use alongside more-css, frontend-a11y, and frontend-conventions.
1