Cara Nordhealth menggunakan properti kustom di Komponen Web

Manfaat menggunakan properti kustom dalam sistem desain dan library komponen.

David Darnes
David Darnes

Saya Dave dan saya adalah Developer Front-end Senior di Nordhealth. Saya mengerjakan desain dan pengembangan sistem desain Nord, yang mencakup pembuatan Komponen Web untuk library komponen kami. Saya ingin berbagi cara kami menyelesaikan masalah seputar penataan gaya Komponen Web menggunakan properti kustom CSS, dan beberapa manfaat lain dari penggunaan properti kustom dalam sistem desain dan library komponen.

Cara kami membangun Komponen Web

Untuk membangun Komponen Web, kami menggunakan Lit, library yang menyediakan banyak kode boilerplate seperti status, gaya tercakup, template, dan lainnya. Lit tidak hanya ringan, tetapi juga dibuat di API JavaScript native, yang berarti kami dapat mengirimkan paket kode yang ramping yang memanfaatkan fitur yang sudah dimiliki browser.


import {html, css, LitElement} from 'lit';

export class SimpleGreeting extends LitElement {
  static styles = css`:host { color: blue; font-family: sans-serif; }`;

  static properties = {
    name: {type: String},
  };

  constructor() {
    super();
    this.name = 'there';
  }

  render() {
    return html`

Hey ${this.name}, welcome to Web Components!

`
; } } customElements.define('simple-greeting', SimpleGreeting);
Komponen Web yang ditulis dengan Lit.

Namun, hal yang paling menarik dari Komponen Web adalah komponen ini berfungsi dengan hampir semua framework JavaScript yang ada, atau bahkan tanpa framework sama sekali. Setelah paket JavaScript utama direferensikan di halaman, penggunaan Komponen Web sangat mirip dengan penggunaan elemen HTML native. Satu-satunya tanda nyata bahwa elemen ini bukan elemen HTML native adalah tanda hubung yang konsisten dalam tag, yang merupakan standar untuk menandakan kepada browser bahwa elemen ini adalah Komponen Web.

Enkapsulasi gaya Shadow DOM

Sama seperti elemen HTML native yang memiliki Shadow DOM, Komponen Web juga memilikinya. Shadow DOM adalah hierarki node tersembunyi dalam sebuah elemen. Cara terbaik untuk memvisualisasikannya adalah dengan membuka web inspector dan mengaktifkan opsi "Show Shadow hierarki DOM". Setelah melakukannya, coba lihat elemen input native di pemeriksa—Anda kini akan memiliki opsi untuk membuka input tersebut dan melihat semua elemen di dalamnya. Anda bahkan dapat mencobanya dengan salah satu Komponen Web kami—coba periksa komponen input kustom kami untuk melihat Shadow DOM-nya.

Shadow DOM yang diperiksa di DevTools.
Contoh Shadow DOM dalam elemen input teks reguler dan dalam Komponen Web input Nord.

Salah satu keunggulan (atau kelemahan, bergantung pada pandangan Anda) Shadow DOM adalah enkapsulasi gaya. Jika Anda menulis CSS dalam Komponen Web, gaya tersebut tidak dapat bocor dan memengaruhi halaman utama atau elemen lainnya; gaya tersebut sepenuhnya berada di dalam komponen. Selain itu, CSS yang ditulis untuk halaman utama atau Komponen Web induk tidak dapat bocor ke Komponen Web Anda.

Enkapsulasi gaya ini merupakan manfaat dalam library komponen kami. Hal ini memberi kita jaminan lebih besar bahwa saat seseorang menggunakan salah satu komponen kita, komponen tersebut akan terlihat seperti yang kita inginkan, terlepas dari gaya yang diterapkan ke halaman induk. Dan untuk lebih memastikan, kita menambahkan all: unset; ke root, atau "host", dari semua Komponen Web kita.


:host {
  all: unset;
  display: block;
  box-sizing: border-box;
  text-align: start;
  /* ... */
}
Beberapa kode boilerplate komponen diterapkan ke root shadow, atau pemilih host.

Namun, bagaimana jika seseorang yang menggunakan Komponen Web Anda memiliki alasan yang sah untuk mengubah gaya tertentu? Mungkin ada baris teks yang memerlukan lebih banyak kontras karena konteksnya, atau batas yang perlu lebih tebal? Jika tidak ada gaya yang dapat masuk ke komponen Anda, bagaimana Anda dapat membuka opsi gaya tersebut?

Di sinilah properti kustom CSS berperan.

Properti kustom CSS

Properti kustom diberi nama yang sangat sesuai—properti ini adalah properti CSS yang dapat Anda beri nama sendiri sepenuhnya dan menerapkan nilai apa pun yang diperlukan. Satu-satunya persyaratan adalah Anda harus mengawalinya dengan dua tanda hubung. Setelah Anda mendeklarasikan properti kustom, nilainya dapat digunakan di CSS menggunakan fungsi var().


:root {
  --n-color-accent: rgb(53, 89, 199);
  /* ... */
}

.n-color-accent-text {
  color: var(--n-color-accent);
}
Contoh dari Framework CSS kami tentang token desain sebagai properti kustom dan penggunaannya pada class helper.

