Cara Nordhealth menggunakan Properti Khusus di Komponen Web

Manfaat menggunakan Properti Kustom dalam sistem desain dan library komponen.

David Darnes
David Darnes

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

Cara kami membangun Komponen Web

Untuk mem-build Komponen Web kami, kami menggunakan Lit, sebuah library yang menyediakan banyak kode boilerplate seperti status, gaya yang dicakup, pembuatan template, dan lainnya. Selain ringan, Lit juga dibangun di atas API JavaScript native, yang berarti kami dapat memberikan 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 bahwa komponen ini berfungsi dengan hampir semua framework JavaScript yang ada, atau bahkan tanpa framework sama sekali. Setelah paket JavaScript utama dirujuk di halaman, penggunaan Komponen Web sangat mirip dengan penggunaan elemen HTML native. Satu-satunya tanda yang menunjukkan bahwa elemen tersebut bukan elemen HTML asli adalah tanda hubung yang konsisten dalam tag, yang merupakan standar untuk memberi tahu browser bahwa ini adalah Komponen Web.

Enkapsulasi gaya Shadow DOM

Sama seperti elemen HTML native yang memiliki Shadow DOM, begitu juga dengan Komponen Web. DOM bayangan adalah hierarki node tersembunyi dalam elemen. Cara terbaik untuk memvisualisasikannya adalah dengan membuka pemeriksa web dan mengaktifkan opsi "Show Shadow DOM tree". Setelah melakukannya, coba lihat elemen input asli 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 biasa dan dalam Komponen Web input Nord kami.

Salah satu keunggulan (atau kekurangan, bergantung pada perspektif Anda) Shadow DOM adalah enkapsulasi gaya. Jika Anda menulis CSS dalam Komponen Web, gaya tersebut tidak dapat bocor dan memengaruhi halaman utama atau elemen lain; gaya tersebut sepenuhnya berada 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 kita. Hal ini memberi kita jaminan yang lebih besar bahwa saat seseorang menggunakan salah satu komponen kita, komponen tersebut akan terlihat seperti yang kita inginkan, terlepas dari gaya yang diterapkan pada halaman induk. Untuk memastikan lebih lanjut, kita menambahkan all: unset; ke root, atau "host", semua Komponen Web kita.


:host {
  all: unset;
  display: block;
  box-sizing: border-box;
  text-align: start;
  /* ... */
}
Beberapa kode boilerplate komponen diterapkan ke shadow root, 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 perlu lebih tebal? Jika tidak ada gaya yang dapat masuk ke komponen Anda, bagaimana Anda dapat membuka opsi gaya tersebut?

Di sinilah Properti Khusus CSS berperan.

Properti Kustom CSS

Properti Kustom dinamai dengan sangat tepat—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 menambahkan dua tanda hubung di depannya. Setelah Anda mendeklarasikan properti kustom, nilai 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 CSS Framework kami tentang token desain sebagai Properti Kustom dan penggunaannya pada class helper.

Terkait pewarisan, semua Properti Kustom diwariskan, yang mengikuti perilaku umum properti dan nilai CSS biasa. Setiap properti kustom yang diterapkan ke elemen induk, atau elemen itu sendiri, dapat digunakan sebagai nilai pada properti lain. Kami banyak menggunakan Properti Kustom untuk token desain dengan menerapkannya ke elemen root melalui Framework CSS, yang berarti 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 DOM Bayangan Komponen Web dan memungkinkan developer memiliki kontrol yang lebih terperinci saat menata gaya komponen.

Properti Kustom di Komponen Web Nord

Setiap kali kami mengembangkan komponen untuk sistem desain kami, kami menggunakan pendekatan yang cermat terhadap CSS-nya—kami ingin mendapatkan kode yang ramping tetapi sangat mudah dikelola. Token desain yang kami miliki ditentukan sebagai Properti Kustom dalam Framework CSS utama kami 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 lainnya, 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 khusus untuk komponen, tetapi tidak ada dalam token kita dan mengubahnya menjadi Properti Kustom kontekstual. Properti Kustom yang kontekstual terhadap komponen memberikan dua manfaat utama bagi kita. Pertama, kita bisa lebih "kering" 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);
}
Padding grup tab sebagai Properti Kustom kontekstual yang digunakan di beberapa tempat dalam kode komponen.

