diff --git a/javascript/packages/linter/docs/rules/README.md b/javascript/packages/linter/docs/rules/README.md
index 08503a87a..f8d511966 100644
--- a/javascript/packages/linter/docs/rules/README.md
+++ b/javascript/packages/linter/docs/rules/README.md
@@ -26,6 +26,7 @@ This page contains documentation for all Herb Linter rules.
- [`html-avoid-both-disabled-and-aria-disabled`](./html-avoid-both-disabled-and-aria-disabled.md) - Avoid using both `disabled` and `aria-disabled` attributes
- [`html-body-only-elements`](./html-body-only-elements.md) - Require content elements inside `
`.
- [`html-boolean-attributes-no-value`](./html-boolean-attributes-no-value.md) - Prevents values on boolean attributes
+- [`html-head-only-elements`](./html-head-only-elements.md) - Require head-scoped elements inside ``.
- [`html-iframe-has-title`](./html-iframe-has-title.md) - `iframe` elements must have a `title` attribute
- [`html-input-require-autocomplete`](./html-input-require-autocomplete.md) - Require `autocomplete` attributes on `` tags.
- [`html-img-require-alt`](./html-img-require-alt.md) - Requires `alt` attributes on `` tags
diff --git a/javascript/packages/linter/docs/rules/html-head-only-elements.md b/javascript/packages/linter/docs/rules/html-head-only-elements.md
new file mode 100644
index 000000000..8a12b47f8
--- /dev/null
+++ b/javascript/packages/linter/docs/rules/html-head-only-elements.md
@@ -0,0 +1,81 @@
+# Linter Rule: Require head-scoped elements inside ``
+
+**Rule:** `html-head-only-elements`
+
+## Description
+
+Enforce that certain elements only appear inside the `` section of the document.
+
+Elements like ``, ``, ``, ``, and `
+
+
+
+
+
+
+ `)
+ })
+
+ test("fails when title is in body", () => {
+ expectError("Element `` must be placed inside the `` tag.")
+
+ assertOffenses(dedent`
+
+
+
+
+ My Page
+
Welcome
+
+
+ `)
+ })
+
+ test("fails when meta is in body", () => {
+ expectError("Element `` must be placed inside the `` tag.")
+
+ assertOffenses(dedent`
+
+
+
+
+
+
Welcome
+
+
+ `)
+ })
+
+ test("fails when link is in body", () => {
+ expectError("Element `` must be placed inside the `` tag.")
+
+ assertOffenses(dedent`
+
+
+
+
+
+
Welcome
+
+
+ `)
+ })
+
+ test("fails when style is in body", () => {
+ expectError("Element `
+
Welcome
+
+
+ `)
+ })
+
+ test("fails when base is in body", () => {
+ expectError("Element `` must be placed inside the `` tag.")
+
+ assertOffenses(dedent`
+
+
+
+
+
+
Welcome
+
+
+ `)
+ })
+
+ test("fails for multiple head-only elements in body", () => {
+ expectError("Element `` must be placed inside the `` tag.")
+ expectError("Element `` must be placed inside the `` tag.")
+ expectError("Element `` must be placed inside the `` tag.")
+
+ assertOffenses(dedent`
+
+
+
+
+ My Page
+
+
+
Welcome
+
+
+ `)
+ })
+
+ test("fails when elements are outside html structure", () => {
+ expectError("Element `` must be placed inside the `` tag.")
+ expectError("Element `` must be placed inside the `` tag.")
+
+ assertOffenses(dedent`
+ My Page
+
+
+
+
+
+
+