Dalam hal pewarisan, semua properti kustom diwariskan, yang mengikuti perilaku umum properti dan nilai CSS reguler. Setiap properti kustom yang diterapkan ke elemen induk, atau elemen itu sendiri, dapat digunakan sebagai nilai pada properti lain. Kami sering menggunakan properti kustom untuk token desain kami dengan menerapkannya ke elemen root menggunakan Framework CSS kami, yang berarti bahwa semua elemen di halaman dapat menggunakan nilai token ini, baik itu Komponen Web, class helper CSS, atau developer yang ingin mengambil nilai dari daftar token kami.

Kemampuan untuk mewarisi properti kustom, dengan penggunaan fungsi var(), adalah cara kita menembus Shadow DOM Komponen Web dan memungkinkan developer memiliki kontrol yang lebih mendetail saat menata gaya komponen kita.

Properti kustom di Nord Web Component

Setiap kali mengembangkan komponen untuk sistem desain, kami mengambil pendekatan yang cermat terhadap CSS-nya—kami ingin membuat kode yang ringkas, tetapi sangat mudah dikelola. Token desain yang kita miliki ditentukan sebagai properti kustom dalam Framework CSS utama kita pada elemen root.


:root {
  --n-space-m: 16px;
  --n-space-l: 24px;
  /* ... */
  --n-color-background: rgb(255, 255, 255);
  --n-color-border: rgb(216, 222, 228);
  /* ... */
}
Properti kustom CSS yang ditentukan pada pemilih root.

Nilai token ini kemudian direferensikan dalam komponen kita. Dalam beberapa kasus, kami akan menerapkan nilai langsung pada properti CSS, tetapi untuk kasus lain, kami akan menentukan properti kustom kontekstual baru dan menerapkan nilai ke properti tersebut.


:host {
  --n-tab-group-padding: 0;
  --n-tab-list-background: var(--n-color-background);
  --n-tab-list-border: inset 0 -1px 0 0 var(--n-color-border);
  /* ... */
}

.n-tab-group-list {
  box-shadow: var(--n-tab-list-border);
  background-color: var(--n-tab-list-background);
  gap: var(--n-space-s);
  /* ... */
}
Properti kustom yang ditentukan pada shadow root komponen, lalu digunakan dalam gaya komponen. Properti kustom dari daftar token desain juga digunakan.

Kita juga akan mengabstraksi beberapa nilai yang spesifik untuk komponen, tetapi tidak ada dalam token kita, dan mengubahnya menjadi properti kustom kontekstual. Properti kustom yang kontekstual dengan komponen memberi kita dua manfaat utama. Pertama, ini berarti kita dapat lebih "dry" dengan CSS karena nilai tersebut dapat diterapkan ke beberapa properti di dalam komponen.


.n-tab-group-list::before {
  /* ... */
  padding-inline-start: var(--n-tab-group-padding);
}

.n-tab-group-list::after {
  /* ... */
  padding-inline-end: var(--n-tab-group-padding);
}
Properti kustom kontekstual padding grup tab yang digunakan di beberapa tempat dalam kode komponen.

Kedua, perubahan status dan variasi komponen menjadi sangat rapi—hanya properti kustom yang perlu diubah untuk memperbarui semua properti tersebut saat, misalnya, Anda menata gaya status pengarahan kursor atau aktif atau, dalam hal ini, variasi.


:host([padding="l"]) {
  --n-tab-group-padding: var(--n-space-l);
}
Variasi komponen tab yang padding-nya diubah menggunakan satu update properti kustom, bukan beberapa update.

Namun, manfaat yang paling efektif adalah saat kita menentukan properti kustom kontekstual ini pada komponen, kita membuat semacam CSS API kustom untuk setiap komponen, yang dapat dimanfaatkan oleh pengguna komponen tersebut.


<nord-tab-group label="Title">
  <!-- ... -->
</nord-tab-group>

<style>
  nord-tab-group {
    --n-tab-group-padding: var(--n-space-xl);
  }
</style>
Menggunakan komponen grup tab di halaman dan memperbarui properti kustom padding ke ukuran yang lebih besar.

Contoh sebelumnya menunjukkan salah satu Komponen Web kami dengan properti kustom kontekstual yang diubah menggunakan pemilih. Hasil dari seluruh pendekatan ini adalah komponen yang memberikan fleksibilitas gaya yang cukup kepada pengguna, sekaligus tetap menjaga sebagian besar gaya sebenarnya. Selain itu, sebagai bonus, kita sebagai developer komponen memiliki kemampuan untuk mengintersepsi gaya yang diterapkan oleh pengguna. Jika ingin menyesuaikan atau memperluas salah satu properti tersebut, kita dapat melakukannya tanpa mengharuskan pengguna mengubah kode mereka.

Kami menganggap pendekatan ini sangat efektif, tidak hanya bagi kami sebagai pembuat komponen sistem desain, tetapi juga bagi tim pengembangan kami saat mereka menggunakan komponen ini dalam produk kami.

Mengembangkan properti kustom lebih lanjut

