HTML Best Practices: Writing Markup You Won't Regret Later

A practical guide to writing clean, accessible, maintainable HTML. Semantic structure, proper nesting, accessibility, validation, and the small habits that separate sloppy markup from professional code.

Published August 9, 202611 min readBy ACY Partner Indonesia
HTML best practices — clean, semantic, accessible markup
300 × 250Ad Space AvailablePlace your ad here

You can write HTML that works without knowing any of this. The browser is forgiving — forget a closing tag, skip the alt on an image, wrap everything in <div>s, and the page still shows up. So why bother with “best practices” at all?

Because working and good are two different things. Good HTML is easier to read, easier to style, easier to maintain six months from now, accessible to people using screen readers, and friendlier to search engines. None of that shows up in the browser window on day one — but all of it shows up the moment someone (often future you) has to touch the code again.

This article pulls together everything from the HTML section into a set of habits. Think of it less as new syntax and more as the difference between markup that just renders and markup a professional would be happy to ship.

Write semantic HTML, not div soup

This is the single most important habit, so it goes first. Use the element that describes what the content is, not just one that happens to look right.

It’s tempting to build everything out of <div> and <span> because they’re neutral and you can style them however you like. But a page made entirely of <div>s — what people call “div soup” — throws away all the meaning baked into HTML. Compare these two versions of the same page header:

<!-- Div soup: works, but means nothing -->
<div class="header">
  <div class="logo">ACY Partner Indonesia</div>
  <div class="nav">
    <div class="link">Home</div>
    <div class="link">About</div>
  </div>
</div>

<!-- Semantic: same look, real meaning -->
<header>
  <h1>ACY Partner Indonesia</h1>
  <nav>
    <a href="/">Home</a>
    <a href="/about">About</a>
  </nav>
</header>

Both can be styled to look identical. But the second version tells the browser, screen readers, and search engines that this is a page header containing the main heading and a navigation block of real links. The first version says nothing — it’s just a stack of generic boxes.

Reach for <header>, <nav>, <main>, <article>, <section>, <aside>, and <footer> to describe the regions of your page, and use <button>, <a>, <h1><h6>, <p>, <ul>, and the rest for their intended jobs. Only fall back to <div> and <span> when there genuinely is no meaningful element for the grouping you need.

A quick gut-check

Before you type <div>, ask: “Is there an element that already describes this?” A navigation menu is a <nav>. A self-contained blog post is an <article>. A clickable thing that does something on the page is a <button>, not a <div onclick>. If after honestly asking you still can’t find a fitting element, then <div> is the right call — that’s exactly what it’s for.

If semantic elements are still fuzzy, the dedicated semantic HTML article walks through each one with examples.

Use one h1, and don’t skip heading levels

Headings (<h1> through <h6>) aren’t just “big text” — they form the outline of your page, the table of contents that screen readers and search engines rely on to understand structure. So they need to be used in order, not picked for size.

Two rules cover almost everything:

  • One <h1> per page, describing what the whole page is about.
  • Don’t skip levels going down. An <h2> can be followed by an <h3>, but jumping straight from <h2> to <h4> because <h4> “looks the right size” breaks the outline.
<h1>Learning HTML</h1>
  <h2>The Basics</h2>
    <h3>Elements and Tags</h3>
    <h3>Attributes</h3>
  <h2>Going Further</h2>
    <h3>Forms</h3>

If a heading is the wrong size for your design, fix that in CSS — never reach for the “wrong” heading level just to get a certain font size. The level communicates importance and nesting; the size is a styling decision that lives somewhere else entirely.

Always give images meaningful alt text

Every <img> should have an alt attribute. It’s the text a screen reader announces, and what shows up if the image fails to load. Good alt text describes the content or purpose of the image, not the fact that it’s an image:

<!-- Bad -->
<img src="team.jpg" alt="image" />
<img src="team.jpg" alt="team.jpg" />

<!-- Good -->
<img src="team.jpg" alt="The ACY Partner Indonesia team at the 2026 launch event" />

There’s one important exception: purely decorative images — a divider line, a background flourish, an icon that sits next to text already saying the same thing — should have an empty alt="". That deliberate empty value tells assistive tech “skip this, it adds nothing,” which is exactly right. Leaving alt off entirely is different and worse: some screen readers will then read out the file name, which is useless.

Empty alt is intentional, missing alt is a bug

alt="" and no alt at all are not the same thing. alt="" says “this image is decorative, ignore it” — a real, valid choice. A missing alt is just an oversight, and tools that check accessibility will flag it. So decorative images get alt="", meaningful images get a real description, and no image goes without the attribute.