Kedua, hal ini membuat perubahan status dan variasi komponen menjadi sangat bersih—hanya properti kustom yang perlu diubah untuk memperbarui semua properti tersebut saat, misalnya, Anda menata gaya status melayang 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 API CSS kustom untuk setiap komponen, yang dapat digunakan oleh pengguna komponen tersebut.


<nord-tab-group label="T>itl<e"
  >!<-- ... --
/nord>-t<ab-gr>oup

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

Contoh sebelumnya menunjukkan salah satu Komponen Web kami dengan Properti Kustom kontekstual yang diubah melalui 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, kami sebagai developer komponen dapat mencegat gaya yang diterapkan oleh pengguna. Jika ingin menyesuaikan atau memperluas salah satu properti tersebut, kita dapat melakukannya tanpa pengguna perlu mengubah kode apa pun.

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

Memanfaatkan Properti Kustom lebih lanjut

Pada saat penulisan, kami sebenarnya tidak 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 dikemas di npm dengan file manifes, yang berisi semua hal yang perlu diketahui tentang komponen tersebut. Kemudian, kita menggunakan file manifes sebagai data saat situs dokumentasi kita di-deploy, yang dilakukan menggunakan Eleventy dan fitur Data Globalnya. 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. Saat ini, misalnya, jika Anda ingin menyesuaikan warna dua komponen pemisah, Anda harus menargetkan kedua komponen tersebut secara khusus dengan pemilih, atau menerapkan properti kustom langsung pada elemen dengan atribut gaya. Ini mungkin terlihat baik, tetapi akan lebih bermanfaat jika developer dapat menentukan gaya tersebut pada elemen yang berisi 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 pemisah yang memerlukan dua perlakuan warna yang berbeda. Salah satunya berada di dalam bagian yang dapat kita gunakan untuk pemilih yang lebih spesifik, tetapi kita harus menargetkan pembagi secara khusus.

Alasan Anda harus menetapkan nilai Properti Kustom langsung pada komponen adalah karena kita menentukannya pada elemen yang sama melalui pemilih host komponen. Token desain global yang kita gunakan langsung di komponen diteruskan secara langsung, tidak terpengaruh oleh masalah ini, dan bahkan dapat dicegat pada elemen induk. Bagaimana cara mendapatkan yang terbaik dari keduanya?

Properti Kustom pribadi dan publik

Properti khusus pribadi adalah sesuatu yang telah disusun oleh Lea Verou, yang merupakan Properti Khusus "pribadi" kontekstual pada komponen itu sendiri, tetapi ditetapkan ke Properti Khusus "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 dengan cara 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 mana 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 pemisah lagi, tetapi kali ini pemisah dapat diwarnai ulang dengan menambahkan Properti Kustom kontekstual pemisah ke pemilih bagian. Pembagi akan mewarisinya, sehingga menghasilkan kode yang lebih rapi dan fleksibel.

Meskipun dapat diperdebatkan bahwa metode ini tidak benar-benar "pribadi", kami tetap menganggap ini adalah solusi yang cukup elegan untuk masalah yang kami khawatirkan. Jika ada kesempatan, kami akan menanganinya di komponen kami sehingga tim pengembangan kami memiliki lebih banyak kontrol atas penggunaan komponen sambil tetap mendapatkan manfaat dari batas aman yang kami terapkan.

Semoga insight tentang cara kami menggunakan Komponen Web dengan Properti Khusus 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 lainnya yang telah bekerja keras menyatukan sistem desain ini dan menjalankan fitur yang disebutkan dalam artikel ini: @Viljamis, @WickyNilliams, dan @eric_habich.

Gambar banner besar oleh Dan Cristian Pădureț