@codelaw/rules-html 0.0.0-stage → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. package/LICENSE +23 -0
  2. package/NOTICE +24 -0
  3. package/README.md +166 -2
  4. package/codelaw-pack.toml +6 -0
  5. package/corpus.toml +76 -0
  6. package/data/html/elements.json +4892 -0
  7. package/data/html/entities.json +1 -0
  8. package/data/html/input-attributes.json +227 -0
  9. package/package.json +14 -4
  10. package/presets/html/a11y.toml +29 -0
  11. package/presets/html/conventions.toml +6 -0
  12. package/presets/html/document.toml +8 -0
  13. package/presets/html/recommended.toml +67 -0
  14. package/rules/html/area-alt.md +48 -0
  15. package/rules/html/aria-hidden-body.md +39 -0
  16. package/rules/html/aria-label-misuse.md +42 -0
  17. package/rules/html/attribute-allowed-values.cases.md +31 -0
  18. package/rules/html/attribute-allowed-values.md +41 -0
  19. package/rules/html/attribute-misuse.md +39 -0
  20. package/rules/html/autocomplete-password.md +41 -0
  21. package/rules/html/button-type.cases.md +28 -0
  22. package/rules/html/button-type.md +35 -0
  23. package/rules/html/close-attr.md +33 -0
  24. package/rules/html/close-order.cases.md +38 -0
  25. package/rules/html/close-order.md +47 -0
  26. package/rules/html/deprecated.md +39 -0
  27. package/rules/html/doctype-html.md +36 -0
  28. package/rules/html/element-name.cases.md +21 -0
  29. package/rules/html/element-name.md +42 -0
  30. package/rules/html/element-permitted-content.cases.md +54 -0
  31. package/rules/html/element-permitted-content.md +45 -0
  32. package/rules/html/element-permitted-occurrences.md +45 -0
  33. package/rules/html/element-permitted-order.md +44 -0
  34. package/rules/html/element-permitted-parent.cases.md +11 -0
  35. package/rules/html/element-permitted-parent.md +43 -0
  36. package/rules/html/element-required-ancestor.md +45 -0
  37. package/rules/html/element-required-attributes.md +44 -0
  38. package/rules/html/element-required-content.cases.md +23 -0
  39. package/rules/html/element-required-content.md +43 -0
  40. package/rules/html/empty-heading.md +40 -0
  41. package/rules/html/empty-title.md +39 -0
  42. package/rules/html/fieldset-legend.md +44 -0
  43. package/rules/html/form-dup-name.cases.md +38 -0
  44. package/rules/html/form-dup-name.md +48 -0
  45. package/rules/html/form-submit.md +44 -0
  46. package/rules/html/heading-level.cases.md +26 -0
  47. package/rules/html/heading-level.md +44 -0
  48. package/rules/html/hidden-focusable.cases.md +23 -0
  49. package/rules/html/hidden-focusable.md +40 -0
  50. package/rules/html/img-alt.cases.md +22 -0
  51. package/rules/html/img-alt.md +44 -0
  52. package/rules/html/img-title.md +40 -0
  53. package/rules/html/input-attributes.md +34 -0
  54. package/rules/html/input-image-alt.md +39 -0
  55. package/rules/html/input-missing-label.md +40 -0
  56. package/rules/html/link-text.md +39 -0
  57. package/rules/html/long-title.md +40 -0
  58. package/rules/html/map-dup-name.md +40 -0
  59. package/rules/html/map-id-name.md +38 -0
  60. package/rules/html/meta-refresh.md +42 -0
  61. package/rules/html/missing-doctype.cases.md +24 -0
  62. package/rules/html/missing-doctype.md +43 -0
  63. package/rules/html/multiple-labeled-controls.md +40 -0
  64. package/rules/html/no-abstract-role.md +39 -0
  65. package/rules/html/no-autoplay.md +37 -0
  66. package/rules/html/no-conditional-comment.md +39 -0
  67. package/rules/html/no-deprecated-attr.md +39 -0
  68. package/rules/html/no-dup-attr.md +37 -0
  69. package/rules/html/no-dup-class.md +33 -0
  70. package/rules/html/no-duplicate-id.cases.md +23 -0
  71. package/rules/html/no-duplicate-id.md +42 -0
  72. package/rules/html/no-implicit-close.cases.md +38 -0
  73. package/rules/html/no-implicit-close.md +40 -0
  74. package/rules/html/no-implicit-input-type.md +34 -0
  75. package/rules/html/no-inline-script.md +42 -0
  76. package/rules/html/no-inline-style.cases.md +12 -0
  77. package/rules/html/no-inline-style.md +42 -0
  78. package/rules/html/no-missing-references.md +39 -0
  79. package/rules/html/no-multiple-main.md +40 -0
  80. package/rules/html/no-raw-characters.cases.md +84 -0
  81. package/rules/html/no-raw-characters.md +35 -0
  82. package/rules/html/no-redundant-aria-label.md +36 -0
  83. package/rules/html/no-redundant-for.md +34 -0
  84. package/rules/html/no-redundant-role.md +42 -0
  85. package/rules/html/no-utf8-bom.md +45 -0
  86. package/rules/html/prefer-button.md +39 -0
  87. package/rules/html/prefer-native-element.md +40 -0
  88. package/rules/html/prefer-tbody.md +41 -0
  89. package/rules/html/require-sri.md +41 -0
  90. package/rules/html/script-element.md +36 -0
  91. package/rules/html/script-type.md +33 -0
  92. package/rules/html/target-blank-rel.cases.md +35 -0
  93. package/rules/html/target-blank-rel.md +32 -0
  94. package/rules/html/tel-non-breaking.md +37 -0
  95. package/rules/html/text-content.md +39 -0
  96. package/rules/html/th-scope.md +47 -0
  97. package/rules/html/unique-landmark.md +41 -0
  98. package/rules/html/unrecognized-char-ref.cases.md +41 -0
  99. package/rules/html/unrecognized-char-ref.md +42 -0
  100. package/rules/html/valid-autocomplete.cases.md +42 -0
  101. package/rules/html/valid-autocomplete.md +42 -0
  102. package/rules/html/valid-for.md +40 -0
  103. package/rules/html/valid-id.md +40 -0
  104. package/rules/html/void-content.md +34 -0