Saat menulis, kami tidak benar-benar mengungkapkan properti kustom kontekstual ini dalam dokumentasi kami; namun, kami berencana melakukannya agar tim pengembangan kami yang lebih luas dapat memahami dan memanfaatkan properti ini. Komponen kami dipaketkan di npm dengan file manifes, yang berisi semua hal yang perlu diketahui tentang komponen tersebut. Kemudian, kami menggunakan file manifes sebagai data saat situs dokumentasi di-deploy, yang dilakukan menggunakan Eleventy dan fitur Data Global-nya. Kami berencana menyertakan properti kustom kontekstual ini dalam file data manifes ini.

Area lain yang ingin kami tingkatkan adalah cara properti kustom kontekstual ini mewarisi nilai. Sekarang, misalnya, jika Anda ingin menyesuaikan warna dua komponen pembagi, Anda harus menargetkan kedua komponen tersebut secara khusus dengan pemilih, atau menerapkan properti kustom langsung pada elemen dengan atribut gaya. Hal ini mungkin terlihat baik-baik saja, tetapi akan lebih membantu jika developer dapat menentukan gaya tersebut pada elemen penampung atau bahkan di tingkat root.


<nord-divider></nord-divider>

<section>
  <nord-divider></nord-divider>
   <!-- ... -->
</section>

<style>
  nord-divider {
    --n-divider-color: var(--n-color-status-danger);
  }

  section {
    padding: var(--n-space-s);
    background: var(--n-color-surface-raised);
  }
  
  section nord-divider {
    --n-divider-color: var(--n-color-status-success);
  }
</style>
Dua instance komponen pembagi yang memerlukan dua perlakuan warna yang berbeda. Satu elemen ditempatkan di dalam bagian yang dapat kita gunakan untuk pemilih yang lebih spesifik, tetapi kita harus menargetkan pemisah secara khusus.

Alasan Anda harus menetapkan nilai properti kustom langsung pada komponen adalah karena kita menentukannya pada elemen yang sama menggunakan pemilih host komponen. Token desain global yang kita gunakan langsung dalam komponen akan diteruskan secara langsung, tidak terpengaruh oleh masalah ini, dan bahkan dapat dicegat pada elemen induk. Bagaimana cara kita mendapatkan yang terbaik dari kedua dunia?

Properti kustom pribadi dan publik

Properti kustom pribadi adalah sesuatu yang telah disusun oleh Lea Verou, yang merupakan properti kustom "pribadi" kontekstual pada komponen itu sendiri, tetapi ditetapkan ke properti kustom "publik" dengan penggantian.



:host {
  --_n-divider-color: var(--n-divider-color, var(--n-color-border));
  --_n-divider-size: var(--n-divider-size, 1px);
}

.n-divider {
  border-block-start: solid var(--_n-divider-size) var(--_n-divider-color);
  /* ... */
}
CSS Komponen Web pembagi dengan properti kustom kontekstual yang disesuaikan sehingga CSS internal mengandalkan properti kustom pribadi, yang telah ditetapkan ke properti kustom publik dengan penggantian.

Dengan menentukan properti kustom kontekstual seperti ini, kita masih dapat melakukan semua hal yang kita lakukan sebelumnya, seperti mewarisi nilai token global dan menggunakan kembali nilai di seluruh kode komponen; tetapi komponen juga akan mewarisi definisi baru properti tersebut dengan baik pada dirinya sendiri atau elemen induk apa pun.


<nord-divider></nord-divider>

<section>
  <nord-divider></nord-divider>
   <!-- ... -->
</section>

<style>
  nord-divider {
    --n-divider-color: var(--n-color-status-danger);
  }

  section {
    padding: var(--n-space-s);
    background: var(--n-color-surface-raised);
    --n-divider-color: var(--n-color-status-success);
  }
</style>
Dua pembatas lagi, tetapi kali ini pembatas dapat diwarnai ulang dengan menambahkan properti kustom kontekstual pembatas ke pemilih bagian. Pembagi akan mewarisinya, sehingga menghasilkan kode yang lebih rapi dan fleksibel.

Meskipun mungkin ada yang berpendapat bahwa metode ini tidak benar-benar "pribadi", kami tetap menganggapnya sebagai solusi yang cukup elegan untuk masalah yang kami khawatirkan. Jika ada kesempatan, kami akan mengatasi masalah ini di komponen kami sehingga tim pengembangan kami memiliki kontrol lebih besar atas penggunaan komponen, sekaligus tetap mendapatkan manfaat dari batasan yang kami miliki.

Semoga insight tentang cara kami menggunakan Komponen Web dengan properti kustom CSS ini bermanfaat bagi Anda. Beri tahu kami pendapat Anda dan jika Anda memutuskan untuk menggunakan salah satu metode ini dalam pekerjaan Anda sendiri, Anda dapat menemukan saya di Twitter @DavidDarnes. Anda juga dapat menemukan Nordhealth @NordhealthHQ di Twitter, serta anggota tim saya lainnya, yang telah bekerja keras untuk menyatukan sistem desain ini dan menjalankan fitur yang disebutkan dalam dokumen ini: @Viljamis, @WickyNilliams, dan @eric_habich.