作者定義的 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 屬性「不會」由陰影 DOM 封裝。相反地,它們是用於在不同陰影樹之間傳遞參數的常用方式。這會讓 @property
描述元件變得特別:它應會像是文件全域型別宣告一樣運作,定義特定命名屬性的運作方式。由於屬性必須在陰影樹中比對,屬性宣告不相符會導致意外結果,因此 @property
宣告會依據文件順序指定為扁平化並解析。
規則應如何與 ::part
搭配運作
陰影元件會將陰影樹狀結構中的元素公開給其父項樹狀結構。這樣一來,父項樹狀結構就能存取該元素,並使用 ::part
元素為其設定樣式。
由於 ::part
允許兩個樹狀範圍為同一個元素設定樣式,因此指定了以下的層疊順序:
- 首先,請檢查陰影內容中的樣式。這是零件的「預設」樣式。
- 然後套用
::part
中定義的外部樣式。這是零件的「自訂」樣式。 - 接著,套用與
!important
一併定義的任何內部樣式。這可讓自訂元素宣告特定部分的特定屬性無法由::part
自訂。
這表示從 shadow DOM 中的名稱無法從 ::part
參照,因為 ::part
是主機範圍的樣式,而非 shadow 範圍的樣式。例如:
// 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 的目前互通狀態快照時,發現體驗不一致且有錯誤。我們在這裡檢查的功能全都在瀏覽器之間的行為不一致,也不符合規格。好消息是,要讓體驗一致,所需的差異只會是有限的錯誤和規格問題清單。讓我們一起解決這個問題! 在此同時,如果您遇到本文所述的資料不一致問題,這份總覽或許能提供協助。