D
P
0
← Semua artikel Read in English

Next.js & React di Produksi

CSS Modules Menolak Selector Data-Attribute: Error `selector is not pure`

· · 4 menit baca
CSS Modules Menolak Selector Data-Attribute: Error `selector is not pure`

Saya ingin tiap artikel di blog ini punya warna aksennya sendiri. Bukan tema yang bisa diganti pembaca, cukup satu angka di frontmatter yang menentukan nuansa satu halaman: kicker kecil di atas judul, garis pendek di bawah judul, warna hover tautan, dan kartu artikel terkait di bawah. Sepuluh nilai, satu warna untuk tiap angka.

Rencananya seringkas mungkin. Angka dari frontmatter dipasang sebagai atribut data-mood di elemen pembungkus, CSS mendefinisikan satu custom property per nilai atribut, lalu semua turunan tinggal membaca property itu. Yang dipakai cuma tiga hal: Next.js 16 App Router, CSS Modules, dan CSS custom property biasa.

Blok pertama saya tulis begini, di CSS Module milik bagian blog:

/* articles.module.css */
[data-mood="7"] {
  --tint: #8a6a3b;
}

Kompilasinya berhenti di situ. CSS Modules menolak selector itu, dan errornya memuat frasa not pure. Warnanya cuma contoh, nilainya sama sekali tidak relevan dengan penolakan ini. Yang ditolak bentuk selectornya.

Kenapa selector atribut polos dianggap tidak murni

CSS Modules mewajibkan tiap selector memuat setidaknya satu class atau id lokal. Selector atribut yang berdiri sendiri tidak memuat keduanya, jadi ia terbaca sebagai selector global, dan pemeriksaan kemurnian menolaknya sebagai tidak murni.

Kalau diingat lagi apa yang sebenarnya dikerjakan CSS Modules, aturan itu masuk akal. Seluruh gunanya adalah men-scope nama class supaya tidak bocor ke luar berkasnya. Selector yang tidak punya satu pun kaitan lokal tidak punya apa-apa untuk di-scope, jadi dia akan diam-diam jadi aturan global yang berlaku ke seluruh dokumen. Daripada meloloskan itu tanpa suara, kompilernya memilih berhenti.

Yang sempat menipu saya: selector itu sah sepenuhnya sebagai CSS biasa. Tidak ada yang salah dengan [data-mood="7"] di stylesheet global mana pun, dan browser tidak akan mengeluh sedikit pun. Yang menolaknya bukan parser CSS, melainkan lapisan aturan yang dipasang CSS Modules di atas CSS.

Perbaikannya: bungkus dengan :global()

/* satu blok seperti ini untuk tiap nilai, 1 sampai 10 */
:global([data-mood="7"]) {
  --tint: #8a6a3b;
}

:global() menyatakan bahwa selector di dalamnya memang dimaksudkan global dan tidak perlu di-scope, jadi pemeriksaan kemurnian tidak lagi punya alasan menolaknya. Sepuluh warna aksen saya definisikan dengan bentuk ini, dan bentuk inilah satu-satunya yang lolos. Versi polosnya akan selalu gagal.

Satu custom property, empat pembungkus

Atributnya dipasang di elemen pembungkus, dan nilainya diambil dari frontmatter. Tepatnya ada empat kelas yang membawanya: pembungkus artikel, kartu unggulan, item di grid daftar, dan kartu artikel terkait.

<article className={styles.shell} data-mood={meta.mood}>

Setelah itu tidak ada lagi yang perlu tahu soal angkanya. Custom property diwariskan ke turunan, jadi begitu --tint terdefinisi di pembungkus, semua yang ada di dalamnya bisa membacanya. Empat hal yang saya warnai dengan cara ini: kicker, garis judul, hover tautan, dan kartu artikel terkait.

.kicker { color: var(--tint); }
.titleRule { background: var(--tint); }
.prose a:hover { color: var(--tint); }
.linkedCard:hover { border-color: var(--tint); }

Perhatikan bahwa aturan-aturan konsumen ini tidak pernah bermasalah dengan pemeriksaan kemurnian. Semuanya bersandar pada class lokal, jadi mereka memenuhi syaratnya tanpa perlu diapa-apakan. Satu-satunya blok yang bermasalah adalah blok yang mendefinisikan property-nya, karena cuma blok itu yang selectornya tidak punya class sama sekali.

Kartu artikel terkait baru butuh warna ini karena di batch yang sama daftar artikel terkait saya ubah dari daftar teks jadi kartu bergambar, lengkap dengan hover beraksen.

Menyebarkannya ke seluruh arsip

Field angkanya diurai di modul metadata, di tipe post yang dipakai seluruh blog. Aturan pembagiannya sederhana: tiap tiga artikel berbagi satu nilai. Dengan sepuluh nilai yang tersedia, satu putaran penuh menutup 10 x 3 = 30 artikel.

Batch yang membawa theming ini menambah 30 artikel baru, jadi putaran itu pas sekali habis di artikel barunya saja. Masalahnya, kalau cuma artikel baru yang diberi field-nya, 22 artikel lama akan tetap tanpa warna dan blognya jadi belang. Jadi field itu diisikan juga ke 22 artikel lama berdasarkan triplet tanggal masing-masing, sehingga 22 + 30 = 52 artikel bertema seluruhnya, bukan cuma yang baru.

Hasil akhir batch itu: 52 artikel Indonesia dan 52 Inggris, yang berarti 52 + 52 = 104 berkas MDX, build keluar dengan kode 0, dan 122 halaman ter-render.

Pelajaran