@@ -0,0 +1,39 @@
1
+ ---
2
+ id: html/input-image-alt
3
+ title: "Image buttons have alt text"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "the text needs a human"
7
+ stability: preview
8
+ ported_from: [html-validate/wcag/h36]
9
+ tags: [a11y]
10
+ since: 0.1.0
11
+ ---
12
+
13
+ An `<input type="image">` has non-empty `alt` text.
14
+
15
+ **Why:** The `alt` is the button's only name for screen readers and when the image doesn't load.
16
+
17
+ ```codelaw
18
+ native: html/input-image-alt
19
+ ```
20
+
21
+ ## How to fix
22
+
23
+ 1. Add `alt` saying what the button does.
24
+
25
+ ## Examples
26
+
27
+ ### HTML
28
+
29
+ #### Don't
30
+
31
+ ```html
32
+ <input type="image" src="go.png">
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```html
38
+ <input type="image" src="go.png" alt="Search">
39
+ ```
@@ -0,0 +1,40 @@
1
+ ---
2
+ id: html/input-missing-label
3
+ title: "Form controls have a label"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "the label text needs a human"
7
+ stability: preview
8
+ ported_from: [html-validate/input-missing-label, htmlhint/input-requires-label]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Every visible `<input>`, `<select>` and `<textarea>` has a label: a `<label for>`, a wrapping `<label>`, `aria-label` or `aria-labelledby`, with text.
13
+
14
+ **Why:** Without a label, screen readers announce the control as "edit text" with no hint of what to enter.
15
+
16
+ ```codelaw
17
+ native: html/input-missing-label
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Add a `<label for>` with text, or wrap the control in a `<label>`.
23
+ 2. If a visible label is impossible, add `aria-label`.
24
+
25
+ ## Examples
26
+
27
+ ### HTML
28
+
29
+ #### Don't
30
+
31
+ ```html
32
+ <input type="text" name="q">
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```html
38
+ <label for="q">Search</label>
39
+ <input type="text" id="q" name="q">
40
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ id: html/link-text
3
+ title: "Links have text"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "the link text needs a human"
7
+ stability: preview
8
+ ported_from: [html-validate/wcag/h30]
9
+ tags: [a11y]
10
+ since: 0.1.0
11
+ ---
12
+
13
+ A link with `href` has text that says where it goes: content, an image with `alt`, or `aria-label`.
14
+
15
+ **Why:** Screen readers announce a link by its text; without it the user hears only "link".
16
+
17
+ ```codelaw
18
+ native: html/link-text
19
+ ```
20
+
21
+ ## How to fix
22
+
23
+ 1. Give the link text, an image with `alt`, or an `aria-label`.
24
+
25
+ ## Examples
26
+
27
+ ### HTML
28
+
29
+ #### Don't
30
+
31
+ ```html
32
+ <a href="/cart"><img src="cart.png" alt=""></a>
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```html
38
+ <a href="/cart"><img src="cart.png" alt="Cart"></a>
39
+ ```
@@ -0,0 +1,40 @@
1
+ ---
2
+ id: html/long-title
3
+ title: "Page titles are short"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "shortening a title needs a human"
7
+ stability: preview
8
+ options:
9
+ maxlength: { type: "integer", default: 70, description: "the longest title allowed, in characters" }
10
+ ported_from: [html-validate/long-title]
11
+ since: 0.1.0
12
+ ---
13
+
14
+ The `<title>` text is at most 70 characters.
15
+
16
+ **Why:** Search results and browser tabs cut long titles, often before the words that matter.
17
+
18
+ ```codelaw
19
+ native: html/long-title
20
+ ```
21
+
22
+ ## How to fix
23
+
24
+ 1. Shorten the title to what identifies the page.
25
+
26
+ ## Examples
27
+
28
+ ### HTML
29
+
30
+ #### Don't
31
+
32
+ ```html
33
+ <title>Acme Corporation — Products, Services, Pricing, Support, Downloads and Contact Details</title>
34
+ ```
35
+
36
+ #### Do
37
+
38
+ ```html
39
+ <title>Pricing — Acme</title>
40
+ ```
@@ -0,0 +1,40 @@
1
+ ---
2
+ id: html/map-dup-name
3
+ title: "Image maps have unique names"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "renaming a map means updating the `usemap` that points at it"
7
+ stability: preview
8
+ ported_from: [html-validate/map-dup-name]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ No two `<map>` elements share a `name`.
13
+
14
+ **Why:** An image's `usemap` picks one by name, so the other map is unreachable.
15
+
16
+ ```codelaw
17
+ native: html/map-dup-name
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Rename one map and the `usemap` attributes that refer to it.
23
+
24
+ ## Examples
25
+
26
+ ### HTML
27
+
28
+ #### Don't
29
+
30
+ ```html
31
+ <map name="nav"></map>
32
+ <map name="nav"></map>
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```html
38
+ <map name="nav"></map>
39
+ <map name="footer-nav"></map>
40
+ ```
@@ -0,0 +1,38 @@
1
+ ---
2
+ id: html/map-id-name
3
+ title: "A map's id and name match"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "which of the two names is right depends on what refers to the map"
7
+ stability: preview
8
+ ported_from: [html-validate/map-id-name]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ When a `<map>` has both `id` and `name`, they are the same.
13
+
14
+ **Why:** HTML requires them to match; tools that look maps up by `id` and browsers that use `name` would find different maps.
15
+
16
+ ```codelaw
17
+ native: html/map-id-name
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Make `id` and `name` equal, and update references to the one you changed.
23
+
24
+ ## Examples
25
+
26
+ ### HTML
27
+
28
+ #### Don't
29
+
30
+ ```html
31
+ <map id="nav" name="menu"></map>
32
+ ```
33
+
34
+ #### Do
35
+
36
+ ```html
37
+ <map id="nav" name="nav"></map>
38
+ ```
@@ -0,0 +1,42 @@
1
+ ---
2
+ id: html/meta-refresh
3
+ title: "Meta refresh redirects at once"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "redirecting at once or removing the refresh changes the page's behaviour"
7
+ stability: preview
8
+ options:
9
+ allowLongDelay: { type: "boolean", default: false, description: "allow delays over 20 hours (72,000 seconds)" }
10
+ ported_from: [html-validate/meta-refresh]
11
+ tags: [a11y]
12
+ since: 0.1.0
13
+ ---
14
+
15
+ A `<meta http-equiv="refresh">` redirects immediately (`0;url=…`); it doesn't wait, and doesn't reload the page.
16
+
17
+ **Why:** A timed refresh moves on before slower readers and screen-reader users have finished; a reload without a URL repeats forever.
18
+
19
+ ```codelaw
20
+ native: html/meta-refresh
21
+ ```
22
+
23
+ ## How to fix
24
+
25
+ 1. Make the refresh instant with `0;url=…`.
26
+ 2. Or replace it with a link the user follows when ready.
27
+
28
+ ## Examples
29
+
30
+ ### HTML
31
+
32
+ #### Don't
33
+
34
+ ```html
35
+ <meta http-equiv="refresh" content="5;url=/next">
36
+ ```
37
+
38
+ #### Do
39
+
40
+ ```html
41
+ <meta http-equiv="refresh" content="0;url=/next">
42
+ ```
@@ -0,0 +1,24 @@
1
+ # html/missing-doctype — cases
2
+
3
+ ### case: a fragment is not a document
4
+
5
+ ```html
6
+ <div>
7
+ <p>Partial</p>
8
+ </div>
9
+ ```
10
+
11
+ ```expect
12
+ (none)
13
+ ```
14
+
15
+ ### case: a malformed doctype still counts
16
+
17
+ ```html
18
+ <!DOCTYPE>
19
+ <html lang="en"></html>
20
+ ```
21
+
22
+ ```expect
23
+ (none)
24
+ ```
@@ -0,0 +1,43 @@
1
+ ---
2
+ id: html/missing-doctype
3
+ title: "The document has a doctype"
4
+ languages: [html]
5
+ fix: suggest
6
+ fix_reason: "leaving quirks mode can change the page's layout"
7
+ stability: preview
8
+ ported_from: [html-validate/missing-doctype]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ A document starts with `<!DOCTYPE html>`.
13
+
14
+ **Why:** Without it browsers render in quirks mode, which lays the page out differently from the standard.
15
+
16
+ ```codelaw
17
+ native: html/missing-doctype
18
+ ```
19
+
20
+ ## Examples
21
+
22
+ ### HTML
23
+
24
+ #### Don't
25
+
26
+ ```html
27
+ <html lang="en">
28
+ <title>Home</title>
29
+ </html>
30
+ ```
31
+
32
+ #### Do
33
+
34
+ ```html
35
+ <!DOCTYPE html>
36
+ <html lang="en">
37
+ <title>Home</title>
38
+ </html>
39
+ ```
40
+
41
+ ## Differences from the source
42
+
43
+ Only files with an `<html>` element are checked: a fragment or template partial is not a document, and gets no doctype of its own.
@@ -0,0 +1,40 @@
1
+ ---
2
+ id: html/multiple-labeled-controls
3
+ title: "A label labels one control"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "each control needs its own label text"
7
+ stability: preview
8
+ ported_from: [html-validate/multiple-labeled-controls]
9
+ tags: [a11y]
10
+ since: 0.1.0
11
+ ---
12
+
13
+ A `<label>` is associated with at most one control, by nesting or by `for`.
14
+
15
+ **Why:** A label names one control; assistive technology announces the others without the right name.
16
+
17
+ ```codelaw
18
+ native: html/multiple-labeled-controls
19
+ ```
20
+
21
+ ## How to fix
22
+
23
+ 1. Give each control its own `<label>`.
24
+
25
+ ## Examples
26
+
27
+ ### HTML
28
+
29
+ #### Don't
30
+
31
+ ```html
32
+ <label>Name <input type="text" name="first"> <input type="text" name="last"></label>
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```html
38
+ <label>First name <input type="text" name="first"></label>
39
+ <label>Last name <input type="text" name="last"></label>
40
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ id: html/no-abstract-role
3
+ title: "No abstract ARIA roles"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "the concrete role depends on what the element is"
7
+ stability: preview
8
+ ported_from: [html-validate/no-abstract-role]
9
+ tags: [a11y]
10
+ since: 0.1.0
11
+ ---
12
+
13
+ `role` does not name one of ARIA's abstract roles (`widget`, `input`, `landmark`, `section`, `command`, …).
14
+
15
+ **Why:** Abstract roles only organise the ARIA specification; browsers don't map them, so the element gets no role at all.
16
+
17
+ ```codelaw
18
+ native: html/no-abstract-role
19
+ ```
20
+
21
+ ## How to fix
22
+
23
+ 1. Use the concrete role that describes the element, or a native element.
24
+
25
+ ## Examples
26
+
27
+ ### HTML
28
+
29
+ #### Don't
30
+
31
+ ```html
32
+ <div role="landmark">News</div>
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```html
38
+ <div role="region" aria-label="News">News</div>
39
+ ```
@@ -0,0 +1,37 @@
1
+ ---
2
+ id: html/no-autoplay
3
+ title: "Media does not play by itself"
4
+ languages: [html]
5
+ fix: suggest
6
+ fix_reason: "the media no longer starts on load"
7
+ stability: preview
8
+ options:
9
+ include: { type: "string[]", default: ["audio", "video"], description: "elements on which `autoplay` is reported" }
10
+ ported_from: [html-validate/no-autoplay]
11
+ tags: [a11y]
12
+ since: 0.1.0
13
+ ---
14
+
15
+ `<audio>` and `<video>` don't have `autoplay`.
16
+
17
+ **Why:** Sound that starts by itself talks over screen readers and startles users; WCAG asks that users control playback.
18
+
19
+ ```codelaw
20
+ native: html/no-autoplay
21
+ ```
22
+
23
+ ## Examples
24
+
25
+ ### HTML
26
+
27
+ #### Don't
28
+
29
+ ```html
30
+ <video src="intro.mp4" autoplay controls></video>
31
+ ```
32
+
33
+ #### Do
34
+
35
+ ```html
36
+ <video src="intro.mp4" controls></video>
37
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ id: html/no-conditional-comment
3
+ title: "No Internet Explorer conditional comments"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "whether the markup inside is still needed is the author's call"
7
+ stability: preview
8
+ ported_from: [html-validate/no-conditional-comment]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ The document has no conditional comments (`<!--[if IE]>…<![endif]-->`).
13
+
14
+ **Why:** Only Internet Explorer 9 and older read them; every current browser treats them as comments, so the markup inside is dead.
15
+
16
+ ```codelaw
17
+ native: html/no-conditional-comment
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Delete the conditional comment, or move markup that is still needed out of it.
23
+
24
+ ## Examples
25
+
26
+ ### HTML
27
+
28
+ #### Don't
29
+
30
+ ```html
31
+ <!--[if lt IE 9]><script src="html5shiv.js"></script><![endif]-->
32
+ <p>Hello</p>
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```html
38
+ <p>Hello</p>
39
+ ```
@@ -0,0 +1,39 @@
1
+ ---
2
+ id: html/no-deprecated-attr
3
+ title: "No obsolete attributes"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "the replacement is usually CSS, which needs a class and a rule"
7
+ stability: preview
8
+ ported_from: [html-validate/no-deprecated-attr]
9
+ since: 0.1.0
10
+ ---
11
+
12
+ Elements don't use attributes HTML has made obsolete, such as `align`, `bgcolor`, `border` on `<img>`, or `name` on `<a>`.
13
+
14
+ **Why:** Browsers support them unevenly; CSS and current attributes do the same job reliably.
15
+
16
+ ```codelaw
17
+ native: html/no-deprecated-attr
18
+ ```
19
+
20
+ ## How to fix
21
+
22
+ 1. Replace presentational attributes with a class and CSS.
23
+ 2. Replace other obsolete attributes with their current equivalent (for example `id` for `<a name>`).
24
+
25
+ ## Examples
26
+
27
+ ### HTML
28
+
29
+ #### Don't
30
+
31
+ ```html
32
+ <img src="a.png" alt="Chart" align="left">
33
+ ```
34
+
35
+ #### Do
36
+
37
+ ```html
38
+ <img src="a.png" alt="Chart" class="float-left">
39
+ ```
@@ -0,0 +1,37 @@
1
+ ---
2
+ id: html/no-dup-attr
3
+ title: "No attribute written twice"
4
+ languages: [html]
5
+ fix: auto
6
+ stability: preview
7
+ ported_from: [html-validate/no-dup-attr, htmlhint/attr-no-duplication]
8
+ since: 0.1.0
9
+ ---
10
+
11
+ An element does not have the same attribute twice (names compare without case).
12
+
13
+ **Why:** Browsers keep the first one and silently drop the rest, which is rarely what the author meant.
14
+
15
+ ```codelaw
16
+ native: html/no-dup-attr
17
+ ```
18
+
19
+ ## Examples
20
+
21
+ ### HTML
22
+
23
+ #### Don't
24
+
25
+ ```html
26
+ <input type="text" name="q" name="search">
27
+ ```
28
+
29
+ #### Do
30
+
31
+ ```html
32
+ <input type="text" name="q">
33
+ ```
34
+
35
+ ## Differences from the source
36
+
37
+ The fix removes the later copies, keeping what browsers already use. Check that the first value is the one you meant.
@@ -0,0 +1,33 @@
1
+ ---
2
+ id: html/no-dup-class
3
+ title: "No class listed twice"
4
+ languages: [html]
5
+ fix: auto
6
+ stability: preview
7
+ ported_from: [html-validate/no-dup-class]
8
+ since: 0.1.0
9
+ ---
10
+
11
+ A `class` attribute lists each class once.
12
+
13
+ **Why:** The repeat does nothing, and suggests a merge went wrong.
14
+
15
+ ```codelaw
16
+ native: html/no-dup-class
17
+ ```
18
+
19
+ ## Examples
20
+
21
+ ### HTML
22
+
23
+ #### Don't
24
+
25
+ ```html
26
+ <p class="lead lead">Text</p>
27
+ ```
28
+
29
+ #### Do
30
+
31
+ ```html
32
+ <p class="lead">Text</p>
33
+ ```
@@ -0,0 +1,23 @@
1
+ # html/no-duplicate-id — cases
2
+
3
+ ### case: ids in separate templates do not clash
4
+
5
+ ```html
6
+ <template><p id="a"></p></template>
7
+ <template><p id="a"></p></template>
8
+ ```
9
+
10
+ ```expect
11
+ (none)
12
+ ```
13
+
14
+ ### case: template code is not compared
15
+
16
+ ```html
17
+ <p id="{{ id }}"></p>
18
+ <p id="{{ id }}"></p>
19
+ ```
20
+
21
+ ```expect
22
+ (none)
23
+ ```
@@ -0,0 +1,42 @@
1
+ ---
2
+ id: html/no-duplicate-id
3
+ title: "Ids are unique in the document"
4
+ languages: [html]
5
+ fix: manual
6
+ fix_reason: "renaming an id breaks the CSS, scripts and links that use it"
7
+ stability: preview
8
+ ported_from: [html-validate/no-dup-id, htmlhint/id-unique]
9
+ tags: [a11y]
10
+ since: 0.1.0
11
+ ---
12
+
13
+ No two elements in the document have the same `id`. Ids inside a `<template>` only need to be unique within it.
14
+
15
+ **Why:** `getElementById`, `label for`, `aria-labelledby` and fragment links all find the first element with the id and miss the others.
16
+
17
+ ```codelaw
18
+ native: html/no-duplicate-id
19
+ ```
20
+
21
+ ## How to fix
22
+
23
+ 1. Decide which element keeps the id.
24
+ 2. Rename the other one, and update the CSS, scripts, `for`, `aria-*` and links that refer to it.
25
+
26
+ ## Examples
27
+
28
+ ### HTML
29
+
30
+ #### Don't
31
+
32
+ ```html
33
+ <h2 id="intro">Intro</h2>
34
+ <p id="intro">Text</p>
35
+ ```
36
+
37
+ #### Do
38
+
39
+ ```html
40
+ <h2 id="intro">Intro</h2>
41
+ <p id="intro-text">Text</p>
42
+ ```