com um clique
accessibility
Web accessibility (a11y). WCAG 2.1, ARIA, keyboard navigation, screen readers, testing tools.
Menu
Web accessibility (a11y). WCAG 2.1, ARIA, keyboard navigation, screen readers, testing tools.
Analytics and event tracking. Product analytics, Mixpanel, PostHog, Segment, GDPR compliance, event taxonomy.
UI animation patterns. CSS transitions, Framer Motion, GSAP, performance, accessibility.
Authentication and authorization patterns. JWT, sessions, OAuth 2.0, RBAC, ABAC, refresh tokens, security.
Background job patterns. Cron jobs, queued jobs, scheduling, BullMQ, pg-boss, Sidekiq patterns.
Caching patterns. HTTP caching, Redis, CDN, browser cache, cache invalidation strategies.
CI/CD pipeline patterns. GitHub Actions, GitLab CI, Docker builds, testing, deployment stages, secrets management.
| name | accessibility |
| description | Web accessibility (a11y). WCAG 2.1, ARIA, keyboard navigation, screen readers, testing tools. |
Accessible apps are better apps for everyone.
| Principle | Meaning |
|---|---|
| Perceivable | Info must be presentable to all senses |
| Operable | UI must be keyboard + assistive tech operable |
| Understandable | Content and operation must be comprehensible |
| Robust | Must work with current + future assistive tech |
Target: AA conformance (minimum for most products)
| Element | Minimum Ratio | AA+ Ratio |
|---|---|---|
| Normal text (< 18px) | 4.5:1 | 7:1 |
| Large text (≥ 18px) | 3:1 | 4.5:1 |
| UI components (borders, icons) | 3:1 | — |
| Disabled state | No requirement | — |
Tools:
├── WebAIM Contrast Checker
├── Chrome DevTools → Accessibility panel
└── axe DevTools browser extension
\x3C!-- ❌ Bad -->
<div class="button" onclick="submit()">Submit</div>
<div class="heading">Section Title</div>
<div class="nav">...</div>
\x3C!-- ✅ Good -->
<button type="submit">Submit</button>
<h2>Section Title</h2>
<nav aria-label="Main navigation">...</nav>
\x3C!-- When to use ARIA:
Rule 1: Don\'t use ARIA if native HTML works
Rule 2: Don\'t change native semantics unnecessarily
Rule 3: All interactive ARIA controls must be keyboard-focusable -->
\x3C!-- Modal Dialog -->
<div role="dialog" aria-modal="true" aria-labelledby="modal-title" aria-describedby="modal-desc">
<h2 id="modal-title">Confirm Delete</h2>
<p id="modal-desc">This action cannot be undone.</p>
<button>Cancel</button>
<button>Delete</button>
</div>
\x3C!-- Live Region (dynamic content) -->
<div aria-live="polite" aria-atomic="true">
\x3C!-- Updates announced to screen readers -->
{statusMessage}
</div>
\x3C!-- Icon button -->
<button aria-label="Close dialog">
<svg aria-hidden="true">...</svg>
</button>
\x3C!-- Loading -->
<div role="status" aria-label="Loading users...">
<Spinner />
</div>
| Key | Expected Behavior |
|---|---|
Tab | Move to next focusable element |
Shift+Tab | Move to previous |
Enter / Space | Activate button/link |
Escape | Close modal/dropdown |
| Arrow keys | Navigate within widget (menu, tabs, listbox) |
// Focus trap (modals)
import { useFocusTrap } from "@mantine/hooks"; // or write custom
function Modal({ isOpen, onClose, children }) {
const focusTrapRef = useFocusTrap(isOpen);
return isOpen ? (
<div ref={focusTrapRef} role="dialog" aria-modal="true">
{children}
</div>
) : null;
}
// Skip link (keyboard shortcut to main content)
<a href="#main-content" className="sr-only focus:not-sr-only">
Skip to main content
</a>
\x3C!-- Informative image -->
<img src="chart.png" alt="Bar chart showing 40% increase in Q4 revenue" />
\x3C!-- Decorative image -->
<img src="divider.png" alt="" role="presentation" />
\x3C!-- Icon (standalone meaning) -->
<svg aria-label="Settings" role="img">...</svg>
\x3C!-- Video -->
<video>
<track kind="captions" src="captions.vtt" srclang="en" label="English" />
</video>
\x3C!-- Always associate label with input -->
<label for="email">Email address</label>
<input id="email" type="email" required
aria-describedby="email-hint email-error"
aria-invalid="true" />
<span id="email-hint">We\'ll never share your email.</span>
<span id="email-error" role="alert">Please enter a valid email.</span>
\x3C!-- Fieldset for groups -->
<fieldset>
<legend>Notification preferences</legend>
<label><input type="checkbox" name="email"> Email</label>
<label><input type="checkbox" name="sms"> SMS</label>
</fieldset>
| Tool | Type | Use For |
|---|---|---|
| axe DevTools | Browser ext | Automated scan |
| Lighthouse | Chrome DevTools | Audit score |
| NVDA | Screen reader (Windows) | Manual testing |
| VoiceOver | Screen reader (Mac/iOS) | Manual testing |
| jest-axe | Unit test | CI/CD integration |
| Playwright | E2E | Keyboard/focus testing |
// jest-axe
import { axe, toHaveNoViolations } from "jest-axe";
expect.extend(toHaveNoViolations);
it("has no accessibility violations", async () => {
const { container } = render(<MyForm />);
const results = await axe(container);
expect(results).toHaveNoViolations();
});
outline: none)<h1>, logical heading hierarchy