HTML Spec & DOCTYPE
The HTML specification defines the rules, elements, attributes, and behaviors that make up the web's core language. Understanding the specification history, doctype evolution, and the differences between standards bodies is essential for writing valid, forward-compatible HTML.
The doctype declaration is the first thing in every HTML document. It tells the browser which version of HTML to expect and triggers the correct rendering mode. Despite its importance, many developers treat it as a magic incantation without understanding what it does.
info
HTML has evolved through several major versions since its creation by Tim Berners-Lee in 1991. Each version introduced new elements, attributes, and capabilities while removing deprecated features.
| Version | Year | Key Features | Status |
|---|---|---|---|
| HTML 2.0 | 1995 | First standardized version. Forms, tables (basic), images with alt text | Historic |
| HTML 3.2 | 1997 | Tables, applets, text flow around images, superscript/subscript | Historic |
| HTML 4.01 | 1999 | Separation of structure and presentation (CSS), internationalization, accessibility | Legacy |
| XHTML 1.0 | 2000 | HTML reformulated as XML. Stricter parsing rules, lowercase tags, required closing | Legacy |
| XHTML 1.1 | 2001 | Modular XHTML. Removed presentation elements entirely | Legacy |
| HTML5 | 2014 | Semantic elements (<header>, <nav>, <section>), multimedia (<video>, <audio>), canvas, form types, APIs | Current |
| HTML Living Standard | Ongoing | Continuously updated. No version numbers. WHATWG maintains. W3C adopted in 2019 | Active |
The transition from HTML4 to HTML5 was a major shift. HTML4 was based on SGML (Standard Generalized Markup Language), which required complex doctype declarations. HTML5 simplified the doctype to its current minimal form and abandoned SGML compatibility.
note
The doctype declaration has evolved from verbose SGML declarations to the simple <!DOCTYPE html> used today. Each variation tells the browser how to interpret the document and which rendering mode to use.
| 1 | <!-- HTML5 (modern, always use this) --> |
| 2 | <!DOCTYPE html> |
| 3 | |
| 4 | <!-- HTML 4.01 Strict --> |
| 5 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" |
| 6 | "http://www.w3.org/TR/html4/strict.dtd"> |
| 7 | |
| 8 | <!-- HTML 4.01 Transitional (includes presentational elements) --> |
| 9 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" |
| 10 | "http://www.w3.org/TR/html4/loose.dtd"> |
| 11 | |
| 12 | <!-- HTML 4.01 Frameset (for frameset documents) --> |
| 13 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN" |
| 14 | "http://www.w3.org/TR/html4/frameset.dtd"> |
| 15 | |
| 16 | <!-- XHTML 1.0 Strict --> |
| 17 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
| 18 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> |
| 19 | |
| 20 | <!-- XHTML 1.0 Transitional --> |
| 21 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" |
| 22 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> |
| 23 | |
| 24 | <!-- XHTML 1.0 Frameset --> |
| 25 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN" |
| 26 | "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd"> |
| 27 | |
| 28 | <!-- XHTML 1.1 --> |
| 29 | <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" |
| 30 | "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"> |
| 31 | |
| 32 | <!-- Legacy doctypes (triggers almost-standards mode in some browsers) --> |
| 33 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN"> |
| 34 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> |
best practice
Browsers use three rendering modes depending on the doctype (or lack thereof). Understanding these modes is critical because they dramatically affect how CSS is applied and how elements are rendered.
| Mode | Trigger | Behavior |
|---|---|---|
| Standards Mode | HTML5 doctype, HTML4 Strict, XHTML doctypes | Full standards-compliance. CSS box model per spec, consistent layout behavior |
| Quirks Mode | No doctype, or old/unrecognized doctype | Emulates IE5 box model, non-standard sizing, buggy behavior. Avoid at all costs |
| Almost Standards Mode | HTML4 Transitional with URI, HTML4 Frameset | Mostly standards-compliant but with minor quirks in table cell sizing |
The most visible difference between quirks mode and standards mode is the box model. In quirks mode, the CSS width property includes padding and borders (the IE5 box model). In standards mode, width applies only to the content area.
| 1 | <!-- Triggers standards mode in all browsers --> |
| 2 | <!DOCTYPE html> |
| 3 | <html lang="en"> |
| 4 | <head> |
| 5 | <meta charset="UTF-8"> |
| 6 | <title>Standards Mode</title> |
| 7 | <style> |
| 8 | .box { |
| 9 | width: 200px; |
| 10 | padding: 20px; |
| 11 | border: 5px solid black; |
| 12 | /* In standards mode: total width = 200 + 40 + 10 = 250px */ |
| 13 | /* In quirks mode: content width = 200 - 40 - 10 = 150px */ |
| 14 | } |
| 15 | </style> |
| 16 | </head> |
| 17 | <body> |
| 18 | <div class="box">Standards Mode</div> |
| 19 | </body> |
| 20 | </html> |
| 21 | |
| 22 | <!-- No doctype → triggers quirks mode --> |
| 23 | <html> |
| 24 | <head> |
| 25 | <title>Quirks Mode</title> |
| 26 | <!-- Same CSS behaves differently --> |
| 27 | </head> |
| 28 | <body> |
| 29 | <div class="box">Quirks Mode</div> |
| 30 | </body> |
| 31 | </html> |
warning
You can check which mode a browser is using from the JavaScript console:
| 1 | // Check rendering mode |
| 2 | console.log(document.compatMode); |
| 3 | // "CSS1Compat" → Standards mode |
| 4 | // "BackCompat" → Quirks mode |
The W3C Markup Validation Service is the official tool for checking HTML documents against the specification. Validating your HTML catches structural errors, deprecated elements, and accessibility issues before they reach users.
| 1 | # Validate from command line with vnu.jar |
| 2 | java -jar vnu.jar index.html |
| 3 | |
| 4 | # Validate with HTML-validate (npm) |
| 5 | npx html-validate src/**/*.html |
| 6 | |
| 7 | # Validate with W3C validator API |
| 8 | curl -H "Content-Type: text/html; charset=utf-8" \ |
| 9 | --data-binary @index.html \ |
| 10 | https://validator.w3.org/nu/?out=json |
| 11 | |
| 12 | # Integrate into CI pipeline |
| 13 | # .github/workflows/validate.yml |
| 14 | name: HTML Validate |
| 15 | on: [push] |
| 16 | jobs: |
| 17 | validate: |
| 18 | runs-on: ubuntu-latest |
| 19 | steps: |
| 20 | - uses: actions/checkout@v4 |
| 21 | - run: npx html-validate "src/**/*.html" |
pro tip
The Web Hypertext Application Technology Working Group (WHATWG) was formed in 2004 by Apple, Mozilla, and Opera in response to the W3C's decision to abandon HTML in favor of XHTML. The WHATWG developed what would become HTML5, and since 2019, the W3C has officially adopted the WHATWG Living Standard as the sole source of truth for HTML.
| Characteristic | WHATWG | W3C |
|---|---|---|
| Model | Living Standard (continuously updated) | Versioned snapshots (historically) |
| URL | html.spec.whatwg.org | w3.org/TR/html |
| Status | Active, authoritative standard | Adopted WHATWG Living Standard in 2019 |
| Versioning | No version numbers | Versioned (HTML 4.01, HTML5, HTML 5.1, 5.2, 5.3) |
| Process | Consensus-driven, browser vendors | Member organization, formal recommendations |
In practice, the two organizations now agree: the HTML Living Standard is the specification that browser vendors implement. The W3C's role is to publish review drafts and coordinate with other web standards (CSS, SVG, ARIA). For developers, html.spec.whatwg.org is the definitive reference.
note
While the HTML specification defines how documents should be parsed and rendered, browser implementations have historically differed. Modern browsers converge on the standard, but edge cases still exist. Understanding parser behavior helps you write HTML that works consistently.
| 1 | <!-- Parser behavior examples --> |
| 2 | |
| 3 | <!-- Implicit tag closure (browser infers </p>) --> |
| 4 | <p>Paragraph 1 |
| 5 | <p>Paragraph 2 |
| 6 | <!-- Parsed as: <p>Paragraph 1</p><p>Paragraph 2</p> --> |
| 7 | |
| 8 | <!-- Misnested tags (browser reconciles) --> |
| 9 | <b><i>Bold and italic</b></i> |
| 10 | <!-- Parsed as: <b><i>Bold and italic</i></b><i></i> --> |
| 11 | |
| 12 | <!-- Table inside paragraph (paragraph auto-closes) --> |
| 13 | <p>Text before <table><tr><td>Cell</td></tr></table></p> |
| 14 | <!-- Parsed as: <p>Text before</p><table>...</table><p></p> --> |
| 15 | |
| 16 | <!-- Script with </script> in content (breaks) --> |
| 17 | <script> |
| 18 | var x = "</script>"; // Syntax error! Closes the script tag |
| 19 | </script> |
| 20 | <!-- Fix: escape the slash --> |
| 21 | <script> |
| 22 | var x = "</script>"; |
| 23 | </script> |
| 24 | |
| 25 | <!-- Void elements don't need closing --> |
| 26 | <br> |
| 27 | <br /> |
| 28 | <img src="photo.jpg" alt="Photo"> |
| 29 | <img src="photo.jpg" alt="Photo" /> |
| 30 | |
| 31 | <!-- Foreign content (SVG, MathML) maintains namespace --> |
| 32 | <svg viewBox="0 0 100 100"> |
| 33 | <circle cx="50" cy="50" r="40" /> |
| 34 | </svg> |
| 35 | |
| 36 | <!-- Custom elements must have a hyphen --> |
| 37 | <my-component></my-component> |
| 38 | <my-element></my-element> |
best practice
Several HTML elements have been deprecated or obsolete across different versions of the specification. These elements should not be used in new code and should be replaced with modern CSS equivalents.
| Element | Status | Replacement |
|---|---|---|
| <center> | Obsolete | CSS text-align: center or margin: 0 auto |
| <font> | Obsolete | CSS font-family, font-size, color |
| <marquee> | Obsolete | CSS animations (prefers-reduced-motion) |
| <frame> | Obsolete | <iframe> or CSS layout (Flexbox, Grid) |
| <frameset> | Obsolete | CSS Grid or Flexbox |
| <noframes> | Obsolete | Fallback content in <iframe> |
| <big> | Obsolete | CSS font-size: larger |
| <strike> | Obsolete | <s>, <del>, or CSS text-decoration: line-through |
| <tt> | Obsolete | <code>, <kbd>, <samp>, <pre>, or CSS font-family: monospace |
| <acronym> | Obsolete | <abbr> |
| <applet> | Obsolete | <object>, <embed>, or modern JavaScript |
| <bgsound> | Obsolete | <audio> with autoplay |
| 1 | <!-- Deprecated — don't use these --> |
| 2 | <center>Centered text</center> |
| 3 | <font color="red" size="4">Red text</font> |
| 4 | <marquee behavior="scroll">Scrolling text</marquee> |
| 5 | <big>Big text</big> |
| 6 | <strike>Strikethrough text</strike> |
| 7 | <tt>Monospace text</tt> |
| 8 | <acronym title="HyperText Markup Language">HTML</acronym> |
| 9 | |
| 10 | <!-- Modern equivalents --> |
| 11 | <p style="text-align: center;">Centered text</p> |
| 12 | <span style="color: red; font-size: 1.25rem;">Red text</span> |
| 13 | <p style="font-size: larger;">Big text</p> |
| 14 | <s>Strikethrough text</s> |
| 15 | <code>Monospace text</code> |
| 16 | <abbr title="HyperText Markup Language">HTML</abbr> |
| 17 | |
| 18 | <!-- Frames (obsolete — replaced by iframes and CSS) --> |
| 19 | <!-- Instead of <frameset>, use: --> |
| 20 | <iframe src="navigation.html" title="Navigation"></iframe> |
| 21 | <iframe src="content.html" title="Content"></iframe> |
| 22 | |
| 23 | <!-- Marquee replacement with CSS --> |
| 24 | <style> |
| 25 | .scroll-text { |
| 26 | animation: scroll 10s linear infinite; |
| 27 | } |
| 28 | @media (prefers-reduced-motion: reduce) { |
| 29 | .scroll-text { |
| 30 | animation: none; |
| 31 | } |
| 32 | } |
| 33 | @keyframes scroll { |
| 34 | from { transform: translateX(100%); } |
| 35 | to { transform: translateX(-100%); } |
| 36 | } |
| 37 | </style> |
| 38 | <div class="scroll-text">Scrolling text (accessible)</div> |
warning
The HTML Living Standard is the modern approach to web standards. Instead of releasing versioned specifications every few years, the WHATWG maintains a continuously updated standard that reflects the latest browser implementations and web developer needs.
| 1 | <!-- Recent additions to the HTML Living Standard --> |
| 2 | |
| 3 | <!-- Lazy loading (added 2019) --> |
| 4 | <img src="photo.jpg" loading="lazy" alt="Photo"> |
| 5 | |
| 6 | <!-- Decoding hint (added 2019) --> |
| 7 | <img src="photo.jpg" decoding="async" alt="Photo"> |
| 8 | |
| 9 | <!-- Fetch priority (added 2022) --> |
| 10 | <img src="hero.jpg" fetchpriority="high" alt="Hero"> |
| 11 | |
| 12 | <!-- Invoker commands (proposed) --> |
| 13 | <button command="show-modal" commandfor="myDialog">Open</button> |
| 14 | <dialog id="myDialog"> |
| 15 | <p>Modal content</p> |
| 16 | <button command="close" commandfor="myDialog">Close</button> |
| 17 | </dialog> |
| 18 | |
| 19 | <!-- Popover API (added 2024) --> |
| 20 | <button popovertarget="my-popover">Toggle Popover</button> |
| 21 | <div id="my-popover" popover> |
| 22 | <p>Popover content</p> |
| 23 | </div> |
| 24 | |
| 25 | <!-- Declarative Shadow DOM (added 2023) --> |
| 26 | <template shadowrootmode="open"> |
| 27 | <style> |
| 28 | :host { display: block; color: #00FF41; } |
| 29 | </style> |
| 30 | <p>Shadow DOM content</p> |
| 31 | </template> |
| 32 | |
| 33 | <!-- Search element (added 2023) --> |
| 34 | <search> |
| 35 | <form action="/search"> |
| 36 | <input type="search" name="q"> |
| 37 | <button type="submit">Search</button> |
| 38 | </form> |
| 39 | </search> |
pro tip