Design token adalah nilai desain (warna, jarak, radius, ukuran huruf, shadow) yang disimpan sebagai variabel dengan nama, lalu dipakai di mana-mana lewat namanya. Bayangin buku catatan cat Bu Sri. Di halaman pertama ada kode dari katalog toko: "Hijau Daun 700", "Abu Kabut 200". Di halaman kedua ada tugasnya: "cat pintu kamar: Hijau Daun 700", "cat dinding: Abu Kabut 200". Tukang cat cuma membaca halaman kedua. Kalau Bu Sri ingin pintu jadi hijau yang lebih terang, dia mengubah satu baris di halaman kedua, dan katalognya tetap. Design token disusun dengan cara yang sama.
🧪 Tebakan cepat: Halaman pesan Bu Sri akan punya versi gelap. Di versi gelap, tombol pesan harus
#22c55edengan teks#111827, dan kartunya#1f2937. Kalau tokennya disusun tiga tingkat, berapa baris aturan komponen (.kabut,.book, pilihan malam) yang harus diubah? Jawabannya ada di bagian "Tema gelap: tukar semantic saja".
Masalahnya: angka yang sama di banyak tempat
Sebelum memakai token, hijau Bu Sri #15803d ditulis di lima tempat: latar tombol pesan, latar pilihan malam yang aktif, garis pilihan malam itu, warna link "Lihat aturan batal", dan cincin fokus. Mengganti warna brand berarti mencari kelimanya, dan berharap gak ada yang menulis #16803d karena salah ketik. Jarak 16px dan radius 12px juga tersebar di banyak aturan.
Token menyimpan tiap keputusan sekali, dengan nama. Semua yang lain memanggil namanya.
Design token di CSS: custom property
Di CSS, token ditulis sebagai custom property (sering disebut CSS variable):
:root {
--action: #15803d; /* deklarasi: nama diawali dua tanda minus */
--space-4: 1rem; /* 16px pada ukuran huruf bawaan browser */
}
.book { background: var(--action); } /* var() membaca nilainya */
.kabut { padding: var(--space-4, 1rem); } /* angka kedua: cadangan kalau tokennya gak ada */
:rootadalah elemen<html>. Custom property diwariskan ke semua elemen di bawahnya, jadi token yang ditulis di sini bisa dibaca di seluruh halaman.var(--action)mengganti dirinya dengan nilai token saat browser menghitung gaya. Namanya peka huruf besar kecil:--Actiontoken yang lain.1remsama dengan ukuran huruf<html>, bawaan browser 16px. Jadi--space-4: 1remmenghasilkan padding 16px, dan--radius-md: 0.5remradius 8px.
Di latihan nanti, kode awalnya punya .card dengan judul dan paragraf, dan <style> dengan :root dan .card yang masih kosong. Isi :root dengan --color-primary: #2563EB, --color-text: #111827, --space-4: 1rem, dan --radius-md: 0.5rem, lalu tulis .card { padding: var(--space-4); border-radius: var(--radius-md); }. Hasilnya padding 16px dan radius 8px, dan angkanya cuma ditulis sekali.
Tiga tingkat: primitive, semantic, component
Token yang rapi disusun bertingkat. Tiap tingkat cuma boleh menunjuk tingkat di bawahnya:
| Tingkat | Isinya | Contoh | Dibaca oleh |
|---|---|---|---|
| Primitive | semua nilai mentah, namanya menurut warna atau ukuran | --green-700: #15803d |
token semantic |
| Semantic | namanya menurut tugasnya | --action: var(--green-700) |
token komponen, aturan CSS |
| Component | milik satu komponen | --book-bg: var(--action) |
aturan komponen itu |
:root {
/* 1. primitive: ramp hijau 50 sampai 900 (nilai Tailwind) dan abu-abu halaman ini */
--green-50: #f0fdf4; --green-100: #dcfce7; --green-200: #bbf7d0; --green-300: #86efac; --green-400: #4ade80;
--green-500: #22c55e; --green-600: #16a34a; --green-700: #15803d; --green-800: #166534; --green-900: #14532d;
--white: #ffffff; --slate-100: #f1f5f9; --slate-200: #e2e8f0; --slate-400: #94a3b8; --slate-600: #475569;
--gray-700: #374151; --gray-800: #1f2937; --gray-900: #111827;
--space-3: 0.75rem; --space-4: 1rem; --radius-md: 0.5rem; --radius-lg: 0.75rem;
/* 2. semantic: namanya menurut tugasnya */
--surface-page: var(--slate-100);
--surface-card: var(--white);
--border: var(--slate-200);
--text: var(--gray-800);
--text-muted: var(--slate-600);
--action: var(--green-700);
--on-action: var(--white);
/* 3. component: milik satu komponen */
--card-bg: var(--surface-card);
--card-padding: var(--space-4);
--card-radius: var(--radius-lg);
--book-bg: var(--action);
--book-text: var(--on-action);
--book-radius: var(--radius-md);
}
.kabut { padding: var(--card-padding); background: var(--card-bg); border: 1px solid var(--border); border-radius: var(--card-radius); }
.book { color: var(--book-text); background: var(--book-bg); border-radius: var(--book-radius); }
.nights [aria-pressed="true"] { color: var(--on-action); background: var(--action); border-color: var(--action); }
- Primitive adalah katalognya, dan isinya gak pernah berubah karena tema:
--green-700selalu#15803d. Semantic adalah halaman kedua buku catatan:--actionartinya "semua yang bisa ditekan", nama yang dipakai sejak pelajaran warna struktural dan interaktif. - Component memberi satu komponen nama sendiri. Kalau tombol pesan perlu warna yang beda dari link, cukup
--book-bgyang diarahkan ulang, tanpa mengganggu--action. - Aturan komponen gak pernah menyebut primitive. Itu satu-satunya aturan yang harus dijaga.
Tema gelap: tukar semantic saja
Versi gelap menunjuk tujuh token semantic ke primitive yang lain. Tidak ada yang lain:
@media (prefers-color-scheme: dark) {
:root {
--surface-page: var(--gray-900);
--surface-card: var(--gray-800);
--border: var(--gray-700);
--text: var(--slate-100);
--text-muted: var(--slate-400);
--action: var(--green-500);
--on-action: var(--gray-900);
}
}
Tujuh baris token berubah, dan aturan .kabut, .book, serta pilihan malam gak disentuh. Jadi jawaban tebakannya: 0 baris komponen. Itu jawaban tebakannya. Kalau halaman punya tombol ganti tema, selector-nya :root[data-theme="dark"] dengan isi yang sama (Lab memakai cara ini).
Hasilnya, diukur dengan getComputedStyle di Chromium, WebKit, dan Firefox:
| Terang | Gelap | |
|---|---|---|
| Tombol pesan | #15803d, teks putih 5,02:1 |
#22c55e, teks #111827 7,79:1 |
| Tombol terhadap kartu (minimal 3:1, WCAG 1.4.11) | 5,02:1 di kartu #ffffff |
6,44:1 di kartu #1f2937 |
Itulah keuntungan semantic token dibanding raw (primitive) token: mudah ganti theme, tanpa mengubah komponen. Bandingkan dengan tim yang komponennya langsung memakai raw token, misalnya .book { background: var(--green-700); }. Komponen itu terikat (coupled) ke raw token: blok gelap gak menyentuhnya, tombolnya tetap #15803d di kartu #1f2937, cuma 2,93:1, gagal 3:1. Memperbaikinya berarti mengedit tiap komponen yang menyebut --green-700, di setiap tema. Obatnya satu lapis semantic: komponen membaca --action, dan tema mengubah --action di satu tempat.
Di luar CSS: format DTCG
Token sering juga disimpan di file JSON, supaya Figma, aplikasi Android dan iOS, dan CSS membaca sumber yang sama. Formatnya distandarkan oleh Design Tokens Community Group (DTCG) di W3C, dalam dokumen Design Tokens Format Module 2025.10 (Final Community Group Report, 28 Oktober 2025). Dokumen itu sendiri menulis bahwa ia bukan W3C Standard dan gak berada di jalur standar W3C. Filenya berakhiran .tokens atau .tokens.json:
{
"color": {
"green": {
"700": {
"$type": "color",
"$value": { "colorSpace": "srgb", "components": [0.0824, 0.502, 0.2392], "hex": "#15803d" }
}
}
},
"action": {
"$type": "color",
"$value": "{color.green.700}",
"$description": "Semua yang bisa ditekan: tombol pesan, pilihan malam, link."
}
}
$valueadalah nilainya dan$typejenisnya. Warna di versi 2025.10 berupa objek: ruang warna, tiga komponen 0 sampai 1 (21/255, 128/255, 61/255), danhexsebagai cadangan."{color.green.700}"adalah alias, sama sepertivar(--green-700).- Alat seperti Style Dictionary (sejak versi 4) mengubah file ini jadi custom property CSS; dukungan penuh untuk versi 2025.10 masih dikerjakan, jadi cek versi alatmu.
⚠️ Jebakan yang sering ketemu
- Salah ketik nama token.
padding: var(--space4)saat tokennya--space-4: padding jadi 0px, tanpa error di console (kami ukur di tiga browser). Deklarasi dengan variabel yang gak ada gak berlaku, dan padding kembali ke nilai awalnya. - Menempelkan satuan di belakang
var().--radius: 8laluborder-radius: var(--radius)pxjuga jadi 0px. Simpan satuannya di token (0.5rem), atau pakaicalc(var(--radius) * 1px). - Komponen membaca primitive.
var(--green-700)di.bookmembuat tema gelap gak sampai ke tombol. - Tema untuk sebagian halaman saja. Token komponen di
:rootsudah dihitung di:root. Kalau.promo { --action: #22c55e; }, tombol yang membacavar(--book-bg)di dalam.promotetap#15803d; cuma yang membaca--actionlangsung yang berubah. Tema per bagian butuh token komponennya ditulis ulang di bagian itu juga. - Nama menurut warna di tingkat semantic.
--greenuntuk tombol berhenti masuk akal begitu brand-nya biru. Semantic dinamai menurut tugasnya.
TL;DR: Design token adalah nilai desain (warna, jarak, radius, huruf, shadow) yang disimpan sebagai variabel bernama dan dipakai lewat namanya; di CSS, custom property --nama: nilai di :root, dibaca dengan var(--nama) (cadangan: var(--nama, nilai)). Susun tiga tingkat: primitive (--green-700: #15803d), semantic menurut tugas (--action: var(--green-700)), component (--book-bg: var(--action)); komponen gak pernah membaca primitive. Tema gelap cuma menukar 7 token semantic, 0 baris komponen: itu keuntungan semantic token, mudah ganti theme. Komponen yang langsung memakai raw token terikat (coupled) ke raw token, dan tombol #15803d di kartu gelap cuma 2,93:1. Di latihan: --color-primary: #2563EB, --color-text: #111827, --space-4: 1rem, --radius-md: 0.5rem, lalu .card memakai var(--space-4) dan var(--radius-md) (16px dan 8px). Di luar CSS, format JSON-nya adalah Design Tokens Format Module 2025.10 dari DTCG ($value, $type, alias "{color.green.700}"). Salah ketik nama token gagal diam-diam (0px).