The HTML images in depth article covers responsive images and formats if you want to go further.

Label your form controls properly

Forms are where accessibility most often falls apart. The golden rule: every input needs a label that’s programmatically connected to it. The cleanest way is a <label> with a for that matches the input’s id:

<label for="email">Email address</label>
<input type="email" id="email" name="email" />

Now clicking the label focuses the input, the tap target is bigger, and a screen reader announces “Email address, edit text” instead of just “edit text.” A placeholder is not a substitute for a label — it disappears the moment the user starts typing, and assistive tech doesn’t treat it as the field’s name.

Pick the right type, too. type="email", type="tel", type="number", and friends give mobile users the correct on-screen keyboard and let the browser help with validation. There’s a whole article on the forms and inputs if you need a refresher.

Keep your nesting valid

HTML elements have rules about what can go inside what, and breaking them leads to bugs that are maddening to track down because the browser silently “fixes” your markup in ways you didn’t intend.

A few of the most common offenders:

  • Don’t put block elements inside <p>. A <p> can only hold inline content. Putting a <div> inside a <p> makes the browser close the paragraph early, and suddenly your layout is off.
  • Don’t nest interactive elements. A <button> inside an <a>, or an <a> inside another <a>, is invalid and behaves unpredictably.
  • Close tags in the right order. Open <strong> then <em>? Close <em> first, then <strong> — innermost out.
<!-- Wrong: block element inside a paragraph -->
<p>Some text <div>more text</div></p>

<!-- Right -->
<p>Some text</p>
<div>more text</div>

If a layout is breaking in a way that makes no sense, mismatched or illegal nesting is one of the first things to suspect.

Quote your attributes and write clean values

You met this in the attributes article, but it’s worth restating as a habit:

  • Always quote attribute values. class="card" is correct. Unquoted values can break in subtle ways the moment a value contains a space.
  • Use lowercase for element and attribute names. <div class="box">, not <DIV CLASS="box">. It’s the convention everyone follows, and it’s easier to read.
  • Give classes and ids meaningful names. class="price-card" tells you what it is; class="c1" tells you nothing. Names that describe the purpose survive redesigns better than names that describe the current appearance (class="blue-box" is a trap the day you make it green).

If you want to go deeper on naming and the difference between the two, the classes and ids article covers it.

Set the basics in your document head

A few lines in the <head> quietly do a lot of work, and leaving them out causes real problems:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>A clear, descriptive page title</title>
</head>
  • <!DOCTYPE html> on the very first line keeps the browser in standards mode. Skip it and you risk “quirks mode,” where old, broken layout rules kick in.
  • lang="en" on <html> tells assistive tech and translation tools what language the page is in.
  • <meta charset="UTF-8"> makes sure characters and emoji render correctly.
  • The viewport meta tag is what makes a page actually responsive on phones — without it, mobile browsers pretend they’re a desktop and shrink everything.
  • A real, unique <title> is what shows in the browser tab and the search results. “Untitled Document” is a missed opportunity on every page.

The head and meta article goes through all of these in detail.

Separate structure from style and behavior

HTML’s job is structure. CSS handles appearance, JavaScript handles behavior, and keeping those three in their own lanes makes everything easier to maintain.

In practice that means avoiding inline styles and inline event handlers for anything beyond a quick test:

<!-- Avoid -->
<p style="color: red; font-size: 20px;" onclick="alert('hi')">Click me</p>

<!-- Prefer: structure here, style and behavior elsewhere -->
<p class="alert" id="greeting">Click me</p>

With the second version, the styling lives in a CSS file and the click behavior lives in a JavaScript file, both hooked in through the class and id. The HTML stays clean and readable, and you can restyle or rewire the page without hunting through your markup for stray style= attributes. Inline style is fine for a one-off experiment; it just shouldn’t be how you build the real thing.

Inline handlers age badly

An onclick (or onmouseover, etc.) buried in your HTML is hard to find, hard to reuse, and mixes two concerns that should stay apart. On any project bigger than a quick demo, attach behavior from a JavaScript file instead. Your future self, scanning clean markup, will thank you.

Format for humans: indent and comment

The browser doesn’t care about whitespace, but the next person to read your code does — and that person is usually you. Indent nested elements consistently so the structure is visible at a glance, and drop in the occasional comment to mark larger sections:

<main>
  <!-- Pricing section -->
  <section class="pricing">
    <h2>Our Plans</h2>
    <div class="plan">
      <h3>Starter</h3>
      <p>Free forever.</p>
    </div>
  </section>
