WEBDEVMASTERS
Structure before styling

Semantic HTML and resilient CSS

Build a coherent document first, then let intrinsic sizing, explicit cascade rules, and content-driven breakpoints absorb real variation.

Semantic regions arranged into a browser document
A durable page starts with relationships the browser can understand before styling arrives.

Model the document, not the screenshot

A screenshot describes one viewport and one content state. HTML describes relationships: which heading names a section, which label names a control, which navigation offers site links, and which article can stand on its own. Those relationships survive zoom, a missing stylesheet, a screen reader, reader mode, and content that grows after localization.

Begin with a plain-language outline. Give the page one clear primary heading, use subsequent heading levels to represent nesting, and select landmarks such as header, nav, main, article, aside, and footer for their purpose. Do not select a heading level because its default font size looks convenient; visual size belongs to CSS.

<main>
  <article aria-labelledby="guide-title">
    <header>
      <p>Field guide</p>
      <h1 id="guide-title">Resilient page structure</h1>
    </header>
    <section aria-labelledby="principles">
      <h2 id="principles">Principles</h2>
      <p>Content remains understandable in source order.</p>
    </section>
  </article>
</main>

The source order should match reading and interaction order. CSS grid can place an aside visually beside an article, but it should not reverse a confusing DOM sequence merely to match a wide-screen composition. This decision immediately supports the accessibility workflow because assistive technology and keyboard navigation start from that same structure.

Treat the cascade as a priority model

The cascade resolves competing declarations by origin, importance, layer, specificity, scope, and source order. When a team treats it as random, it responds with stronger selectors and !important, creating a ratchet that future code must overpower. A more durable approach names the responsibility of each layer.

@layer reset, base, components, utilities;

@layer base {
  :where(h1, h2, p) { margin-block-start: 0; }
  body { color: #20231f; background: #f7f4ed; }
}

@layer components {
  .guide-card { padding: clamp(1rem, 3vw, 2rem); }
}

@layer utilities {
  .visually-hidden { position: absolute; inline-size: 1px; block-size: 1px; overflow: hidden; }
}

:where() can provide deliberately low-specificity defaults, while cascade layers state which family of rules should win. This does not mean every small site needs many layers. It means priority should be intentional and visible. If one component needs an exception, prefer a meaningful variant class or custom property over a selector that depends on five ancestors.

EntityAttributeValue to inspectFailure pattern
HeadingLevelMatches section nestingChosen for visual size
ControlNative behaviorButton, link, input, detailsClickable div with custom keyboard code
RulePriorityLayer and low specificityEscalating selectors and important flags
ComponentInline constraintContainer-aware, content-ledDevice-name breakpoint copied globally

Express constraints before coordinates

Resilient layout asks what may grow, shrink, wrap, or overflow. Fixed coordinates assume the answer. Start with normal flow, give media a maximum inline size, and use grid or flexbox when elements have a genuine two-dimensional or one-dimensional relationship. Functions such as min(), max(), clamp(), minmax(), and fit-content() let the browser negotiate available space.

.guide-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: clamp(1rem, 2vw, 1.75rem);
}

.guide-grid img {
  inline-size: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

The min(100%, 18rem) guard keeps the minimum track from overflowing a container narrower than eighteen rem. Explicit image dimensions or an aspect ratio reserve layout space, helping prevent the visual shifts discussed in the performance guide. For reusable components, a container query can respond to the component’s own space instead of the viewport.

Let forms expose their contract

A form is a data contract and an interaction. Use a visible label associated through for and id, the narrowest useful input type, and native attributes such as required, autocomplete, minlength, or inputmode. Server validation remains authoritative because client validation can be bypassed, but browser constraints give immediate feedback and useful mobile keyboards.

<form action="/subscribe" method="post">
  <label for="email">Work email</label>
  <input id="email" name="email" type="email" autocomplete="email" required>
  <p id="email-help">We use this only for release notes.</p>
  <button type="submit">Subscribe</button>
</form>

Do not use an input hint as the only label. Do not disable a submit button without explaining what is missing. When the server rejects input, preserve the submitted values, place a concise error near the field, and provide a summary for long forms. Those choices connect the document, interaction, and recovery layers.

A five-pass review framework

Review in dependency order

  1. Meaning: read the outline and link text without CSS.
  2. Source order: tab through the page and compare focus to reading order.
  3. Constraints: test narrow width, long content, zoom, and missing images.
  4. Cascade: identify which layer owns each override; remove accidental specificity.
  5. Enhancement: delay JavaScript and confirm the core content and navigation still work.

This order matters. If the document model is wrong, polishing grid tracks hides the cause. If the layout constraint is wrong, adding resize listeners creates new timing problems. Correct the lowest failing layer, rerun the review, then advance.

Sources and further reading

These references support the standards and factual claims in this guide. The decision frameworks and worked examples are original editorial synthesis.

  1. MDN: Structuring documents
  2. MDN: HTML accessibility
  3. MDN: CSS cascade
  4. MDN: Container queries

Questions practitioners ask

Is a div always wrong?

No. A div is appropriate when a grouping has no specific semantic meaning. It becomes a problem when it replaces a native element that already communicates structure or behavior.

Should breakpoints match device names?

Usually no. Add a breakpoint where the component’s content and constraints stop fitting, not where a marketing list says a tablet begins.

When should CSS use JavaScript measurements?

Only when the required behavior cannot be expressed through normal flow, grid, flexbox, intrinsic sizing, container queries, or another platform feature. Measurement scripts add timing and resize coordination.