Rule renames and splits
Master reference for rule-name changes with a stable v4 lineage (v4.18.3). Option-format changes beyond a rename or split are on the per-rule pages under rules/. ARIA version and wai-aria option mapping: ARIA.
v5 redoes the catalog: verb-prefix names, one rule per independent check, and spec-conformance metadata. v4's 38 built-in rules become 106 in v5. Of that: 12 were renamed, 10 were split, wai-aria became 21 independent rules, and the rest are new checks with no v4 predecessor.
Old names that were renamed or split keep working: Markuplint reports a deprecation warning and expands the config. Those aliases are removed in v6. Names that did not change have no alias — see Known migration gap.
Categories
v4 used five categories (validation, a11y, naming-convention, maintainability, style). v5 uses nine, from each rule's meta.ts category. Categories are a browsing aid on the rules index; they do not appear in config files.
| Category | What it covers |
|---|---|
syntax | Parse-level conformance |
structure | Content model, ancestors, doctype, table model |
attributes | Attribute name/value conformance |
references | ID and fragment references |
forms | Form-control checks |
a11y | ARIA and accessibility |
style | Opinionated formatting/naming |
maintainability | Project hygiene |
compat | browserslist × BCD, experimental/non-standard flags |
naming-convention (only class-naming) folds into style.
1:1 renames
These 12 names existed in stable v4 and map 1:1:
| Old name (v4) | New name |
|---|---|
attr-duplication | no-duplicate-attr |
id-duplication | no-duplicate-id |
required-attr | require-attr |
required-element | require-element |
ineffective-attr | no-ineffective-attr |
end-tag | require-end-tag |
disallowed-element | no-restricted-element |
heading-levels | no-skipped-heading-level |
neighbor-popovers | require-adjacent-popover |
no-hard-code-id | no-hardcoded-id |
no-use-event-handler-attr | no-event-handler-attr |
use-list | no-pseudo-list |
required-attr → require-attr keeps { name, value } matching. See Scope changes.
Splits
These 10 names existed in stable v4. Each split is independently configurable.
| Old name (v4) | Split into | What each checks |
|---|---|---|
invalid-attr | no-unknown-attr | Name not defined by the spec |
no-disallowed-attr | Name defined but disallowed here (noUse, unmet condition, is on an autonomous custom element) | |
no-invalid-attr-value | Value type/grammar | |
no-restricted-attr | User disallowAttrs only — omitted from the alias unless disallowAttrs was set | |
doctype | require-doctype | Missing DOCTYPE |
no-obsolete-doctype | Legacy public/system identifier (about:legacy-compat still allowed). Omitted if denyObsoleteType was false | |
character-reference | no-malformed-character-reference | Malformed character references |
no-unescaped-char | Unescaped < / ambiguous & | |
deprecated-attr | no-obsolete-attr | HTML LS obsolete attributes (error) |
no-deprecated-attr | MDN/BCD deprecated attributes (warning) | |
deprecated-element | no-obsolete-element | Obsolete elements (error) |
no-deprecated-element | Deprecated elements (warning) | |
landmark-roles | no-nested-top-level-landmark | Nested banner / main / contentinfo (ignoreRoles carried over) |
require-landmark-label | Duplicate landmark without an accessible name. Omitted if labelEachArea was false | |
no-refer-to-non-existent-id | no-refer-to-non-existent-id | DOMID / ARIA ID-reference (name unchanged — gap) |
no-broken-fragment-link | a[href] / area[href] fragment (fragmentRefersNameAttr moved here) | |
permitted-contents | permitted-contents | Child content model (name unchanged — gap) |
no-disallowed-ancestor | forbiddenAncestors | |
require-ancestor | descendantOf | |
no-duplicate-sibling-attr | Sibling-unique attributes (e.g. track[default]) | |
required-h1 | require-h1 | Missing <h1> |
no-duplicate-h1 | Duplicate <h1>. Omitted if expected-once was false | |
table-row-column-alignment | no-table-cell-overlap | Cell overlap (error) |
no-table-span-overflow | Span past row-group/table (error) | |
no-empty-table-track | Row/column with no anchored cell (error) | |
consistent-table-row-length | Ragged column count (warning) |
aria-* and role are exempt from the three spec-checking invalid-attr successors; ARIA rules own them.
Option-routed splits
Most splits copy the old setting to every successor. These do not:
doctype: dropno-obsolete-doctypewhendenyObsoleteTypewasfalse.landmark-roles: droprequire-landmark-labelwhenlabelEachAreawasfalse.required-h1: dropno-duplicate-h1whenexpected-oncewasfalse.invalid-attr: addno-restricted-attronly whendisallowAttrswas set. Option routing:invalid-attr.
The alias does this automatically. Hand-written configs should match that routing.
New in v5 (no v4 equivalent)
These checks have no v4 rule to migrate from. Presets may enable some of them (see Preset changes).
attr-order, form-attr-references-form, head-element-order, input-list-references-datalist, itemprop-requires-itemscope, label-for-references-labelable, label-no-multiple-controls, link-types, map-id-name-match, meta-charset-position, meter-value-bounds, no-always-matching-source, no-aria-hidden-on-hidden-until-found, no-content-after-body, no-duplicate-autofocus, no-duplicate-visible-main, no-experimental-features, no-extra-selected-options, no-input-file-value, no-mismatched-aspect-ratio, no-mixed-srcset-descriptors, no-nonstandard-features, no-redundant-accessible-name, no-stray-head-or-body-tag, no-unclosed-element-at-eof, no-unpaired-srcset-sizes, no-unsupported-browser-features, progress-value-bounds, require-dialog-autofocus, sizes-auto-requires-lazy-loading, usemap-references-map, valid-importmap, valid-speculation-rules.
label-no-multiple-controls is new as a rule name, but the check lived inside v4 label-has-control — Known migration gap.
Two ARIA rules (require-parent-role, tab-requires-tabpanel) are also new relative to the v4 wai-aria implementation; they still receive the wai-aria alias. See ARIA.
Deletions
Only wai-aria is removed as a rule name. The alias expands it to 21 successors until v6. There is no other v4 built-in rule deletion.
Scope changes
require-attr: full v4required-attrscope, including{ name, value }patterns.label-has-control: only reports a<label>with no associated control. Reporting a second descendant control islabel-no-multiple-controls.
Known migration gap
These old names are not deprecated (they still exist), so there is no alias warning. A raw config that enabled only the old name silently drops the split-off check.
| v4 name you still use | Split-off rules you must add yourself |
|---|---|
permitted-contents | no-disallowed-ancestor, require-ancestor, no-duplicate-sibling-attr |
no-refer-to-non-existent-id | no-broken-fragment-link |
label-has-control | label-no-multiple-controls |
Preset users: markuplint:html-standard already enables the permitted-contents siblings and label-no-multiple-controls. markuplint:a11y enables no-broken-fragment-link and label-has-control. markuplint:recommended extends both.
Gaps that remain even with presets:
- Extending
markuplint:html-standardalone does not enableno-broken-fragment-link(it is only ina11y), while it does enableno-refer-to-non-existent-id. - Extending
markuplint:a11yalone does not enablelabel-no-multiple-controls(it is only inhtml-standard), while v4a11yran that check insidelabel-has-control.
Severity changes
Compared to the v4 default (createRule defaults to error unless defaultSeverity is set):
| Rule | v4 | v5 |
|---|---|---|
no-table-cell-overlap / no-table-span-overflow / no-empty-table-track | warning (as table-row-column-alignment) | error |
consistent-table-row-length | warning (same compound rule) | warning |
no-duplicate-dt | error | warning |
no-obsolete-attr / no-obsolete-element | error (as deprecated-attr / deprecated-element) | error |
no-deprecated-attr / no-deprecated-element | error (same compound rules) | warning |
no-broken-fragment-link | error (as part of no-refer-to-non-existent-id) | warning |
require-h1 / no-duplicate-h1 | error (as required-h1) | warning |
require-adjacent-popover | error (as neighbor-popovers) | warning |
no-consecutive-br stays warning (false positives on legitimate line breaks).
The three table-model errors can fail CI that treats errors as fatal, on markup that only warned in v4.
Preset changes
Verified against v4.18.3 preset JSON and current preset.*.jsonc.
markuplint:code-styleswas{}in v4. It now enablescase-sensitive-attr-nameandcase-sensitive-tag-name.markuplint:securitywas{}in v4. It now enablesno-event-handler-attr.markuplint:compatdid not exist in v4. v5'smarkuplint:recommendedextends it, enablingno-unsupported-browser-featuresandno-nonstandard-features(no-experimental-featuresstays opt-in).markuplint:performancegainshead-element-orderandno-mismatched-aspect-ratio. ItsnodeRules(charset,defer, img dimensions, iframeloading) are unchanged in substance.markuplint:html-standarddropsno-duplicate-dtandno-ineffective-attr(both were on in v4). It addsno-unknown-attr/no-disallowed-attr/no-invalid-attr-value— v4'shtml-standarddid not includeinvalid-attr. It also adds many new MUST checks (parse structure, srcset, forms,itemprop-requires-itemscope, and thepermitted-contentssiblings).markuplint:a11ystill includeswai-ariacoverage, now as named groups. That enables checks the v4 umbrella left off by default, plus two checks the v4 umbrella never ran. See ARIA.no-refer-to-non-existent-idandno-duplicate-idremain in botha11yandhtml-standard.markuplint:recommendedstill extends code-styles, html-standard, a11y, performance, security, and rdfa, and now also compat.
Rules intentionally in no preset: attr-order, attr-value-quotes, class-naming, no-boolean-attr-value, no-default-value, no-empty-palpable-content, no-duplicate-dt, no-ineffective-attr, no-experimental-features.