Design Tokens & Variables — UI/UX

Design Tokens — Fondasi Design System Design tokens adalah nilai-nilai desain yang disimpan sebagai variabel — warna, spacing, typography, border-radius…

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 #22c55e dengan 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 */

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); }

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."
  }
}

⚠️ Jebakan yang sering ketemu

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).

Yang akan kamu pelajari