在設計系統和元件程式庫中使用自訂屬性的優點。
我是 Dave,是 Nordhealth 的資深前端開發人員。我負責設計及開發設計系統 Nord,其中包括為元件程式庫建構網頁元件。我想分享我們如何運用 CSS 自訂屬性來設定網頁元件樣式的相關問題,以及在設計系統和元件程式庫中使用自訂屬性的其他好處。
我們如何建構網頁元件
我們使用 Lit 建構我們的網頁元件。Lit 是提供許多樣板程式碼的程式庫,例如狀態、範圍樣式、範本等。這不僅是 Lit 輕量級,它也建立在原生 JavaScript API 上,這表示我們可以運用瀏覽器既有的功能,提供精簡的程式碼組合。
不過,網頁元件最吸引人的一點是,這些元件幾乎可以與任何現有的 JavaScript 架構搭配使用,甚至完全沒有架構。在網頁上參照主要 JavaScript 套件後,使用 Web 元件的方法就和使用原生 HTML 元素非常類似。唯一有效的警示標誌是,它不是原生 HTML 元素,就是代碼內一致的連字號,這是向瀏覽器表明這是網頁元件的標準。
陰影 DOM 樣式封裝
就像原生 HTML 元素具有 Shadow DOM 一樣,Web 元件也是如此。陰影 DOM 是元素中節點的隱藏樹狀結構。視覺化呈現相關數據的最佳方式,就是開啟網頁檢查器,並開啟「顯示陰影 DOM 樹狀結構」選項。完成這項作業後,請嘗試在檢查器中查看原生輸入元素,您現在就可以選擇開啟該輸入項目,並查看其中的所有元素。您甚至可以使用我們的其中一個網頁元件嘗試這項功能,請嘗試檢查我們的自訂輸入元件,查看其 Shadow DOM。
使用 Shadow DOM 的其中一項優點 (或缺點,視你的前景而定) 就是樣式封裝。當您在網頁元件中編寫 CSS 時,這些樣式就無法外洩並影響主頁面或其他元素,而是完全包含在元件中。此外,為主頁面或上層 Web 元件編寫的 CSS 無法外洩至網頁元件。
這種樣式封裝是元件程式庫的優點。這可讓我們進一步保證,當有人使用我們的元件時,無論上層頁面套用的樣式為何,該元件都會正常顯示。為進一步確保,我們會將 all: unset;
新增至所有網頁元件的根層級 (或稱「主機」)。
不過,如果使用者使用您的網頁元件有正當理由變更某些樣式,該怎麼辦?也許某一行文字是因為上下文而需要更高的對比度,或是邊框可能更粗。如果所有樣式都無法存取元件,該如何解鎖這些樣式選項?
這時 CSS 自訂屬性就能派上用場。
CSS 自訂屬性
自訂屬性的名稱非常合適,也就是可完全自行命名的 CSS 屬性,並視需要套用任何值。唯一的條件是必須在前面加上兩個連字號。宣告自訂屬性後,即可透過 var()
函式在 CSS 中使用該值。
如果要繼承,所有自訂屬性都將沿用,並遵循一般 CSS 屬性和值的典型行為。任何套用至父項元素或元素本身的自訂屬性,都可以做為其他屬性的值使用。我們會透過 CSS 架構將自訂屬性套用到根元素,藉此大量將自訂屬性用於設計權杖。也就是說,網頁上的所有元素皆可使用這些權杖值,無論是 Web 元件、CSS 輔助類別,還是開發人員,都能從權杖清單中擷取值。
運用 var()
函式沿用自訂屬性的功能,就是我們運用網頁元件的 Shadow DOM 展開作業,讓開發人員能更精細地控制元件樣式設定。
Nord Web 元件中的自訂屬性
每次為設計系統開發元件時,我們都會對 CSS 供應商採取深思熟慮,同時確保程式碼精簡且易於維護。我們在主要 CSS 架構中,將設計權杖定義為根元素上的自訂屬性。
接著,我們會在元件中參照這些權杖值。在某些情況下,我們會直接在 CSS 屬性上套用這個值,但在其他情況下,我們會定義新的內容相關自訂屬性,並將值套用至該屬性。
我們也會將某些專屬元件專用的值 (不在符記中) 抽取出來,然後轉換成內容相關「自訂屬性」。與元件相關的自訂屬性為我們帶來兩項主要優點。首先,這表示我們能夠與 CSS 「乾燥」,因為這個值可以套用至元件中的多個屬性。
其次,這會讓元件狀態和變化版本的變更更加簡潔,因為如果要設定懸停或有效狀態樣式 (在本例中是變化版本) 時,只需要變更自訂屬性,就能更新所有屬性。
但最強大的優點在於,我們為元件定義這些關聯自訂屬性時,會為每個元件建立一種自訂 CSS API,而該元件的使用者只要使用該 API。
上例是其中一個具有內容相關自訂屬性的網頁元件,透過選擇器變更。這整個做法的結果是一個元件,可為使用者提供足夠的樣式靈活性,同時仍保留大部分實際樣式。此外,元件開發人員還能攔截使用者套用的樣式。如果希望調整或擴充這些屬性,我們無需變更任何程式碼。
我們發現,這個做法極為強大,不僅對設計系統元件的製作者來說,也對開發團隊而言,能夠在我們的產品中使用這些元件。
進一步使用自訂屬性
我們撰寫本文件時,並不會實際在我們的說明文件中提供這些情境自訂屬性;不過,我們計劃讓廣大的開發團隊瞭解並利用這些屬性。我們的元件會以資訊清單檔案的形式在 npm 上封裝,這個檔案會包含所有須知事項。接著,我們會在部署說明文件網站時,使用資訊清單檔案做為資料,而使用 Eleventy 和其全域資料功能達成此目的。我們預計將這些內容比對自訂屬性加入資訊清單檔案資料檔案。
另一個想要改善的部分,是這些內容比對自訂屬性繼承值的方式。以目前來說,如果您想調整兩個分隔線元件的顏色,就需要使用選取器特別指定這兩個元件的顏色,或是使用樣式屬性,直接在元素上套用自訂屬性。雖然這看起來沒問題,但如果開發人員能在包含的元素 (甚至是根層級) 定義這些樣式,那麼會更加實用。
您必須直接在元件上設定自訂屬性值,這是因為我們透過元件主機選取器,在同一個元素上定義自訂屬性值。我們在元件中直接使用的全域設計權杖會直接傳遞、不受此問題影響,甚至可能會攔截父項元素。如何才能同時兼顧這兩個世界?
私人和公開自訂屬性
私人自訂屬性是由 Lea Verou 一併提供的,也就是在元件上根據情境設定的「私人」自訂屬性,但設為具有備用選項的「公開」自訂屬性。
以這種方式定義內容關聯自訂屬性,表示我們仍可執行之前執行的所有操作,例如繼承全域權杖值以及在整個元件程式碼中重複使用值,但元件也會優雅地繼承該屬性本身或任何父項元素的新定義。
儘管此方法可能不是所謂的「私人」解決方案,但我們仍認為這是一個比較典雅的解決方案,以便解決我們所擔心的問題。如果有機會,我們會在元件中解決這個問題,讓開發團隊進一步控管元件使用方式,同時仍享有既有的防護機制。
希望以上說明能讓您深入瞭解我們如何運用搭配 CSS 自訂屬性的網頁元件。歡迎與我們分享你的看法。如果您決定使用上述任一種方法,歡迎透過 Twitter (@DavidDarnes) 與我們聯繫。您也可以在 Twitter 中找到 Nordhealth @NordhealthHQ,以及我們的其他團隊成員,他們負責整合這個設計系統並執行以下文章中提及的功能:@Viljamis、@WickyNilliams 和 @eric_habich。