作者定義的 CSS 名稱和陰影 DOM 應可搭配運作。不過,瀏覽器與規格不一致,有時彼此一致,且每個 CSS 名稱的方式也略有不同。
本文將說明作者定義的 CSS 名稱在陰影範圍中運作的方式,希望能做為指南,在近期內改善互通性。
什麼是作者定義的 CSS 名稱?
作者定義的 CSS 名稱是相對舊的 CSS 語法機制,最初為 @keyframes
規則導入,會將 <keyframe-name>
定義為自訂 ID 或字串。這個概念的目的是在樣式表中宣告某些內容,然後在其他部分參照。
/* "fade-in" is a CSS name, representing a set of keyframes */
@keyframes fade-in {
from { opacity: 0 };
to { opacity: 1 }
}
.card {
/* "fade-in" is a reference to the above keyframes */
animation-name: fade-in;
}
其他使用 CSS 名稱的 CSS 功能包括字型、屬性宣告、容器查詢,以及較新的檢視畫面轉場、錨點定位和捲動驅動動畫。下表列出 Chrome 會檢查狀態的名稱 (不含所有名稱)。
功能 | 名稱宣告 | 名稱參照 |
---|---|---|
主要畫面格 | @keyframes |
animation-name |
字型 | @font-face { }
@font-palette-values |
font-family
font-palette |
屬性宣告 | @property 任何未註冊的自訂屬性宣告 |
var() |
查看轉場效果 | view-transition-name
view-transition-class |
::view-transition-* 個虛擬元素 |
錨定定位 | anchor-name |
position-anchor |
捲動觸發的動畫 | view-timeline-name
scroll-timeline-name |
animation-timeline |
清單樣式 | @counter-style |
list-style |
計數器 | counter-reset
counter-set
counter-increment |
|
容器查詢 | container-name |
@container |
頁面 | page |
@page |
如表格所示,CSS 名稱通常會對應到 CSS 參照。例如,animation-name
是 @keyframes
名稱的參照。CSS 名稱與 DOM 中定義的名稱 (例如屬性和標記名稱) 不同,因為它們會在樣式表格中宣告及參照。
名稱與 Shadow DOM 的關聯
雖然 CSS 名稱的設計目的是在文件或樣式表單的不同部分之間建立關係,但 Shadow DOM 的設計目的則是相反。它會封裝關係,以免在應有自己命名空間的網頁元件之間發生資料外洩。
將 CSS 名稱和陰影 DOM 結合後,您應該會發現,組合網頁元件的體驗既靈活又穩定,既能充分表達,又能維持穩定性。
理論上來說,這麼做是正確的。實際上,瀏覽器在 CSS 名稱與 shadow DOM 互動的方式上並不一致,無論是同一個瀏覽器中的功能、不同瀏覽器之間,或是功能與規格之間都是如此。
名稱和陰影 DOM 的搭配運作方式
為了瞭解問題,建議您瞭解 CSS 的這些部分在理論上應如何搭配運作。
通則
CSS Scoping Level 1 規格定義了 CSS 名稱在陰影樹狀結構中的行為一般規則。總結來說,CSS 名稱在其定義的範圍內是全域的,也就是說,可以從子系別影像樹存取,但無法從同層或祖系影像樹存取。請注意,這與網路平台的名稱不同,例如在同一個樹狀結構範圍內封裝的元素 ID。
例外狀況:@property
與其他 CSS 名稱不同,CSS 屬性「不」以 shadow DOM 封裝。
相反地,它們是用於在不同陰影樹之間傳遞參數的常用方式。這會使 @property
描述元具有特殊性:其行為應像 Document-global 類型宣告一樣,以定義特定具名屬性的行為方式。由於屬性必須跨陰影樹狀結構相符,屬性宣告不相符會產生非預期的結果,因此必須指定 @property
宣告,讓系統根據文件順序加以整併並解決。
規則應如何與 ::part
搭配運作
陰影元件會將陰影樹狀結構中的元素公開給其父項樹狀結構。這樣一來,父項樹狀結構就能存取該元素,並使用 ::part
元素為其設定樣式。
由於 ::part
允許兩個樹狀結構範圍設定同一個元素的樣式,因此指定了以下串聯順序:
- 首先,請檢查陰影內容中的樣式。這是零件的「預設」樣式。
- 然後套用
::part
中定義的外部樣式。這就是該部分的「自訂」樣式。 - 然後套用搭配使用
!important
定義的所有內部樣式。這可讓自訂元素宣告特定部分的特定屬性無法由::part
自訂。
這表示在陰影 DOM 中的名稱無法從 ::part
參照,因為 ::part
是主機範圍樣式,而不是陰影範圍樣式。例如:
// inside the shadow DOM:
@keyframes fade-in {
from { opacity: 0}
}
// This shouldn't work!
// The host style shouldn't know the name "fade-in"
::part(slider) {
animation-name: fade-in;
}
規則應與內嵌樣式搭配運作的方式
與 ::part
不同,內嵌樣式 (含有 style
屬性) 或以程式輔助方式使用指令碼設定樣式的內嵌樣式,會以元素的範圍為範圍。這是因為您需要存取元素句柄,才能將樣式套用至元素,進而套用至陰影根。
CSS 名稱和陰影 DOM 在實際情況下如何搭配運作
雖然上述規則已明確定義且一致,但目前的實作方式不一定反映這項規則。實際運作時,@property
與規格在不同瀏覽器中的運作方式會有所不同,且大多數其他功能都有未解決的錯誤 (有些功能尚未發布,因此有時間修正)。
為了測試並展示這些功能在實際情況下如何運作,我們建立了以下網頁:https://css-names-in-the-shadow.glitch.me/。本頁面有多個 iframe,每個 iframe 分別用於其中一項功能,以及測試六個情境:
- 外部參照外部名稱:不涉及陰影 DOM,應該可正常運作。
- 外部參照內部名稱:這不應運作,因為這表示在陰影內容中定義的名稱已外洩。
- 內部參照外部名稱:這應該可行,因為樹狀範圍名稱會由陰影根繼承。
- 內部參照至內部名稱:這應該會運作,因為參照的名稱都位於相同範圍內。
::part
參照外部名稱:這應正常運作,因為::part
和名稱都是在相同範圍內宣告。::part
參照內部名稱:這不應該運作,因為外部範圍不應取得陰影 DOM 內宣告的名稱。
@keyframes
根據規格定義,只要 @keyframes
at-rule 位於祖系範圍內,您就能從陰影根目錄中參照主要畫面格名稱。實際上,瀏覽器不會實作這個行為,因此主要畫面格定義只能在定義的範圍中參照。請參閱問題 10540。
@property
如規格說明所定義,任何 @property
宣告都會扁平化至文件範圍。不過,目前在所有瀏覽器中,您只能在文件範圍內宣告 @property
,系統會忽略陰影根目錄中的 @property
宣告。
請參閱問題 10541。
瀏覽器專屬錯誤
其他功能在不同瀏覽器中顯示的行為不一致:
@font-face
會在 Safari 中扁平化為根範圍。- Chromium 不允許在陰影根目錄中繼承
anchor-name
規則 scroll-timeline-name
和view-timeline-name
在::part
(也包括在 Chromium 中) 上沒有正確的範圍。- 沒有任何瀏覽器允許在陰影根目錄中宣告
@font-palette-values
。 view-transition-class
可在陰影根目錄中定義 (轉場本身位於陰影根目錄之外)。- Firefox 可讓
::part
存取內部陰影名稱 (容器查詢、關鍵影格)。 - Firefox 和 Safari 不會遵循陰影根中的
@counter-style
。
請注意,counter-reset
、counter-set
、counter-increment
的規則略有不同,因為它們是隱含名稱,而宣告 CSS 屬性則有已建立且經過充分測試的一組規則。
結論
壞消息是,當我們檢查 CSS 名稱和 shadow DOM 的目前互通狀態快照時,發現體驗不一致且有錯誤。我們在這裡檢查的功能全都在瀏覽器之間的行為不一致,也不符合規格。好消息是,要讓體驗一致,差異只會是有限的錯誤和規格問題清單。讓我們一起解決這個問題! 在此同時,如果您遇到本文所述的資料不一致問題,這份總覽或許能提供協助。