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 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 membagikan cara kami memecahkan masalah seputar gaya visual Komponen Web menggunakan Properti Khusus CSS, dan beberapa manfaat lain dari penggunaan Properti Khusus dalam sistem desain dan library komponen.

Cara kami membangun Komponen Web

Untuk membuat Komponen Web kami, kami menggunakan Lit, sebuah library yang menyediakan banyak kode boilerplate seperti status, gaya cakupan, template, dan lainnya. Lit tidak hanya ringan, tetapi juga dibuat di API JavaScript native, artinya kami dapat mengirimkan paket kode ringkas 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.

Tetapi hal yang paling menarik tentang Komponen Web adalah bahwa komponen ini bekerja dengan hampir semua kerangka kerja JavaScript yang ada, atau bahkan tanpa kerangka kerja sama sekali. Setelah paket JavaScript utama direferensikan pada halaman, menggunakan Komponen Web sangat mirip dengan menggunakan elemen HTML native. Satu-satunya tanda nyata bahwa ini bukan elemen HTML native adalah tanda hubung yang konsisten dalam tag, yang merupakan standar untuk memberi tahu browser bahwa ini adalah Komponen Web.


// TODO: DevSite - Code sample removed as it used inline event handlers
Menggunakan Komponen Web yang dibuat di atas pada halaman.

Enkapsulasi gaya Shadow DOM

Sama seperti elemen HTML native yang memiliki Shadow DOM, begitu juga Komponen Web. Shadow DOM adalah pohon simpul tersembunyi di dalam sebuah elemen. Cara terbaik untuk memvisualisasikan ini adalah dengan membuka web inspector dan mengaktifkan opsi "Show Shadow DOM tree". Setelah melakukannya, coba lihat elemen input native di inspector—Anda kini 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 di Komponen Web input Nord kami.

Salah satu kelebihan (atau kekurangan, bergantung pada pandangan Anda) terhadap Shadow DOM adalah enkapsulasi gaya. Jika Anda menulis CSS dalam Komponen Web, gaya tersebut tidak dapat bocor dan mempengaruhi laman utama atau elemen lain; gaya tersebut sepenuhnya terkandung 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 kami lebih banyak jaminan bahwa saat seseorang menggunakan salah satu komponen kami, komponen tersebut akan terlihat seperti yang kita inginkan, terlepas dari gaya yang diterapkan ke halaman induk. Dan untuk memastikan lebih lanjut, 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 yang diterapkan ke root bayangan, 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 harus lebih tebal? Jika tidak ada gaya yang bisa masuk ke komponen Anda, bagaimana cara membuka opsi gaya tersebut?

Di sinilah Properti Khusus CSS berperan.

Properti Kustom CSS

Properti Kustom diberi nama yang sangat tepat. Ini adalah properti CSS yang dapat Anda beri nama sendiri sepenuhnya dan menerapkan nilai apa pun yang diperlukan. Satu-satunya persyaratan adalah Anda harus memberi awalan dua tanda hubung. Setelah Anda mendeklarasikan properti khusus, nilainya dapat digunakan di CSS Anda 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 digunakan di class helper.

Dalam hal pewarisan, semua Properti Khusus diwarisi, yang mengikuti perilaku umum properti dan nilai CSS reguler. Setiap properti khusus yang diterapkan ke elemen induk, atau elemen itu sendiri, dapat digunakan sebagai nilai pada properti lain. Kami banyak menggunakan Properti Khusus untuk token desain dengan menerapkannya ke elemen root melalui Framework CSS kami, yang berarti bahwa semua elemen pada halaman dapat menggunakan nilai token ini, baik itu Komponen Web, kelas bantuan CSS, atau developer yang ingin mengambil nilai dari daftar token kami.

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

Properti Kustom di Komponen Web Nord

Setiap kali kami mengembangkan komponen untuk sistem desain kami, kami mengambil pendekatan yang bijaksana untuk CSS-nya—kami ingin menargetkan kode yang ramping namun sangat mudah dikelola. Token desain yang kita miliki ditetapkan sebagai Properti Kustom dalam Framework CSS utama di 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 Khusus CSS yang ditentukan pada pemilih root.

