html-spec-maintenance
Perform maintenance tasks for @markuplint/html-spec: regenerate specification data,
review upstream changes, update manual spec files, and ensure cross-package consistency.
Input
$ARGUMENTS specifies the task. Supported tasks:
| Task | Description |
|---|---|
update | Regenerate index.json and review upstream changes |
add-element <name> | Add a new HTML element specification |
add-svg-element <name> | Add a new SVG element specification |
add-attribute <element> <attr> | Add an attribute to an element |
remove-attribute <element> <attr> | Remove an attribute from an element |
obsolete-element <name> | Mark an element as obsolete |
obsolete-attribute <element> <attr> | Mark an attribute as deprecated |
change-flag <element> <attr> <flag> | Change experimental/deprecated/nonStandard flags |
check | Verify cross-package consistency |
If omitted, defaults to update.
Reference
Before executing any task, read docs/maintenance.md (or docs/maintenance.ja.md)
for the full guide. The recipes there are the source of truth for procedures.
Also read:
- •
docs/element-spec-format.md-- JSON spec file format reference - •
docs/build-pipeline.md-- Build pipeline and data precedence rules
Task: update
The primary maintenance workflow. Regenerate index.json with the latest MDN/W3C data
and review what has changed.
Step 1: Regenerate
yarn up:gen
This runs @markuplint/spec-generator, which scrapes live MDN data and merges it with
the manual spec files in src/. The result is written to index.json.
Step 2: Review the diff
git diff packages/@markuplint/html-spec/index.json
Categorize each change:
| Category | Examples | Action |
|---|---|---|
| Minor description rewording | MDN refines role/element/attribute descriptions | Commit as-is |
| New attributes | interestfor, switch added by MDN | Commit as-is (MDN-sourced, no manual spec change needed) |
| Flag transitions | experimental → deprecated, nonStandard added/removed | Commit as-is |
| Significant spec changes | ARIA property required → inherited, content model restructured | Requires manual spec update (go to Step 3) |
| ARIA 1.3 updates | New/revised role definitions, property requirement changes | WAI-ARIA 1.3 is a Working Draft -- expect ongoing changes. 1.1 and 1.2 are finalized Recommendations and will not change. |
Caution -- ARIA version duplication:
index.jsoncontains role definitions for WAI-ARIA 1.1, 1.2, and 1.3, so many strings appear three times. When editing descriptions or properties, do not usereplace_all-- it will modify all three versions simultaneously. Always target the specific version block you intend to change.
Step 3: Handle significant changes (if any)
If the diff contains substantive changes to element behavior, ARIA mappings, or content models:
- •Identify the affected elements
- •Determine which source files need updating:
- •
src/spec.<element>.jsonfor element-specific changes - •
src/spec-common.contents.jsonfor content model category changes - •
src/spec-common.attributes.jsonfor global attribute changes - •In rare cases,
@markuplint/ml-specschemas or types may need updating
- •
- •Make the changes, referencing the authoritative specification:
- •HTML Living Standard: https://html.spec.whatwg.org/multipage/
- •HTML-ARIA: https://w3c.github.io/html-aria/
- •WAI-ARIA: https://w3c.github.io/aria/
- •Regenerate to incorporate manual spec changes:
bash
yarn up:gen
- •Verify the final diff is correct
Step 3b: Idempotency verification (when spec files are modified)
When you modify src/spec.*.json files, verify that your changes produce stable output
before committing. This ensures the generated index.json does not contain unintended
drift:
# 1. Stage spec files and index.json git add packages/@markuplint/html-spec/src/spec.*.json packages/@markuplint/html-spec/index.json # 2. Regenerate yarn up:gen # 3. Check that the attributes you changed are NOT in the diff (= stable output) git diff packages/@markuplint/html-spec/index.json | grep '"your-attr"' # 4. If stable, discard the regenerated file and use the staged version git checkout packages/@markuplint/html-spec/index.json # 5. Proceed to commit
If the diff shows unexpected changes for your attribute, it means the spec file and the generator produce different values -- investigate before committing.
Step 4: Test and commit
yarn workspace @markuplint/html-spec run test
Stage and commit index.json and any modified src/ files.
Task: add-element
Add a new HTML element specification. Follow recipe #1 in docs/maintenance.md.
- •Read
src/spec.a.jsonas a reference for a typical element - •Create
src/spec.<name>.jsonwith required fields:- •
contentModelwithcontents - •
globalAttrs(#HTMLGlobalAttrs,#GlobalEventAttrs,#ARIAAttrsset totrue) - •
attributes(element-specific, can be{}) - •
ariawithimplicitRoleandpermittedRoles
- •
- •Add spec URL comments at the top (
//format) - •Cross-package step: If the element belongs to content categories (flow, phrasing, etc.),
add it to the appropriate categories in
src/spec-common.contents.json. Without this,@markuplint/rules'permitted-contentsrule will flag the element as invalid content in parent elements that allow those categories. - •Regenerate:
yarn workspace @markuplint/html-spec run gen - •Verify the element appears in
index.json - •Run tests:
yarn workspace @markuplint/html-spec run test
Task: add-svg-element
Add a new SVG element specification. Follow recipe #3 in docs/maintenance.md.
- •Read
src/spec.svg_circle.jsonas a reference for a typical SVG element - •Create
src/spec.svg_<name>.json(thesvg_prefix maps to namespacesvg:<name>) - •Use SVG-specific global attribute categories (
#SVGCoreAttrs,#SVGPresentationAttrs) - •For ARIA, use AAM references:
{ "core-aam": true, "graphics-aam": true } - •Regenerate:
yarn workspace @markuplint/html-spec run gen - •Run tests:
yarn workspace @markuplint/html-spec run test
Task: add-attribute
Add an attribute to an element. Follow recipe #2 in docs/maintenance.md.
- •Open
src/spec.<element>.json - •Add the attribute entry to the
attributesobject. Seedocs/element-spec-format.mdfor the full attribute definition format. Common patterns:- •Simple typed attribute:
"href": { "type": "URL" } - •Conditional attribute:
"accept": { "type": ..., "condition": "[type='file' i]" } - •Boolean attribute:
"disabled": { "type": "Boolean" }
- •Simple typed attribute:
- •For a global attribute (applies to all elements), edit
src/spec-common.attributes.jsoninstead, adding the attribute to the appropriate category (e.g.,#HTMLGlobalAttrs) - •Regenerate:
yarn workspace @markuplint/html-spec run gen - •Verify the attribute appears in
index.jsonwith correct metadata - •Run tests:
yarn workspace @markuplint/html-spec run test
Task: remove-attribute
Remove an attribute from an element's manual specification.
- •Open
src/spec.<element>.json - •Remove the attribute entry from the
attributesobject - •For a global attribute, edit
src/spec-common.attributes.jsoninstead - •Regenerate:
yarn workspace @markuplint/html-spec run gen - •Verify the attribute no longer appears in
index.jsonfor the element. Note: If the attribute also exists in MDN data, it will still appear inindex.jsonfrom the MDN source. To fully suppress an MDN-sourced attribute, you may need to override it in the manual spec rather than simply removing it. - •Run tests:
yarn workspace @markuplint/html-spec run test
Task: obsolete-element
Mark an element as obsolete. Follow recipe #8 in docs/maintenance.md.
There are two approaches:
- •Via spec-generator's hardcoded list (preferred for standard obsolete elements):
Add the element name to the
obsoleteListarray inpackages/@markuplint/spec-generator/src/html-elements.ts - •Via manual spec file: Set
"obsolete": truein the element'ssrc/spec.<element>.json
Obsolete elements automatically get:
- •
citepointing to the HTML spec obsolete features section - •
contents: true(any content allowed) - •
permittedRoles: true,implicitRole: false
After making the change:
- •Regenerate:
yarn up:gen - •Verify the element appears in
index.jsonwith"obsolete": true - •Run tests:
yarn workspace @markuplint/html-spec run test
Task: obsolete-attribute
Mark an attribute as deprecated in an element's specification.
- •Open
src/spec.<element>.json - •Add
"deprecated": trueto the attribute definition:If the attribute already has other fields (json"align": { "deprecated": true }type,condition, etc.), simply add"deprecated": truealongside them. - •For a global attribute, edit
src/spec-common.attributes.jsoninstead - •Regenerate:
yarn workspace @markuplint/html-spec run gen - •Verify the attribute shows
"deprecated": trueinindex.json - •Run tests:
yarn workspace @markuplint/html-spec run test
Task: change-flag
Change the experimental, deprecated, or nonStandard flag on an attribute.
These boolean flags indicate the standardization status of an attribute:
| Flag | Meaning |
|---|---|
experimental | The attribute is part of an emerging specification not yet stable |
deprecated | The attribute is obsolete and should not be used |
nonStandard | The attribute is not part of any standard |
- •Open
src/spec.<element>.json(orsrc/spec-common.attributes.jsonfor globals) - •Add, change, or remove the flag on the target attribute:
To remove a flag, delete the property entirely.json
"attributionsrc": { "deprecated": true } - •Note: Flags from MDN scraping also appear in
index.json. Manual spec flags take precedence, so setting a flag in the manual spec will override the MDN value. However, MDN-only attributes (not defined in manual specs) can only be overridden by adding an entry for that attribute in the manual spec. - •Regenerate:
yarn workspace @markuplint/html-spec run gen - •Verify the flag change in
index.json - •Run tests:
yarn workspace @markuplint/html-spec run test
ARIA Version System
Resolution Logic
resolveVersion() (@markuplint/ml-spec/src/utils/resolve-version.ts) checks
aria[version] first, falls back to top-level aria. The runtime default is
ARIA_RECOMMENDED_VERSION = '1.2' (@markuplint/ml-spec/src/utils/aria-version.ts).
Key Placement Rules
| Key | Meaning | Mutability |
|---|---|---|
Top-level aria | Default / latest. Fallback for versions without overrides | Mutable — update to match current W3C Rec |
"1.1" | ARIA 1.1 snapshot | Frozen — never add new roles |
"1.2" | ARIA 1.2 snapshot (rarely needed) | Only create when top-level diverges from 1.2 |
Decision: Where to add new permittedRoles
- •Is the role in the W3C Recommendation "ARIA in HTML" (ARIA 1.2 based, Aug 2025)? → Add to top-level only
- •Is the role ARIA 1.3 draft-only (not in W3C Rec)?
→ Add to top-level, AND create
"1.2"key with the current 1.2 list to freeze it - •Was the role in the original ARIA 1.1 spec for this element?
→ It should already be in
"1.1". Never add new roles to"1.1".
permittedRoles Quick Reference
| Pattern | Meaning |
|---|---|
true | Any role allowed |
false | No roles allowed |
["role1", "role2"] | Specific roles (alphabetical order) |
[{"name": "role", "deprecated": true}] | Deprecated role |
Task: check
Verify cross-package consistency between @markuplint/html-spec and related packages.
- •Content model categories: Verify that category names in
src/spec-common.contents.jsonmatch theCategoryenum in@markuplint/ml-spec/schemas/content-models.schema.json - •Element membership: Verify that elements listed in content categories have
corresponding
src/spec.<element>.jsonfiles and consistentcontentModeldefinitions - •Attribute types: Check that attribute type references (e.g.,
"URL","<color>") exist in@markuplint/types' definitions registry - •Schema validation: Run
yarn workspace @markuplint/html-spec run testto validate all source JSON files against@markuplint/ml-specschemas
Report results as:
| # | Check | Status | Notes |
Testing Requirements for Spec Changes
Spec data changes propagate to multiple test suites. Always run yarn test
(full suite) before committing.
Test Matrix
| Change type | Primary test file | Also check |
|---|---|---|
| ARIA (implicitRole, permittedRoles) | rules/src/wai-aria/index.spec.ts | ml-spec/src/algorithm/aria/get-permitted-roles-spec.spec.ts |
| Attributes (new/changed) | rules/src/invalid-attr/index.spec.ts | Existing tests with changed enum error messages |
| Content model | rules/src/permitted-contents/index.spec.ts | — |
Cross-Package Impact
- •Hardcoded role arrays:
ml-spec/.../get-permitted-roles-spec.spec.tshas hardcodedpermittedRolesfor img, button, input, form, etc. Update these when changingpermittedRolesin html-spec. - •Enum error messages: Adding a value to an enum (e.g., button
command) changes the error message string in existinginvalid-attrtests.
Test Conventions
- •
toStrictEqualwith exact{ severity, line, col, message, raw }— nevertoBeGreaterThan(0) - •Always include both valid (empty violations) and invalid (exact violation) cases
- •ARIA version in tests:
{ rule: { options: { version: '1.1' } } } - •Some roles require ARIA attributes: focusable
separator→aria-valuenow,meter→aria-valuenow
Rules
- •
index.jsonis generated -- never edit it directly. Always modifysrc/files and regenerate. - •
Manual spec data takes precedence over MDN data. Attributes defined in
src/spec.*.jsonoverride same-named MDN-sourced attributes. Use this to correct inaccurate MDN data. - •
Minor MDN description changes should be committed as-is. Do not attempt to override cosmetic upstream improvements.
- •
Content model category membership is critical. A missing element in a category causes
permitted-contentsrule false positives in downstream linting. - •
Always run tests after changes. Schema validation catches structural errors before they propagate to downstream packages.
- •
Reference authoritative specs for significant changes. Use WebSearch to verify against HTML Living Standard, WAI-ARIA, and HTML-ARIA before modifying manual spec files.
- •
Use conventional commit prefixes based on the nature of the change:
Change type Prefix Example Description updates only chorechore(html-spec): update role descriptionsAttribute/spec additions featfeat(html-spec): add input switch attributeSpec data corrections fixfix(html-spec): correct ARIA mapping for button - •
Separate spec changes into individual PRs. Each specification change (new attribute, ARIA mapping fix, etc.) should be on its own branch and PR. Description-only updates can be batched into a single PR.
- •
Run
yarn test(full suite) before committing. Spec changes affect@markuplint/rulesand@markuplint/ml-spectests. - •
Keep
permittedRolesarrays in alphabetical order.