HTML markup must be valid
On this page
HTML markup validation pass - the class of problems Vite, the HTML5 spec, and downstream tooling (RSS readers, AMP validators, social-card scrapers) complain about:
- Unclosed tags -
<div>with no matching</div>before EOF - Mismatched closing tags -
<div><span></div></span>(the</div>closes the<span>implicitly, then</span>closes nothing) - Duplicate IDs - two elements with the same
id=attribute on the same page
Browsers tolerate most of these via the HTML5 error-recovery algorithm; downstream parsers don’t.
How it works#
Uses the golang.org/x/net/html tokenizer to walk the file. Void elements (<br>, <img>, <input>, etc.) are recognized as auto-closed. Foreign content (<svg>, <math>, <script>, <style>, <template>) is skipped for balance checking because those subtrees have different parsing rules.
For Svelte / Astro files: the framework wrapper (script blocks, frontmatter) is stripped before tokenization, so the report is about the user-visible HTML body.
Common cases this catches#
<!-- Unclosed tag -->
<section>
<p>Content
</section>
<!-- Mismatched close -->
<div>
<span>Text</div>
</span>
<!-- Duplicate ID -->
<input id="email" />
<label for="email">Email</label>
<input id="email" /> <!-- the second `email` is the bug -->
Each surfaces as a separate Hit with the line and a description.
What it does NOT catch#
- Semantic HTML.
<button>vs<a>vs<div onclick>is a separate frame’s job. - Invalid attribute combinations (e.g.
<input type="checkbox" maxlength="10">). - Spec compliance. This isn’t a substitute for Nu Html Checker on production builds - call that from CI for a stricter pass.
Site generator templates#
Template fragments of a static site generator are skipped: files under
Hugo’s layouts/ (next to hugo.toml, config.toml or another Hugo config)
and Jekyll’s _layouts/ and _includes/ (next to _config.yml). They are
pieces of a page, not pages; the built output (for example public/) is
checked instead. A layouts/ folder without the generator’s config beside it
is checked as usual.
Suppressing intentional cases#
Per-file for templates that deliberately leave tags open (rare but legitimate - e.g. partial fragments composed at runtime):
<!-- appframes:disable web/html-markup-valid -->
Per-line is supported via appframes:disable-next-line web/html-markup-valid but rarely useful - markup issues usually span multiple lines.
WARN lets the push through and records the finding. Turn frames on per repo on the dashboard's Policy page - see choosing what the gate checks.
Source on GitHub Live demo How it works Questions: contact@nimblegate.com