Nilai token ini kemudian direferensikan dalam komponen kita. Dalam beberapa kasus, kita akan menerapkan nilai langsung pada properti CSS, tetapi untuk kasus lainnya, kita 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 root bayangan komponen, lalu digunakan dalam gaya komponen. Properti Kustom dari daftar token desain juga digunakan.

Kita juga akan memisahkan beberapa nilai yang spesifik untuk komponen tersebut, tetapi tidak ada di token, lalu mengubahnya menjadi Properti Kustom kontekstual. Properti Kustom yang kontekstual dengan komponen memberi kami dua manfaat utama. Pertama, ini berarti kita dapat 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);
}
Properti Kustom kontekstual padding grup tab yang digunakan di beberapa tempat dalam kode komponen.

Kedua, hal ini membuat perubahan status komponen dan variasi 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 dengan padding yang diubah menggunakan satu update Properti Kustom, bukan beberapa update.

Namun, manfaat yang paling efektif adalah saat kita menentukan Properti Khusus kontekstual ini pada komponen, kita akan membuat semacam API CSS 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 pada halaman dan memperbarui Properti Khusus padding ke ukuran yang lebih besar.

Contoh sebelumnya menunjukkan salah satu Komponen Web kami dengan Properti Kustom kontekstual yang diubah melalui pemilih. Hasil dari keseluruhan pendekatan ini adalah komponen yang memberikan fleksibilitas gaya yang cukup kepada pengguna sambil tetap menjaga sebagian besar gaya sebenarnya tetap diperiksa. Selain itu, sebagai bonus, kita sebagai developer komponen memiliki kemampuan untuk menangkap gaya yang diterapkan oleh pengguna. Jika ingin menyesuaikan atau memperluas salah satu properti tersebut, kita dapat melakukannya tanpa perlu mengubah kode pengguna.

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

Meningkatkan Properti Khusus

Pada saat penulisan, kami tidak benar-benar mengungkapkan Properti Kustom kontekstual ini dalam dokumentasi kami; namun, kami berencana untuk melakukannya agar tim pengembangan yang lebih luas dapat memahami dan memanfaatkan properti ini. Komponen kami dikemas di npm dengan file manifes, yang berisi semua informasi tentang komponen tersebut. Kami kemudian menggunakan file manifes sebagai data saat situs dokumentasi kami 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. Saat ini, misalnya, jika Anda ingin menyesuaikan warna dua komponen pemisah, Anda harus menargetkan kedua komponen tersebut secara khusus dengan pemilih, atau menerapkan properti khusus secara langsung pada elemen dengan atribut gaya. Hal ini mungkin tampak baik, 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. Salah satunya berada 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 melalui pemilih host komponen. Token desain global yang kita gunakan langsung di komponen diteruskan lurus, tidak terpengaruh oleh masalah ini, dan bahkan dapat dicegat pada elemen induk. Bagaimana kita bisa 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 pemisah dengan Properti Khusus kontekstual yang disesuaikan sehingga CSS internal mengandalkan Properti Khusus pribadi, yang telah ditetapkan ke Properti Khusus publik dengan penggantian.

Menentukan Properti Kustom kontekstual dengan cara ini berarti 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 pemisah lagi, tetapi kali ini pemisah dapat diwarnai ulang dengan menambahkan Properti Kustom kontekstual pemisah ke pemilih bagian. Pemisah akan mewarisinya, sehingga menghasilkan potongan kode yang lebih rapi dan fleksibel.

Meskipun dapat dikatakan 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 lebih banyak kontrol atas penggunaan komponen sekaligus tetap mendapatkan manfaat dari pembatasan yang telah 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 saya lainnya, yang telah bekerja keras dalam menyatukan sistem desain ini dan menjalankan fitur yang disebutkan dalam artikel ini: @Viljamis, @WickyNilliams, dan @eric_habich.

Banner besar oleh Dan Cristian Pădure mewah