HTML guidelines
Semantics, UX & Accessibility
Core principle: Use the right HTML element for the job. Every native element ships with free keyboard support, screen reader compatibility, and browser UX that no custom component can fully replicate.
Native elements first
Frameworks make it easy to replace native elements with custom ones. Resist that temptation.
| Instead of | Use |
|---|---|
<div onclick="..."> | <button> or <a href="..."> |
<div class="heading"> | <h1>–<h6> → see Headings |
<div class="list"> | <ul>, <ol>, <dl> → see List |
<div class="form-group"> | <fieldset> + <legend> |
Links vs. buttons
-
<a href="...">→ navigation to a URL -
<button>→ action without page navigation
A <span onclick> won’t appear in the screen reader link list, won’t receive focus via Tab, and won’t show a browser context menu. ARIA can’t fix that.
❌ <span onclick="navigate()">Learn more</span>
✅ <a href="/learn-more">Learn more</a>
Forms: let the browser help
Use the right input type — the browser validates and adapts the mobile keyboard automatically.
<label for="email">Email</label>
<input type="email" id="email" required>
<label for="phone">Phone</label> <input type="tel" id="phone" required>
Always link <label> to its <input> via for/id or by nesting. An unlinked label is invisible to screen readers and reduces the tap target size. Use required, min, max, and pattern before reaching for JS validation.
See also: Input, Checkbox, Select.
Heading hierarchy
One <h1> per page. Avoid skipping levels. → Use CSS for visual size, not the heading level.
Screen readers and search engines navigate by heading structure. A broken hierarchy breaks that navigation. → Headings
Focus & keyboard
All interactive elements must be reachable and operable by keyboard (WCAG 2.1 AA). → Accessibility Guidelines
- Never remove focus styles globally (outline: none). Override them with visible custom styles instead.
- Don’t hide focusable elements with tabindex=“-1” without a clear reason.
- Custom interactive components require role, tabindex, and keyboard handlers — significant effort that’s usually avoidable by using native elements.
Images
<!-- Informative image: -->
<img src="chart.png" alt="Revenue Q1–Q4 2024">
<!-- Decorative image: empty alt, never omitted -->
<img src="divider.png" alt="">
A missing alt attribute is not neutral — screen readers will read out the filename.
ARIA: supplement, don’t replace
“No ARIA is better than bad ARIA.” — W3C
Only use ARIA when no native element covers the use case. role="button" on a <div> does not give you keyboard behavior. Always add aria-label to icon-only interactive elements:
<!-- ❌ role="button" on a div — no keyboard support, ARIA doing unnecessary work -->
<div role="button" onclick="closeMenu()">
<svg>...</svg>
</div>
<!-- ✅ Native button + aria-label to describe the icon's purpose -->
<button aria-label="Close menu">
<svg aria-hidden="true">...</svg>
</button>
HTML-first before JavaScript
| Need | Native solution |
|---|---|
| Accordion | <details> + <summary> |
| Popup / overlay | popover API |
| Modal dialog | <dialog> |
| Progress indicator | <progress> |
| Responsive images | <picture> with srcset |
Pre-merge checklist
Further reading
HTML guidelines skill
Load the Markdown file as knowledge into your projects and prompts, whenever you create a DEHN UI.