</main>

You don’t need to comment every line — over-commenting is its own kind of noise. Comment the why and the big landmarks, let clean structure speak for the rest. A formatter can handle the indentation for you automatically; if you’d like a tidy-up tool, an HTML formatter will re-indent messy markup in one click.

Validate your HTML

Because the browser quietly patches over mistakes, broken markup can hide for a long time. A validator reads your HTML against the actual spec and tells you exactly what’s wrong — an unclosed tag, a duplicate id, an attribute that doesn’t belong on that element, a heading level that’s out of order.

Running a page through the W3C Markup Validator now and then is a great habit, especially before you ship. It catches the small structural mistakes that don’t break the page today but will bite you (or break accessibility tooling) later. Think of it as a spell-check for your markup.

A short pre-flight checklist

When a page is “done,” a quick pass against this list catches most of the common problems:

  • Is there exactly one <h1>, and do the heading levels descend in order?
  • Are you using semantic elements (<header>, <nav>, <main>, <footer>…) instead of div soup?
  • Does every image have a meaningful alt (or a deliberate alt="" if decorative)?
  • Is every form input connected to a <label>?
  • Is the <head> set up — doctype, lang, charset, viewport, a real <title>?
  • Are attribute values quoted and names sensible?
  • Is structure separate from style/behavior — no stray inline styles or onclicks?
  • Does it pass a validator with no errors?

Wrapping up

None of these practices are hard. They’re small, repeatable habits, and once they’re muscle memory you’ll write clean HTML without thinking about it. To recap the big ones:

  • Semantic first — use the element that describes the content; <div> is the last resort, not the default.
  • Headings are an outline — one <h1>, no skipped levels, size belongs to CSS.
  • Accessibility isn’t optional — real alt text, labelled inputs, a lang on <html>.
  • Keep it valid — sensible nesting, quoted attributes, a properly set-up <head>.
  • Separate the three layers — HTML for structure, CSS for style, JavaScript for behavior.
  • Format and validate — indent for humans, comment the landmarks, run a validator before you ship.

Write HTML this way and you’re not just making pages that work — you’re making pages that are accessible, maintainable, and genuinely professional. That’s the foundation everything else in front-end development is built on. Now that the markup is solid, the next layer — making it look exactly the way you want — is where CSS takes over.

Tags:htmlfrontendbest-practicesaccessibilityintermediate
728 × 90Ad Space AvailablePlace your ad here

Related Articles

See All Articles

You Might Also Like

Frontend best practices overview cover
Frontend / Fundamentals

Frontend Best Practices: An Overview

A friendly map of what good frontend really means — semantic HTML, clean CSS, unobtrusive JavaScript, responsive layouts, performance, and progressive enhancement, with pointers to go deeper.

Sep 20, 20268 min read
A Frontend Developer Roadmap cover with the path HTML to CSS to JavaScript
Frontend / Fundamentals

A Frontend Developer Roadmap: What to Learn, and in What Order

A calm, step-by-step learning path for beginners: HTML, CSS, JavaScript, developer tools, responsive and accessible design, a framework, TypeScript, and deployment, with the reasoning behind each step.

Sep 20, 202611 min read
Title card reading Styling and Interactivity over a dark blue ACY Partner background
Frontend / Fundamentals

How Styling and Interactivity Work

A beginner-friendly look at how CSS turns plain HTML into a designed layout, and how JavaScript adds behavior — the structure, style, behavior mental model for building web pages.

Sep 20, 20269 min read
Three front-end layers combining on one web page
Frontend / Fundamentals

How HTML, CSS, and JavaScript Work Together

A hands-on walkthrough for beginners: build one small button, give it structure with HTML, looks with CSS, and behavior with JavaScript, and watch the three layers cooperate on a real page.

Sep 20, 20268 min read
The Frontend Developer Toolkit cover with editor, browser, and CLI motifs
Frontend / Fundamentals

The Frontend Developer Toolkit

A friendly tour of the everyday tools a frontend developer relies on — code editor, browser and DevTools, terminal, version control, package managers, and a dev server — and what each one is actually for.

Sep 20, 202610 min read
Cover illustration for What Frontend Developers Do
Frontend / Fundamentals

What Frontend Developers Do: A Beginner's Guide to the Role

A clear, beginner-friendly look at what frontend developers actually do every day: turning designs into working interfaces, building reusable UI, and caring about responsiveness, accessibility, and speed.

Sep 20, 202610 min read