Install
$ agentstack add skill-mikemai2awesome-agent-skills-frontend-a11y ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Frontend 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 `
,,` before reaching for JavaScript
HTML Guidelines
Use Native Elements
The browser already provides accessible elements. Use them.
Submit
Submit
Don't Add Redundant Roles
Landmark elements already have implicit roles. Don't repeat them.
...
...
...
...
...
...
...
...
Use Semantic Elements Over Divs
Replace generic containers with meaningful elements.
...
...
Skip the Title Attribute
The title attribute is poorly supported. Only use it on ``.
Submit
Component Patterns
Use native elements that already have the behavior you need.
Accordion
Use native ` and `. No JavaScript needed.
Section
Content
Section
Content
Modal Dialog
Use native ` with showModal()`. Focus trapping and Escape key handling are built-in.
Title
Content
Title
Content
Close
Open dialog
The showModal() method automatically:
- Traps focus inside the dialog
- Closes on Escape key
- Adds the
::backdroppseudo-element - Marks content behind as inert
Navigation
Use ` with and aria-expanded` for dropdowns.
Products
Product 1
Don't use role="menu", role="menuitem", or aria-haspopup for navigation.
Alert
A single role="alert" is all you need.
Your changes have been saved.
CSS Guidelines
The patterns below are specific to accessibility.
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;
}
Don't Write All Caps in HTML
Use CSS instead so screen readers don't spell out letters.
SUBMIT
Submit
.u-uppercase {
text-transform: uppercase;
}
Meet Color Contrast Requirements
Text must meet WCAG 2.1 AA minimum contrast ratios — this is one of the most commonly failed accessibility checks. Always choose foreground/background color pairs that clear these thresholds:
- Normal text (below 18pt / 14pt bold): 4.5:1 minimum
- Large text (18pt+ / 14pt+ bold): 3:1 minimum
- UI components and focus indicators: 3:1 minimum against adjacent colors
Safe defaults that clear 4.5:1 without any calculation:
/* Dark text on light backgrounds */
body {
color: #404040;
background: #f4f4f4;
} /* ~9.43:1 */
.muted {
color: #636363;
background: #f4f4f4;
} /* ~5.46:1 */
/* Light text on dark backgrounds */
.dark {
color: #c9d4de;
background: #040014;
} /* ~13.75:1 */
/* Avoid these common low-contrast mistakes */
/* color: #767676 on white = exactly 4.5:1 — pass, but use sparingly */
/* color: #999 on white = 2.85:1 — FAIL */
/* color: #888 on #eee = 3.05:1 — FAIL for normal text */
Create Consistent Focus Outlines
Ensure all interactive elements have visible, high-contrast focus indicators.
*:focus-visible {
outline: 2px solid;
outline-offset: 2px;
}
Handle Reduced Transparency
Only apply translucent or glassy effects when the user hasn't requested reduced transparency.
@media (prefers-reduced-transparency: no-preference) {
.glass-panel {
background: oklch(100% 0 0 / 0.8);
backdrop-filter: blur(1rem);
}
}
Respect Reduced Motion Preferences
Only animate elements when the user hasn't requested reduced motion.
@media (prefers-reduced-motion: no-preference) {
.animated-element {
transition: transform 0.3s ease;
}
}
Fade In Content Safely
Never use opacity: 0 alone to hide content before a fade-in animation. Screen readers ignore opacity — an element at opacity: 0 is still in the accessibility tree and will be announced before sighted users can see it.
A safe fade-in layers two protections:
- JS-ready gating so content stays visible by default if JavaScript fails to load
- IntersectionObserver so the animation triggers when the element enters the viewport — including when a screen reader's virtual cursor scrolls to it
Welcome
@media (prefers-reduced-motion: no-preference) {
.js-ready .fade-in {
opacity: 0;
}
.fade-in.is-visible {
animation: fade-in 0.6s ease forwards;
}
}
@keyframes fade-in {
to {
opacity: 1;
}
}
document.documentElement.classList.add("js-ready");
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
entry.target.classList.add("is-visible");
observer.unobserve(entry.target);
}
}
});
for (const el of document.querySelectorAll(".fade-in")) {
observer.observe(el);
}
This works because:
- Without JavaScript, the
js-readyclass is never added — content stays fully visible and accessible - When a screen reader's virtual cursor reaches the element, the browser scrolls it into view, firing the IntersectionObserver and triggering the fade-in before the content is announced
- Users who prefer reduced motion never get the hidden state applied — content is visible immediately
References
Read these when you need more detail than the guidelines above:
- [standards.md](references/standards.md) - Read when you need to cite WCAG 2.2 criteria, WAI-ARIA specs, or validation tools
- [patterns.md](references/patterns.md) - Read when implementing complex components like carousels, comboboxes, or feed patterns not covered above
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: mikemai2awesome
- Source: mikemai2awesome/agent-skills
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.