Common HTML Mistakes to Avoid
Browsers rarely refuse to render broken HTML—they quietly patch over it, which means bad habits can survive in a codebase for years without anyone noticing. This page rounds up the anti-patterns that show up most often in real projects, why each one is a problem, and the fix.
1. Missing Alt Text
missing-alt.html
<!-- Bad: no alt attribute at all --> <img src="team-photo.jpg" /> <!-- Good: describes the image's content --> <img src="team-photo.jpg" alt="The engineering team at the 2026 offsite" />
alt, screen readers announce the filename or nothing at all, and the image contributes zero information if it fails to load. Purely decorative images should still include alt=""—omitting the attribute entirely is different from an intentionally empty one.2. Missing lang Attribute
missing-lang.html
<!-- Bad --> <html> <!-- Good --> <html lang="en">
lang, screen readers guess the language (often wrongly) and can mispronounce entire pages, and browsers can't offer accurate auto-translation.3. Non-Unique IDs
duplicate-ids.html
<!-- Bad: two elements share the same id --> <div id="card">First card</div> <div id="card">Second card</div> <!-- Good: unique ids, shared styling via class --> <div id="card-1" class="card">First card</div> <div id="card-2" class="card">Second card</div>
document.getElementById, label for associations (the label binds to whichever matching ID comes first), and fragment links (#card jumps to the first match only). Use classes for shared styling, IDs only for genuinely unique references.4. Deprecated Tags
A handful of tags from early HTML are obsolete—they either never made it into HTML5 or were explicitly removed. Modern browsers may still render some of them for backward compatibility, but they should never appear in new code.
Deprecated tag | Replacement |
|---|---|
<font> | CSS font-family / font-size / color |
<center> | CSS text-align: center / margin: auto |
<marquee> | CSS animation or a JavaScript carousel |
<blink> | Removed entirely — do not use |
<big> / <small> as styling | CSS font-size (note: <small> is valid but means "side comment," not "smaller text") |
<strike> | <s> or <del> depending on intent |
deprecated-vs-modern.html
<!-- Bad: presentational tags from the 1990s --> <center><font color="red" size="5">Sale ends soon!</font></center> <!-- Good: semantic markup, styling via CSS --> <p class="banner">Sale ends soon!</p>
5. Div-Soup Instead of Semantic Elements
div-soup.html
<!-- Bad: no semantic meaning anywhere --> <div class="header"> <div class="nav">...</div> </div> <div class="main"> <div class="article">...</div> </div> <div class="footer">...</div> <!-- Good: same layout, real meaning --> <header> <nav>...</nav> </header> <main> <article>...</article> </main> <footer>...</footer>
6. Unclosed Tags
unclosed-tags.html
<!-- Bad: <p> never closed, browser guesses where it should end --> <p>First paragraph <p>Second paragraph <!-- Good --> <p>First paragraph</p> <p>Second paragraph</p>
The browser's error-recovery algorithm auto-closes many unclosed tags, but the rules are subtle and can produce a DOM structure that doesn't match what you intended—especially with nested elements.
7. Invalid Nesting
invalid-nesting.html
<!-- Bad: block-level <div> inside inline-only <p> --> <p> Some text <div>a block inside a paragraph</div> </p> <!-- Bad: <a> nested inside another <a> --> <a href="/outer"> Outer link <a href="/inner">Inner link</a> </a> <!-- Good --> <div> <p>Some text</p> <div>a separate block</div> </div>
<div> inside a <p>, it implicitly closes the paragraph early to keep the document valid—which can split content in ways you didn't expect and didn't write in your source.8. Skipping Heading Levels
skipped-headings.html
<!-- Bad: jumps from h1 to h4 for visual size, not structure --> <h1>Page Title</h1> <h4>Section</h4> <!-- Good: correct hierarchy, size controlled by CSS --> <h1>Page Title</h1> <h2 class="small-heading">Section</h2>
9. Using onclick Attributes for Interactivity
inline-handlers.html
<!-- Bad: div fakes a button, no keyboard access, inline JS handler --> <div onclick="deleteItem(3)">Delete</div> <!-- Good: real button, event listener attached in JS --> <button type="button" data-id="3" class="delete-btn">Delete</button>
10. Missing width/height on Images
missing-dimensions.html
<!-- Bad: causes a layout shift once the image loads --> <img src="banner.jpg" alt="Banner" /> <!-- Good: browser reserves the correct space up front --> <img src="banner.jpg" alt="Banner" width="1200" height="400" />
Quick Reference
Mistake | Consequence | Fix |
|---|---|---|
Missing alt | Inaccessible, meaningless on load failure | Always add alt (or alt="" for decorative) |
Missing lang | Wrong pronunciation, bad auto-translation | Add lang on <html> |
Duplicate IDs | Broken JS lookups, labels, fragment links | Make every id unique |
Deprecated tags | No modern support, poor styling control | Use CSS + semantic tags |
Div-soup | No accessibility/SEO signal | Use semantic elements |
Unclosed tags | Unpredictable auto-correction | Always close non-void tags |
Invalid nesting | Browser silently restructures your DOM | Follow content-model rules |
Skipped headings | Broken navigation for screen readers | Sequential hierarchy, style with CSS |
onclick on divs | No keyboard access, no semantics | Use real <button>/<a> elements |
Missing dimensions | Layout shift (poor CLS) | Set width/height or aspect-ratio |