Accessibility (A11y) Checks
Use semantic HTML; end the <div> stacks
Screen readers and search engines infer structure from tags. Using <div onclick> instead of <button> means you must reimplement keyboard, enter, role, focus — and you'll miss something.
<header>, <nav>, <main>, <article>, <aside>, <footer>. Clickable → <button> or <a>. Form controls → <label>, <fieldset>, <legend>. Lists → <ul>/<ol>/<li>.
Wrong: <div class="btn" onclick=...>Submit</div>. Right: <button type="submit">Submit</button>.
Write meaningful alt text on images
Screen reader users, people with vision impairments, and anyone whose image failed to load depend on alt text. Missing alt = missing content.
Describe the content on informative images; on decorative ones use alt="". If an icon is next to its text, mark the icon aria-hidden="true". Write helpful descriptions, not keyword stuffing.
Wrong: <img src="logo.png" alt="logo">. Right: <img src="logo.png" alt="Duck — wizard from idea to architecture"> or alt="" if decorative.
Color contrast at least 4.5:1 for normal text
Low contrast is unreadable for low-vision users and anyone using a phone outdoors. WCAG AA requires 4.5:1 for normal text and 3:1 for large text.
Test palette for contrast at design time (Figma Contrast, contrast-ratio.com). Avoid light-gray text on gray. Don't use placeholder as a value indicator; #999 fails contrast.
Bootstrap 5: text-muted default #6c757d (4.54:1 on white — passes AA). Lighter than that fails AA.
Keep a visible focus indicator
Keyboard, screen reader, and switch users rely on the focus ring to know where they are. 'outline: none' is the most common a11y mistake.
Never globally strip outline. For custom design, redefine the ring via :focus-visible (sufficient contrast, ≥2px thick). :focus-visible triggers only on keyboard, not mouse.
button:focus-visible { outline: 2px solid #3b82f6; outline-offset: 2px; } All interactions must work with keyboard
Anyone who can't use a trackpad/mouse depends on the keyboard. Menus that appear only on hover, cards that only move via drag — these block many users.
Natural tab order. Trap focus inside modals and return it on close. Dropdowns respond to arrow keys + enter. Escape closes modal/dropdown. Provide a keyboard alternative for drag.
dnd-kit: enable the keyboard sensor → space to 'grab', arrows to move, space to 'drop'.
Always label form controls
Placeholder disappears and doesn't replace the label. Without a label, a screen reader just says 'text field' and the user has no idea what to type.
<label for="id">...</label> + <input id="id">, or wrap input in label. For icon buttons use aria-label. For groups (e.g. radios) use <fieldset>/<legend>. Mark required with aria-required.
<label for="email">Email</label><input id="email" type="email" required aria-required="true">
Use ARIA only when necessary — prefer native HTML
'No ARIA is better than bad ARIA.' Wrong roles/states actively break accessibility. Most needs are already met by native HTML.
<button> beats role="button". For custom patterns (combobox, tablist, tree), follow WAI-ARIA Authoring Practices. Add aria-live="polite"/"assertive" for dynamic content.
Toast notification: <div role="status" aria-live="polite">content</div> gets announced by screen readers.
Add a skip link — so keyboard users can bypass the nav
Tabbing through the same 20–50 menu items on every page is exhausting. A skip link jumps straight to main content.
The first focusable element on the page should be a 'Skip to content' link. Hidden by default, visible on focus.
<a class="visually-hidden-focusable" href="#main">Skip to content</a> + <main id="main">…</main>
Don't rely on color alone — add a secondary signal
Color-blind users can't distinguish red/green. If state is conveyed only by color, those users get no information.
Add an icon + text next to error color. Mark required fields with (*), not only a red border. In charts, add pattern/shape variations.
Form: red border + ⚠ icon + 'Invalid email' text. Chart: distinguish lines with dash/dot patterns, not only color.
Respect reduced-motion preference
Aggressive parallax/animation can trigger vertigo for users with vestibular sensitivity. If you ignore OS-level 'reduce motion', they literally can't stay on the site.
Use the prefers-reduced-motion: reduce media query to shorten or disable animations. Require opt-in for auto-playing video.
@media (prefers-reduced-motion: reduce) { * { animation-duration: .01ms !important; transition-duration: .01ms !important; } } Declare page and section language
Screen readers need the language to pronounce correctly. Wrong lang → Turkish text read with English pronunciation = gibberish.
<html lang="en"> or dynamic to reflect the user's language. Mark a foreign-language quote via span lang="tr".
In Duck's Layout: <html lang="@L.CurrentCode"> → correct voice when switching TR/EN.
Add captions and transcripts to media
Captions are essential for quiet environments and hearing-impaired viewers. Transcripts help search engines and skimming readers.
Provide WebVTT subtitles for video (<track kind="captions">). Publish a text transcript for podcasts. Auto-generation (Whisper) is acceptable but proofread.
<video><track kind="captions" src="subs.en.vtt" srclang="en" label="English" default></video>