markuplint Documentation

website·Indexed 33 days ago

https://markuplint.dev/

A linter for HTML that validates markup against the HTML Living Standard and WAI-ARIA specifications. Includes a comprehensive set of built-in rules for syntax, structure, attributes, accessibility, and browser compatibility, with support for auto-fixing via the --fix option.

Tokens
62.9K
Snippets
390
Records
488
Agent score
69%

What's inside markuplint

  1. Overview of the markuplint:a11y preset checks

    The markuplint:a11y preset validates the following accessibility criteria:

    • WAI-ARIA conformance: Validates role and aria-* attributes.
    • Accessible names: Ensures elements requiring names (form controls, images, landmarks) have them.
    • Landmark structure: Checks for top-level landmarks like banner, main, complementary, and contentinfo.
    • Heading structure: Verifies that <h1> is present and heading levels are not skipped.
    • Label associations: Ensures <label> elements are correctly linked to controls.
    • Media accessibility: Checks that <audio> and <video> elements have <track> elements.
    • Interactive content: Checks for accesskey usage, tabindex restrictions, and autoplay constraints.
  2. Update markuplint presets for v5

    v4-to-v5
    Markuplint v5 introduces several changes to its built-in presets. Users should be aware of new rules added to existing presets and the introduction of the compat preset.
    • markuplint:code-styles: Now enables case-sensitive-attr-name and case-sensitive-tag-name.
    • markuplint:security: Now enables no-event-handler-attr.
    • markuplint:compat: New in v5. Enables no-unsupported-browser-features and no-nonstandard-features.
    • markuplint:performance: Adds head-element-order and no-mismatched-aspect-ratio.
    • markuplint:html-standard: Drops no-duplicate-dt and no-ineffective-attr. Adds no-unknown-attr, no-disallowed-attr, no-invalid-attr-value, and various MUST checks (srcset, forms, etc.).
    • markuplint:recommended: Now extends markuplint:compat in addition to code-styles, html-standard, a11y, performance, security, and rdfa.
  3. Enforce attribute order with the attr-order rule

    The attr-order rule enforces a consistent order of attributes on HTML elements. By default, attributes are sorted alphabetically. This rule is not included in any preset because attribute order is a project-specific style preference. It supports auto-fixing via the --fix CLI option.
    <!-- Incorrect: style before class and id -->
    <div style="color: red" class="foo" id="bar"></div>
    
    <!-- Correct: sorted alphabetically (or by config) -->
    <div class="foo" id="bar" style="color: red"></div>
  4. Validate attribute values with no-invalid-attr-value

    The no-invalid-attr-value rule warns when an attribute's value does not match the type or pattern required by the HTML Living Standard. It specifically checks the value of attributes that have already been accepted as valid names by no-unknown-attr and no-disallowed-attr.

    Note: Dynamic (template-interpolated) values and spread attributes are not evaluated because their content is unknown at lint time.

    <!-- ❌ Incorrect: tabindex must be an integer -->
    <button tabindex="non-integer">The Button</button>
    
    <!-- ❌ Incorrect: referrerpolicy has an invalid value -->
    <a href="/" referrerpolicy="invalid-value">The Anchor</a>
    
    <!-- ✅ Correct -->
    <button tabindex="0">The Button</button>
    <a href="/" referrerpolicy="no-referrer">The Anchor</a>
  5. Prevent unknown attributes with no-unknown-attr

    The no-unknown-attr rule warns if an attribute is used that is not defined by the HTML Living Standard or a custom rule. It focuses on name eligibility; if a non-existent attribute is similar to a valid one, it provides a 'Did you mean...?' suggestion.

    Note: This rule only checks if the attribute name exists. Use no-disallowed-attr for attributes that exist but are not allowed in the current context, and no-invalid-attr-value for invalid values on known attributes.

    <!-- ❌ Incorrect: unexist-attr is not a valid HTML attribute -->
    <div unexist-attr></div>
    
    <!-- ✅ Correct -->
    <div></div>
  6. Understand Markuplint v5 rule name changes and aliases

    v4-to-v5

    Markuplint v5 has restructured its rule catalog, expanding from 38 built-in rules in v4 to 106 in v5. Rules now use verb-prefix names and are designed for one rule per independent check.

    For migration:

    • Rules that were renamed or split in v5 will still work via aliases in v5, but will trigger a deprecation warning.
    • These aliases will be removed in v6.
    • Rules that did not change their name have no alias.
  7. Prevent layout shifts with the no-mismatched-aspect-ratio rule

    The no-mismatched-aspect-ratio rule warns when the width and height attributes of an <img> or <source> (inside <picture>) element do not match the actual aspect ratio of the referenced image file. This prevents Cumulative Layout Shift (CLS) by ensuring the browser reserves the correct amount of space before the image loads.

    How it works:

    • Checks <img> elements with src, width, and height attributes.
    • Checks <source> elements inside <picture> with srcset, width, and height attributes (using the first URL in srcset).
    • Skips remote URLs (http://, https://), data URIs (data:), and dynamic attribute values (e.g., Vue :src or JSX src={...}).
    • Strips query strings and fragments when resolving file paths.
    • Applies a ±0.5 pixel tolerance to account for integer rounding of intrinsic ratios.
    • Silently skips files that cannot be found or read.
    <!-- ❌ Incorrect: Image is 100x50 but attributes say 100x100 -->
    <img src="/photo.png" width="100" height="100" alt="photo" />
    
    <!-- ✅ Correct: Attributes match the 2:1 ratio -->
    <img src="/photo.png" width="100" height="50" alt="photo" />
    
    <!-- ✅ Correct: Scaled proportionally (still 2:1) -->
    <img src="/photo.png" width="200" height="100" alt="photo" />
  8. Prevent use of unsupported browser features with no-unsupported-browser-features

    The no-unsupported-browser-features rule warns when HTML elements or attributes are not supported by the project's target browsers. It uses browserslist for target definitions and @mdn/browser-compat-data for support checks. If no browserslist configuration is found in the project, this rule does nothing (no-op). Features that were previously supported but later removed from a browser are also reported.

    Note: This rule specifically handles browser support. For experimental features, use no-experimental-features, and for non-standard features, use no-nonstandard-features.

  9. Prevent use of non-standard HTML features with no-nonstandard-features

    The no-nonstandard-features rule warns about elements and attributes marked as non-standard in the HTML specification data bundled with markuplint. This rule was split from the no-unsupported-features rule's checkNonStandard option. It is automatically enabled if you use the recommended preset via the compat preset. The default severity for this rule is warning.
    <!-- ❌ Incorrect: uses non-standard attribute moz-opaque -->
    <canvas moz-opaque></canvas>
    
    <!-- ✅ Correct -->
    <canvas></canvas>
  10. Enforce BEM-style class naming and nesting constraints

    To implement a BEM-like architecture where block names start with an uppercase letter and elements are denoted by two underscores, you can combine the class-naming rule with childNodeRules and regexSelector.

    This configuration ensures:

    1. Block names start with an uppercase letter followed by lowercase letters or numbers.
    2. Elements follow the pattern BlockName__elementName.
    3. Elements are not nested inside elements belonging to a different block (they must be children of their own block or a separate block entirely).
    {
      "rules": {
        "class-naming": "/^[A-Z][a-z0-9]+$/"
      },
      "childNodeRules": [
        {
          "regexSelector": {
            "attrName": "class",
            "attrValue":
              // Capture a block name using a named group
              "/^(?<BlockName>[A-Z][a-z0-9]+)(?:__[a-z][a-z0-9-]+)?$/"
          },
          "rules": {
            "class-naming": {
              "value": [
                // Use Mustache format to ensure children match the parent BlockName
                "/^{{BlockName}}__[a-z][a-z0-9-]+$/",
                "/^([A-Z][a-z0-9]+)$/"
              ]
            }
          }
        }
      ]
    }
  11. Validate HTML content models with permitted-contents

    The permitted-contents rule ensures that child elements and text nodes are allowed by their parent's content model according to the HTML Living Standard. It triggers a warning if:

    • A child element or text node is not permitted by the parent.
    • An element requiring non-empty text content (like <title> or <option> without a label) is empty or contains only whitespace.

    Note: This rule focuses on content models. For forbidden ancestors, use no-disallowed-ancestor; for missing required ancestors, use require-ancestor; and for duplicate sibling attributes, use no-duplicate-sibling-attr.

    <!-- ❌ Incorrect: <div> is not allowed inside <ul> -->
    <ul>
      <div>Not allowed DIV element</div>
    </ul>
    
    <!-- ✅ Correct: <li> is allowed inside <ul> -->
    <ul>
      <li>List item</li>
    </ul>
  12. Prevent obsolete DOCTYPE declarations with no-obsolete-doctype

    The no-obsolete-doctype rule warns when a document uses an obsolete DOCTYPE declaration. This includes DOCTYPES with a public identifier or system identifiers, except for the specific legacy-string exception <!DOCTYPE html SYSTEM "about:legacy-compat"> permitted by the HTML Living Standard for compatibility with old parsers. Note that this rule does not check for the total absence of a DOCTYPE; that is handled by the require-doctype rule.
    <!-- ❌ Incorrect: Obsolete HTML 4.01 DOCTYPE -->
    <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
    
    <!-- ✅ Correct: Modern HTML5 DOCTYPE -->
    <!doctype html>
    
    <!-- ✅ Correct: Permitted legacy-compat exception -->
    <!DOCTYPE html SYSTEM "about:legacy-compat">