在設計系統和元件程式庫中使用自訂屬性的好處。
我是 Dave,是 Nordhealth 的前端資深開發人員。我負責設計系統 Nord 的設計及開發工作,其中包括打造元件庫適用的 Web 元件。我想與您分享如何運用 CSS 自訂屬性,解決網頁元件樣式的相關問題,以及在設計系統和元件程式庫中使用自訂屬性的其他好處。
如何建構網頁元件
為了建構網頁元件,我們使用 Lit 這個程式庫提供許多樣板程式碼,例如狀態、範圍樣式和範本等。這不僅是 Lit 輕量,它也有以原生 JavaScript API 建構,這意味著我們能夠提供一組精簡的程式碼,以運用瀏覽器既有的功能。
但「網頁元件」最棒的一點就是可與幾乎任何現有的 JavaScript 架構搭配使用,甚至完全沒有任何架構。一旦網頁中參照了主要 JavaScript 套件,使用網頁元件就會非常類似使用原生 HTML 元素。唯一的明顯特徵是標記中一致的連字號,這是向瀏覽器表示這是網頁元件的標準。
Shadow DOM 樣式封裝
就像原生 HTML 元素有 Shadow DOM 一樣,Web 元件也有。Shadow DOM 是元素內部隱藏的節點樹狀結構。如要將這項功能視覺化,最佳做法是開啟網頁檢查器,並開啟「Show Shadow DOM tree」選項。完成後,請嘗試在檢查器中查看原生輸入元素,您現在可以選擇開啟該輸入內容,並查看其中的所有元素。您也可以在我們的網頁元件中嘗試這個方法,嘗試檢查我們的自訂輸入元件以查看其 Shadow DOM。
Shadow DOM 的優點 (或缺點,視您的觀點而定) 之一是樣式封裝。如果您在網頁元件中編寫 CSS,這些樣式並不會外洩,並影響主頁面或其他元素,因此完全包含在元件中。此外,為主頁面或父項 Web 元件編寫的 CSS 無法流入 Web 元件。
這種樣式封裝方式是元件程式庫的優點。這樣一來,無論父項網頁套用的樣式為何,使用者在使用我們的元件時,都能看到我們預期的樣貌,這點我們更有把握。為了進一步確保,我們會在所有網頁元件的根目錄中新增 all: unset;
,或稱「主機」。
不過,如果使用您 Web 元件的使用者有正當理由變更特定樣式,該怎麼辦呢?也許有一段文字因內容而需要更強的對比度,或是邊框需要加粗?如果元件中沒有任何樣式,該如何解鎖這些樣式選項?
這時 CSS 自訂屬性就能派上用場。
CSS 自訂屬性
自訂屬性的名稱正確,也就是可以完全命名的 CSS 屬性,然後套用所需的值。唯一的規定是,您必須在這些字串前面加上兩個連字號。宣告自訂屬性後,就可以透過 var()
函式在 CSS 中使用這個值。
在繼承方面,所有自訂屬性都會沿用,並遵循一般 CSS 屬性和值的典型行為。任何套用至父項元素或元素本身的自訂屬性,都可以用於其他屬性的值。我們透過 CSS 架構將自訂屬性套用至根元素,過度使用自訂屬性。這表示網頁上的所有元素都可以使用這些權杖值,無論是 Web 元件、CSS 輔助類別,或是希望從權杖清單中擷取值的開發人員,都可以使用這些權杖值。
透過使用 var()
函式繼承自訂屬性的功能,就是我們透過 Web 元件的 Shadow DOM 跳轉,可讓開發人員更精細地控制元件樣式設定。
Nord Web 元件中的自訂屬性
無論我們為設計系統開發哪個元件,都會針對 CSS 採取周全的做法,希望能提供精簡且可輕鬆維護的程式碼。我們在根元素的 CSS 主架構中,將設計符記定義為自訂屬性。
接著,我們的元件會參照這些權杖值。在某些情況下,我們會將這個值直接套用在 CSS 屬性上,但對於其他屬性,我們會定義新的內容比對自訂屬性,並套用至該屬性。
我們也會抽象化一些特定於元件但不在符記中的值,並將這些值轉換為內容相關的自訂屬性。與元件相關聯的自訂屬性可提供兩項重要優勢。首先,這表示我們可以更「乾淨」地使用 CSS,因為該值可套用至元件中的多個屬性。
其次,它可讓元件狀態和變化版本的變更更加簡潔。舉例來說,當您為懸停或有效狀態 (或在本例中為變化版本) 設定樣式時,只需變更自訂屬性即可更新所有這些屬性。
但在元件中定義這些情境式自訂屬性時,最具優勢,我們會為每個元件建立一種自訂 CSS API,供該元件的使用者使用。
上述範例為我們的其中一個網頁元件,其中含有情境式自訂屬性透過選取器變更。這整個方法的結果是元件,可為使用者提供足夠的樣式彈性,同時仍保有大部分實際樣式。此外,元件開發人員還能攔截使用者套用的樣式。如果想要調整或擴充其中一項屬性,使用者不必變更任何程式碼,
我們發現這個做法非常強大,不僅能造福我們設計系統元件的建立者,對開發團隊而言,也在產品中使用這些元件。
進一步使用自訂屬性
在撰寫本文時,我們並未在說明文件中揭露這些內容相關的自訂屬性,但我們計畫將這些屬性公開,讓更多開發團隊能夠瞭解並運用這些屬性。我們的元件會封裝在 npm 上與資訊清單檔案,其中包含所有須知資訊。接著,我們會在部署說明文件網站時,將資訊清單檔案當作資料使用,這項作業會使用 Eleventy 及其全域資料功能完成。我們計劃在這個資訊清單檔案資料檔案中加入這些情境自訂屬性。
我們希望改善的另一個領域,是這些內容相關自訂屬性如何繼承值。目前,假設您想調整兩個分隔線元件的顏色,就必須特別使用選取器指定兩個元件,或是使用樣式屬性,直接在元素上套用自訂屬性。這似乎沒什麼問題,但如果開發人員可以在容納元素或根層級定義這些樣式,會更有幫助。
您必須在元件上直接設定自訂屬性值,這是因為我們要透過元件主機選取器,在相同元素上定義自訂屬性值。我們直接在元件中使用的全域設計權杖會直接傳遞,不會受此問題影響,甚至可能會在父項元素遭到攔截。如何兼具兩種系統的優點?
私人和公開自訂屬性
私人自訂屬性是由 Lea Verou 組成,這是元件本身的結構定義「私人」自訂屬性,但設為包含備用的「公開」自訂屬性。
以這個方式定義內容相關自訂屬性後,我們仍然可以執行之前做的所有動作,例如沿用全域符記值,以及在元件程式碼中重複使用值;不過,元件也會優雅地繼承該屬性本身或任何父項元素的新定義。
雖然可能被認定這個做法並不完全「私人」,但我們仍認為這是一項優雅的解決方案,會讓我們擔心的問題。我們會在適當時機在元件中解決這個問題,讓開發團隊能進一步控管元件用途,同時仍能享有我們已建立的防護機制。
希望這份深入分析讓您瞭解我們如何將網頁元件與 CSS 自訂屬性搭配使用。歡迎與我們分享你的想法,如果你決定在自己的作品中使用其中任何一種方法,歡迎在 Twitter 上找我 @DavidDarnes。您也可以前往 Twitter 尋找 Nordhealth @NordhealthHQ,以及我們其他致力整合這個設計系統的團隊,他們致力整合這個設計系統,並執行本文提到的功能:@Viljamis、@WickyNilliams 和 @eric_habich。
主頁橫幅圖片由 Dan Cristian Pădureț 提供