Clean HTML: Structure, Naming and Maintainability

Markup you can still read in six months. Consistent conventions, meaningful names, shallow nesting and a house style that survives more than one developer.

Concept

Markup is read far more often than it is written. Most of the reading is done by someone who did not write it - including you, six months later, with no memory of why anything is the way it is.

Clean HTML is not an aesthetic preference. It is the difference between a change taking ten minutes and taking an afternoon.

Consistency first

Almost every convention below is arguable. Being consistent about whichever one you pick matters more than the choice itself.

<!-- pick one and hold to it -->
<img src="a.jpg" alt="A map">      <!-- HTML style -->
<img src="a.jpg" alt="A map" />    <!-- XHTML style, ignored by the parser -->

<!-- one quote style -->
class="card"
class='card'

<!-- one attribute order -->
<a class="btn" href="/x" data-id="1">
<a href="/x" class="btn" data-id="1">

A formatter running on save settles most of this without anyone thinking about it, which is why it is worth setting up on day one.

Indentation

<!-- structure invisible -->
<article class="card"><h3>Design</h3><p>Three years</p><a href="/x">More</a></article>

<!-- structure visible from ten feet away -->
<article class="card">
  <h3>Design</h3>
  <p>Three years</p>
  <a href="/x">More</a>
</article>

One level of indentation per level of nesting. Two spaces is the common default and four is equally fine; what matters is that the file uses one of them throughout.

Break long attribute lists across lines:

<img src="/images/studio-800.jpg"
     srcset="/images/studio-400.jpg 400w, /images/studio-800.jpg 800w"
     sizes="(max-width: 50rem) 100vw, 50vw"
     alt="Students working at drawing boards"
     width="1600" height="900"
     loading="lazy">

Name by meaning

<!-- names the appearance: a lie waiting to happen -->
<p class="red-text small-font left-align">Out of stock</p>

<!-- names the meaning: still true after a redesign -->
<p class="stock-status stock-status--out">Out of stock</p>

The day the design changes to orange, class="red-text" becomes a permanent piece of misinformation in the codebase.

A naming convention

<article class="course-card course-card--featured">
  <h3 class="course-card__title">Design</h3>
  <p class="course-card__summary">Three years, studio based.</p>
  <a class="course-card__link" href="/courses/design">Read more</a>
</article>

Block, element, modifier. Verbose, and it makes every selector a flat single class with identical specificity, which removes nesting battles entirely. Any convention works; having one is the point.

State classes

<div class="menu is-open">
<button class="btn is-loading">
<li class="tab is-active">

An is- prefix marks classes that scripts toggle, so it is obvious which are dynamic. A data- attribute is an alternative when the states are mutually exclusive.

Keep nesting shallow

<!-- seven levels before any content -->
<div class="wrapper">
  <div class="container">
    <div class="row">
      <div class="col">
        <div class="card">
          <div class="card-inner">
            <div class="card-body">
              <h3>Design</h3>

<!-- two -->
<div class="grid">
  <article class="card">
    <h3>Design</h3>

CSS grid and flexbox removed most of the reason for wrapper chains. Every level you delete makes the file shorter, the selectors simpler and the structure clearer.

Comment the why

<!-- pointless: the element already says this -->
<!-- header -->
<header>...</header>

<!-- useful: records something the markup cannot express -->
<!-- Column order matters: the middle column is styled as the highlighted
     plan in pricing.css. Do not reorder without updating that file. -->
<table class="pricing">...</table>

<!-- useful in a deeply nested file -->
</div><!-- /site-header -->

One idea per file or partial

Split templates by responsibility rather than by page:

templates/
  layout.html            doctype, head, header, footer
  partials/
    site-header.html
    site-footer.html
    course-card.html
    breadcrumb.html
  pages/
    courses.html
    course-detail.html

A component defined once and included everywhere means one place to fix an accessibility problem, not fourteen.

Write in reading order

<!-- main content first, whatever the visual layout -->
<main>...</main>
<aside>...</aside>

Source order is keyboard order, screen reader order and mobile stacking order. Write the markup in the order it should be read and move it visually with CSS.

A house style worth adopting

  • Lower case element and attribute names.
  • Double quoted attribute values, always.
  • Two space indentation, one level per nesting level.
  • Attribute order: identity, then behaviour, then data and ARIA.
  • Write closing tags even where they are optional.
  • Void elements without a trailing slash.
  • Class names by meaning, in one convention.
  • One h1, no skipped heading levels.
  • Semantic elements before div.
  • Comment constraints, not descriptions.

Write it down in the repository. A convention nobody can find is not a convention.

Signs a file needs attention

  • More than about four wrapper levels with no content between them.
  • Class names that match element names: class="header", class="list-item".
  • Inline style attributes scattered through the markup.
  • The same block copied in three places.
  • Comments describing what an element obviously is.
  • A file over about five hundred lines with no partials.
  • Any div with a click handler.

Important rules

  • Consistency beats any individual convention.
  • Class names should survive a redesign.
  • Source order is reading order.
  • A formatter enforces style; a linter enforces correctness.
  • Every deleted wrapper is a small permanent gain.

Common mistakes

  • A different style in every file.
  • Appearance based class names.
  • Wrapper chains inherited from an older layout approach.
  • Copying a block rather than extracting a partial.
  • Comments that restate the markup.
  • Conventions agreed verbally and never written down.

Best practices

  • Set up a formatter and a linter before writing the second file.
  • Commit the configuration so everyone shares it.
  • Name by meaning, in one convention, written down.
  • Extract a partial the second time you copy something.
  • Delete wrappers that only exist for an older layout method.
  • Review markup in pull requests the way you review code.

Practice

  1. Take a file you wrote a while ago and count the wrapper levels before the first content element.
  2. List every class name in a project and mark those that name an appearance.
  3. Find a block duplicated three times and extract it into a partial.
  4. Write a one page house style for a project and apply it to two files.

Useful resources

Hand picked references for this topic
Written by Lorens Mishra

Default administrator account created by the installer.

Continue reading

All HTML notes →
HTML

Project Exams

Five larger assessments where the deliverable is a working site. Requirements, constraints and a rubric for each.

Read more

Discussion

0 comments
Sign in to join the discussion.

No comments yet. Be the first to say something.