Quick reference untuk web accessibility best practices. WCAG guidelines, ARIA attributes, testing tools, dan checklist lengkap.
Prinsip-prinsip dasar Web Content Accessibility Guidelines yang harus dipenuhi untuk membuat website yang accessible.
| Principle | Description |
|---|---|
| Perceivable | Content dapat dipersepsi oleh user |
| Operable | UI dapat dioperasikan via keyboard/input lain |
| Understandable | Content dan operasi dapat dipahami |
| Robust | Bekerja di berbagai teknologi & browser |
Level-level kepatuhan WCAG untuk mengukur seberapa accessible website kita.
Persyaratan kontras warna yang harus dipenuhi agar text mudah dibaca oleh semua orang.
| Content Type | WCAG AA |
|---|
| WCAG AAA |
|---|
| Normal text (< 24px) | 4.5:1 | 7:1 |
| Large text (≥ 24px) | 3:1 | 4.5:1 |
| UI components | 3:1 | - |
Penggunaan elemen HTML yang tepat sesuai dengan fungsinya untuk membantu assistive technology.
<!-- Use proper elements -->
<button>Click</button> <!-- NOT <div onclick> -->
<a href="/page">Link</a> <!-- NOT <span onclick> -->
<nav>...</nav> <!-- NOT <div class="nav"> -->
<main>...</
<!-- Correct hierarchy -->
<h1>Page Title</h1>
<h2>Section</h2>
<h3>Subsection</h3>
<h3>Subsection</h3>
<h2>Section</h2>
<!-- Don't skip levels -->
Rules:
<h1> per page<!-- Informative image -->
<img src="chart.png" alt="Sales increased 50% in Q4 2024" />
<!-- Decorative image -->
<img src="divider.svg" alt="" />
<!-- Linked image -->
<a
Tips:
<!-- Label association -->
<label for="email">Email</label>
<input type="email" id="email" name="email"
Essential keys:
Tab - Next focusable elementShift + Tab - Previous elementEnter - Activate link/buttonSpace - Activate button, check checkboxArrow keys - Navigate within componentEsc - Close dialog/menuAll functionality must be keyboard accessible!
/* Never do this */
*:focus {
outline: none;
}
/* Custom focus style */
button:focus {
outline: 2px solid #0066cc;
outline-offset: 2px;
<div role="button">Custom Button</div>
<div role="dialog">Modal</div>
<div role="navigation">Nav</div>
<div role="alert"
Catatan: Prefer semantic HTML over ARIA roles!
| Attribute | Usage | Example |
|---|---|---|
aria-label | Label element | <button aria-label="Close">×</button> |
aria-labelledby | Reference label | <div aria-labelledby="title"> |
aria-describedby | Extra description | <input aria-describedby="hint"> |
aria-hidden | Hide from screen readers | <span aria-hidden="true">✓</span> |
aria-live | Announce changes | <div aria-live="polite"> |
<!-- Polite - wait for pause -->
<div aria-live="polite">Message sent!</div>
<!-- Assertive - interrupt immediately -->
<div aria-live="assertive">Error occurred!</div>
<!-- Atomic - read entire region -->
<div aria-live="polite"
.sr-only {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
<button>
<span aria-hidden="true">×</span>
<span class="sr-only">Close dialog</span>
</button><body>
<a href="#main" class="skip-link">Skip to main content</a>
<header>...</header>
<main id="main">...</main>
</body>.skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px;
}
/* Minimum 44×44px (WCAG AAA) */
.button {
min-width: 44px;
min-height: 44px;
padding: 12px 24px;
}<!-- Custom button -->
<div
role="button"
tabindex="0"
onkeydown="handleKeyDown(event)"
onclick="handleClick()">
Custom Button
</div>
Requirements:
role - Define semanticstabindex="0" - Make focusableEnter and Space<div
role="dialog"
aria-labelledby="dialog-title"
aria-describedby="dialog-desc"
aria-modal="true">
<h2 id="dialog-title">Confirm Delete</h2>
<p
Best practices:
Escaria-modal="true"<!-- Video with captions -->
<video controls>
<source src="video.mp4" type="video/mp4" />
<track
kind="captions"
src="captions.vtt"
srclang=
# Lighthouse (Chrome DevTools)
# DevTools → Lighthouse → Accessibility → Run
# axe DevTools (Browser extension)
# Install from Chrome/Firefox store
# Command line
npm install -g @axe-core/cli
axe https://yoursite.com| Wrong | Correct |
|---|---|
<div onclick> | <button> |
| Only placeholder, no label | Label + placeholder |
Removing :focus outline | Custom focus style |
| Low contrast (2:1) | Sufficient contrast (4.5:1) |
| Color only for meaning | Color + icon/text |
| Skipping heading levels | Logical hierarchy |
| Empty link text | Descriptive link text |
| Auto-play video/audio | User-controlled media |
| Time limits without option | Extendable/disablable |
<div aria-live="polite">Status message</div>
<div aria-live="assertive" role="alert">Error!</div><button aria-pressed="false">Toggle Off</button>
<button aria-pressed="true">Toggle On</button>
<button aria-expanded="false">Collapsed</button>
<button aria-expanded=
<!-- Works without JS -->
<details>
<summary>Click to expand</summary>
<p>Content here...</p>
</details>
<!-- Enhanced with JS if available -->
<script>
// Add custom behavior
</script>"The power of the Web is in its universality. Access by everyone regardless of disability is an essential aspect." — Tim Berners-Lee
Accessibility = Better UX for everyone! 🌐♿
aria-expanded| Expandable state |
<button aria-expanded="false"> |
aria-pressed | Toggle button | <button aria-pressed="true"> |
aria-invalid | Invalid input | <input aria-invalid="true"> |
aria-required | Required field | <input aria-required="true"> |
aria-disabled | Disabled state | <button aria-disabled="true"> |