HTML 規格允許 body 內的 style,條件是 @scope
WHATWG HTML Standard · commit 877d614,關閉 issue #12951(2026-10-09)
WHATWG HTML 規格不再一律禁止 <style> 出現在 <body> 內:只要樣式表全部被包進 @scope,style 就可以當作 flow content 使用。這取代了過去「style 只能放在 head(metadata content)」的單一規則。
變更由 zcorpan 提交,commit 為 877d614,標題 "Allow style in body",訊息寫著 "Fixes #12951.",issue 於 2026 年 10 月 9 日以 PR #13007 結案。
原本的問題
在改動之前,<div><style>…</style></div> 這種寫法會被一致性檢查器(conformance checker)判為不合法。但 issue #12951 的提出者 o-t-w 指出,CSS Cascading and Inheritance Level 6 的 @scope 範例本身就把 style 放在 div 裡,而且「這段程式碼在所有瀏覽器都能運作」。規格與實務、與 CSS 自家範例互相打架。
反對者的理由是效能。zcorpan 在討論中說明:改變較早元素或祖先的樣式會造成 style invalidation,可能需要重算 layout,也可能出現未套樣式內容的閃爍(flash of unstyled content)。因此他把 body 內的 style 稱為「仍然是 footgun」。issue 並沒有提出實測數據;HansBrende 甚至重提 #1605 中沒人回答的問題:有沒有證據顯示它在真實網站上顯著影響效能。
規格細節
折衷方案是讓 style 只能影響「自己的父元素與其後的後代」,這樣就不會回頭改動已經渲染的內容。commit 訊息列出的條件:
- style 必須出現在「預期 flow content 的位置,且為其父元素的第一個子節點」。
- 樣式表不得含有
@import。 - 每條 style rule(以及帶有
<scope-start>的@scoperule)都必須位於不帶前綴的@scoperule 之內。 @mixin或@supports-condition內部的 rule 不受此限。
規格新增的說明指出,scoping root 就是 style 元素的父元素,所以影響範圍被限制在父元素與其後的後代。內容模型表也跟著改:style 的類別多了 flow,contexts 欄多了 flow*。
| 改動前 | 改動後 | |
|---|---|---|
| 內容類別 | metadata content | metadata content 與 flow content |
| body 內使用 | 不合法 | 合法,但需滿足 @scope 條件 |
@import | 不適用 | body 內的 style 不得使用 |
規格中的範例是一個 article 片段,對照如下:
<!-- 合法 -->
<article>
<style>
@scope { h2 { margin: 0; } }
</style>
<h2>標題</h2>
</article>
<!-- 不合法:h2 規則在 @scope 之外 -->
<style> h2 { margin: 0; } </style>影響範圍
寫元件式前端的人(Web Components、模板引擎、CMS 區塊)受益最直接:元件模板可以把 @scope 樣式與標記放在一起,不必再為了通過檢查把樣式搬到 head 或繞道使用其他技巧。反之,原本在 body 內放全域規則的程式碼仍然不合法,要改成包進 @scope。
維護 HTML 驗證流程的團隊要留意:驗證器端的對應變更(validator/validator #2143)在 issue 中被提到,並有一個 9 月 27 日的 commit,但這些內容我沒有逐一查證;升級前請以實際版本的行為為準。
還有兩點本文所引用的來源沒有說明:commit 沒有修改 parser 的「in body」插入模式規則,所以解析行為不在這次改動內;各瀏覽器廠商的立場,issue 頁面也未提供。