01 — Foundation

Why HTML matters

Weak markup creates expensive problems early — usually before anyone files a bug.

Most frontend problems do not start in a framework or build pipeline. They start in the HTML. Bad structure makes accessibility harder, forces JavaScript to replace native behaviour, spreads CSS workarounds, and slows pages down.

These standards describe markup teams can maintain: semantic, accessible, and built for real delivery — not textbook perfection. See code formatting and style for how examples should be laid out.

02 — Semantics

Semantic first

Choose elements for what they are, not what you want them to look like.

A control that submits data is a button. A control that navigates is a link. A section title is a heading. Grouped fields belong in a fieldset. Tabular data belongs in a table.

The page should still make sense with stylesheets and scripts turned off. If structure only works once CSS and JavaScript are in place, the structure is probably wrong.

03 — Controls

Actions stay on the page. Navigation goes somewhere.

Use a button when the user triggers an action on the current page. Use a link when the user moves to a URL — another page, a download, or a real in-page destination.

Use a button for

  • opening a modal or drawer
  • submitting a form
  • toggling visibility or state
  • filtering or sorting in place
  • any action handled by JavaScript on the current page

Use a link for

  • navigation between pages or routes
  • downloadable files
  • jumping to a named section via href="#section"
  • any control whose primary job is “go here”

Bad example

<a href="#" onclick="submitForm()">
    Continue
</a>

This looks like navigation but runs script instead. Keyboard and screen-reader users get the wrong affordance, and you lose native submit behaviour including Enter-to-submit in forms.

Good example

<button type="submit">
    Continue
</button>

The control does what users and assistive technology expect. Native behaviour comes for free.

04 — Structure

Headings and document outline

Headings define structure for everyone — not just visual size.

Screen readers use headings to navigate. Search engines use them to understand content. Developers use them to scan a page. Treat levels as structure, not typography shortcuts.

Rules

  • One h1 per page, describing the page topic
  • Follow a logical order: h2 for major sections, h3 for subsections
  • Do not skip levels to make text bigger — adjust type in CSS instead
  • Do not use headings only for styling; use classes or utilities for that

Bad example

<h1>Page title</h1>
<h4>Section title</h4>

The jump from h1 to h4 breaks the outline. Fix the font size in CSS; keep the heading level honest.

05 — Forms

Forms, labels, and groups

A well-built form feels straightforward. A bad one fails immediately.

Good forms have visible labels, sensible tab order, clear instructions, and errors that connect to the right fields. Placeholders are hints — not replacements for labels.

What to include

  • Visible label elements tied to inputs with for / id
  • fieldset and legend for related groups (address, payment, radio sets)
  • The right type and inputmode where they help keyboards and validation
  • Instructions before users make mistakes, not only after
  • Errors linked to fields and announced to assistive technology
  • Tab order that follows the visual layout

What to avoid

Do not use placeholders as labels. They disappear when users type, often fail contrast checks, and are easy to miss. Show example text in a hint if you need it — keep the label visible.

Good example

<label for="email">
    Email address
</label>

<input
    id="email"
    name="email"
    type="email"
    autocomplete="email"
>

Fieldsets and legends

Group related controls with fieldset and describe the group with legend. This matters for radio buttons, checkbox groups, and multi-part sections such as billing or delivery — screen-reader users get context they would otherwise have to infer.

06 — Data

Tables

Tables are for tabular data. Layout belongs in CSS.

Use tables for comparisons, schedules, order lines, and report figures. Do not use them for page layout, alignment hacks, or “it was faster in the short term.”

Good uses

  • pricing or feature comparisons
  • reports and dashboards with rows and columns
  • schedules and timetables
  • order summaries and structured records

When you use a table

  • Use th for headers and scope where it clarifies relationships
  • Keep the structure readable in source — not only after styling
  • Plan for small screens: horizontal scroll or an alternative presentation, not crushed columns
  • Do not rebuild table semantics with nested div grids

07 — Media

Images

Every image should earn its place.

If an image does not help someone understand the content or complete a task, question whether it should be there at all.

Alt text

Describe what matters in the image — briefly and in context. Skip filler like “image of” or “picture showing”; they add noise without information.

Decorative images

When an image is purely decorative, use an empty altattribute so assistive technology skips it:

<img src="divider.svg" alt="">

That is the correct pattern, not an omission. Meaningful images still need meaningful descriptions.

Performance

At markup time, set up images so the browser can load them efficiently:

  • export or serve files close to their displayed size — compression matters more than multiple descriptors in markup
  • width and height to reduce layout shift
  • loading="lazy" for below-the-fold media where appropriate
  • fetchpriority for critical hero images when needed

Prefer a single sensible src when one well-sized asset scales cleanly in the layout. Long srcset and hand-writtensizes strings add HTML weight; the file-size win is often negligible unless the image is shown at very different sizes or needs a different crop on small screens.

Use picture when art direction changes (for example a portrait crop on mobile and a landscape crop on desktop). Usesrcset when multiple width variants genuinely save meaningful bytes. With lazy-loaded images, sizes="auto" can let the browser pick a width without guessing media queries — check support before relying on it everywhere.

Accessibility and performance overlap here more than people expect.

08 — Accessibility

ARIA and skip links

Use the platform first. Add ARIA only when native HTML is not enough.

No ARIA is better than bad ARIA. Native HTML already exposes roles, states, and names for most UI. Reach for ARIA when filling a gap the platform cannot cover — custom widgets, live regions, complex relationships — not to duplicate what a button or nav already provides.

Common ARIA mistakes

  • adding role on elements that already have the right semantics
  • duplicate or conflicting labels and descriptions
  • ARIA attributes with no relationship to real focus or state
  • “fixing” accessibility in markup while leaving keyboard support broken

Skip links

A skip link lets keyboard and screen-reader users jump past repeated navigation straight to the main content. It is a single anchor, usually hidden until focused, at the top of the page. This site includes one; yours should too.

09 — Delivery

Progressive enhancement

Markup is the contract with users and browsers.

Before you ship CSS or JavaScript, ask whether the page still works: Can someone read the content? Submit the form? Follow the links? If the answer is no, fix the HTML — do not patch around it in script.

JavaScript should improve the experience, not be the only reason the experience exists.

10 — Review

Before you approve

A short checklist for code review.

  • the right semantic element for each control and region
  • buttons for actions, links for navigation
  • a logical heading hierarchy without skipped levels
  • visible labels on every form field; placeholders are not labels
  • fieldsets and legends on grouped inputs where needed
  • tables used only for tabular data, with proper headers
  • accurate alt text, or alt="" when decorative
  • ARIA only where native HTML is insufficient
  • a skip link to main content
  • core tasks achievable without CSS and JavaScript

When in doubt, ask: Is this the simplest HTML that correctly describes what this is and lets people use it? That question catches most expensive mistakes before they ship.

Common questions

What is the first rule for good HTML?

Use semantic elements for their intended purpose — buttons for actions, links for navigation, headings in order — before reaching for ARIA or custom widgets.

When should you use ARIA?

When native HTML cannot express the behaviour or state you need. Prefer fixing the markup first; ARIA should supplement semantics, not replace them.

Should forms use native validation only?

Use native constraints where they fit, but always provide clear labels, error text, and a recoverable path. Server-side validation remains authoritative.