Design System · v1.18.0
Konsisten dari token sampai layar bisnis.
Freeday — sumber kebenaran token-driven & framework-agnostic. Warna, tipografi, spasi, dan 44 komponen aksesibel yang dirakit jadi layar nyata di Vue, React, atau Blazor. Nol dependency runtime, kontras WCAG AA di terang & gelap.
Integrasi framework
Adapter tipis — enhancer tetap sumber kebenaran, event fdy-* dijembatani ke reaktivitas framework. Vue, React, dan Blazor kini sama-sama punya komponen typed (10/10 parity): FdyCombo, FdyDatepicker, FdyCfl, FdyChart, FdyTable, FdyModal/FdyDrawer, dst. Tiap stack punya contoh layar faktur yang jalan (lihat examples/).
Baru mau pakai di project sendiri? Panduan langkah-demi-langkah per stack →
Vue 3
Composable useFreeday() — hydrate saat mount & update, event lewat v-on. Plus komponen v-model: FdyCombo/FdyDatepicker/FdyCfl.
React 19
Hook useFreeday() + komponen controlled typed: FdyCombo/FdyDatepicker/FdyCfl/FdyChart (value/onChange).
Blazor WASM
RCL Freeday.Blazor (net8.0) — komponen native typed <FdyX> dengan @bind, 10/10 parity. Fallback: interop window.FreedayBlazor.
Playground
Pilih komponen, atur varian lewat kontrol Freeday (chip & switch), lihat pratinjau langsung, lalu salin markup-nya. Semua kelas berasal dari kit — nol dependency, siap tempel ke Vue / React / Blazor.
Prinsip
Empat aturan yang menjaga sistem tetap konsisten seiring pertumbuhannya.
1 · Semantic saja
Komponen hanya menyentuh peran (Tier-2), tak pernah ramp mentah. Re-brand & tema cukup ubah token semantic.
2 · Grid 4px
Semua spasi kelipatan 4px lewat skala --space-*. Tak pernah nilai lepas.
3 · Satu aksen per layar
Satu aksi primer menonjol; sisanya ghost/secondary. Warna status ≠ warna aksen.
4 · Aksesibel sejak awal
Kontras WCAG AA di terang & gelap, fokus selalu terlihat, HTML native dulu sebelum ARIA.
Warna
Peran semantic (Tier-2). Komponen hanya menyentuh peran ini, tak pernah ramp mentah.
Tipografi
Display Sora · body IBM Plex Sans · data JetBrains Mono.
Faktur & laporan yang rapi
Teks isi memakai IBM Plex Sans — nyaman dibaca untuk paragraf, label, dan tabel data bisnis sehari-hari.
INV-1042 · Rp 1.240.000 · 2026-07-30
Spasi
Skala kelipatan 4px. Selalu var(--space-N), tak pernah nilai lepas.
Radius & Elevasi
Sudut & bayangan bertingkat — hierarki kedalaman yang konsisten.
Radius
Elevasi
Motion
Skala gerak terbatas & konsisten: tiga durasi (--dur-fast · --dur-base · --dur-slow ) dan dua easing yang keduanya decelerate (tanpa bounce/elastic). Gerak hanya untuk feedback, reveal, dan transisi state — bukan dekorasi. Semua otomatis nonaktif saat prefers-reduced-motion: reduce. Arahkan kursor ke track untuk merasakan easing-nya.
Yang bergerak: Drawer & Modal beranimasi masuk dan keluar (slide / fade-scale + backdrop blur) lewat @starting-style + allow-discrete pada <dialog> native — buka lalu tutup, perhatikan animasi keluarnya. Overlay (menu, select, cascade, autocomplete, date/time picker) reveal saat dibuka; Accordion & Tabs me-reveal konten (fade + rise); baris tabel & kontrol punya transisi hover/press halus.
Tombol
Gradient gelap-ke-bawah + colored-glow. Satu aksi primer per layar; sisanya ghost.
Text + ikon · text-only (emphasis rendah) · icon-only (butuh aria-label).
Button group (segmented) & FAB (bulat · kecil · extended).
Input
Inner-shadow + focus glow. Untuk error, pakai aria-invalid="true" — atribut ini melakukan dua hal sekaligus (state visual dan yang bisa dibaca screen reader), lalu aria-describedby menautkan pesan (help ber-id sama). .fdy-input--error hanya untuk kasus di mana state invalid bukan milik field itu sendiri.
Input group
Addon prefix/suffix bebas — teks (Rp / %), ikon dekoratif (__addon--icon), tombol aksi (__btn), atau tanpa addon sama sekali. Jadi "dengan ikon / tanpa ikon / ikon custom" cukup dari markup addon.
Choose-from-list
Field read-only yang di-backing master data (pelanggan, item, gudang…). Ikon kaca pembesar di kanan membuka dialog pencari; baris yang dipilih mengisi field (kode + nama sekaligus). Nilai selalu dipilih, tak pernah diketik. Butuh freeday-cfl.js.
Single-select commit saat klik baris (tanpa "OK"). Keyboard: ketik untuk mencari, ↓ masuk daftar, ↑/↓ navigasi, Enter pilih, Esc tutup. Fokus kembali ke tombol pemicu saat ditutup. Baris = server state: di app nyata, jadikan komponen terkontrol (callback fetchPage + cache), bukan store global.
Date picker
Trigger bergaya input + kalender popover — konsisten lintas-browser & bisa di-theme. (Native <input type="date"> tetap pilihan ringan bila tak butuh theming.) Rentang = dua picker tertaut; akhir tak boleh mendahului awal. Keyboard: ←/→ hari, ↑/↓ minggu, PgUp/PgDn bulan, Enter pilih, Esc tutup. Butuh freeday-datepicker.js.
Varian ikon (konvensi kit): default (kalender), tanpa ikon lewat data-fdy-no-icon, atau ikon custom dengan <template data-fdy-icon> di dalam wrapper.
Time picker
Trigger bergaya input + popup daftar waktu (pola listbox WAI-ARIA). Format 24 jam HH:MM; rentang & langkah diatur lewat data-min/data-max/data-step (default tiap 30 menit). Keyboard: ↓ buka, ↑/↓ pindah, Home/End, Enter pilih, Esc tutup. Butuh freeday-timepicker.js.
Varian ikon (konvensi kit): default (jam), tanpa ikon lewat data-fdy-no-icon, atau ikon custom dengan <template data-fdy-icon>.
Datetime picker
Komposisi date picker + time picker jadi satu nilai — reuse dua komponen yang sudah ada, bukan popover monolitik. Composer freeday-datetime.js menyatukan keduanya dan memancarkan satu event fdy-datetime-change (value = YYYY-MM-DDTHH:MM). Varian ikon diwarisi dari tiap picker anak.
File upload
Drop target yang juga membuka dialog berkas saat klik / Enter. State eksplisit — idle, drag-over, mengunggah (progress), selesai, error — karena "tidak terjadi apa-apa" adalah feedback unggah terburuk. Butuh freeday-upload.js. (Demo ini mensimulasikan progress; di app nyata, sambungkan event fdy-upload-add ke fungsi unggahmu.)
Dua state terminal (statis) — terunggah & ditolak:
Stepper / Wizard
Alur multi-langkah — indikator progres (nomor → centang), panel per langkah, tombol Kembali/Lanjut. Langkah yang sudah dilewati bisa diklik untuk ditinjau (linear, tak bisa lompat maju). Butuh freeday-stepper.js.
Tinjau data, lalu tekan Selesai untuk menyimpan faktur.
Slider
Native <input type="range"> di-tema lewat accent-color — keyboard & ARIA bawaan, fill & thumb otomatis. freeday-slider.js cuma menyalin nilai ke <output>. Warna: primary/accent/danger.
Rating
Bintang — grup radio native (panah kiri/kanan untuk memilih, ter-asosiasi form). Terisi vs kosong dibedakan isi vs garis (bukan warna saja). Ada varian read-only. Butuh freeday-rating.js.
Autocomplete
Combobox editable — ketik untuk memfilter saran (pola APG combobox + listbox), keyboard ↑/↓/Enter/Esc. Butuh freeday-autocomplete.js. (Opsi statis di-filter di klien; di app nyata, fetch/filter server-side.)
- PT Sumber Makmur
- CV Berkah Jaya
- UD Cahaya Abadi
- PT Mitra Sentosa
- Toko Rejeki Makmur
- Koperasi Sejahtera
- Tak ada hasil.
Cascade select
Pemilih hierarki (drill-down) — model data = <ul> bersarang tersembunyi: <li> dengan anak <ul> adalah cabang, tanpa anak adalah daun. Cabang membuka level berikut, daun memilih (nilai = jalur lengkap). Keyboard: ↑/↓ pindah, →/Enter masuk cabang / pilih daun, ←/Backspace naik level, Esc tutup. Butuh freeday-cascade.js.
- Elektronik
- Handphone
- Android
- iOS
- Laptop
- Televisi
- Handphone
- Fashion
- Pria
- Wanita
- Anak
- Lainnya
Password & mask
Field kata sandi dengan tombol tampil/sembunyi (data-fdy-password — memakai kembali chrome tombol input-group) dan input ber-format saat mengetik (data-fdy-mask: # digit, A huruf, * alnum; karakter lain jadi literal otomatis). Butuh freeday-mask.js.
Form validation
Validasi native (Constraint Validation API) di-wire ke error inline aksesibel — aria-invalid + pesan ter-aria-describedby, fokus ke field invalid pertama saat submit, lalu re-validasi live tiap blur/input. Pesan custom via data-fdy-msg-*, cocokkan antar-field via data-fdy-match. Coba tekan Simpan saat kosong. Butuh freeday-form.js.
Validasi inline
Header + detail (dokumen) — .fdy-form-grid + tabel
Filter bar
Baris filter yang konsisten — .fdy-filterbar membungkus beberapa .fdy-field. Tiap field ikut ritme lebar (--w-sm · default · --w-lg · --w-xl); satu field --w-grow (biasanya pencarian) menyerap sisa ruang; aksi (__actions) menempel di ujung, sejajar kontrol. Membungkus rapi di layar sempit. Nol JS — cukup layout di atas .fdy-field.
Select (combobox)
Dropdown berdesain penuh (pola WAI-ARIA APG) — popup bisa distyle, keyboard ↑↓/Enter/Esc/ketik. Butuh freeday-select.js. Varian ikon: caret default, tanpa lewat fdy-combo--no-icon, atau custom (--no-icon + .fdy-combo__icon di dalam tombol).
- Semua
- Lunas
- ✓Tertunda
- Draft
Checkbox · Radio · Switch
Native <input> distyle — Space, asosiasi label, :focus-visible tetap native.
Alert
Pesan inline. Varian danger memakai role="alert".
Toast
Notifikasi sekilas di live-region. Panggil Freeday.toast({variant,title,message}) — mengembalikan elemen toast. Beri key agar toast dengan key sama menggantikan yang lama (bukan menumpuk saat error yang sama meletup beruntun); tutup lebih awal dengan Freeday.dismiss(node|key).
Tooltip
Muncul saat hover/focus. Asosiasi via aria-describedby.
Tabs
Pola APG — panah ←/→, Home/End, roving tabindex. Butuh freeday-tabs.js.
Breadcrumb
Pagination
Table
Tabel semantik statis — <caption>, <th scope>, angka tabular kanan.
| Faktur | Pelanggan | Status | Nilai |
|---|---|---|---|
| INV-1042 | PT Sumber Makmur | Lunas | 1.240.000 |
| INV-1043 | CV Berkah Jaya | Tertunda | 290.000 |
| INV-1044 | Toko Amanah | Draft | 0 |
Data table
Interaktif: cari global, urutkan (klik judul kolom), filter per kolom (ikon corong — teks berisi, enum checklist, angka rentang), pilih baris → bar aksi massal, paginasi. Butuh freeday-table.js.
| INV-1042 | PT Sumber Makmur | Lunas | 1.240.000 | |
| INV-1043 | CV Berkah Jaya | Tertunda | 290.000 | |
| INV-1044 | Toko Amanah | Draft | 0 | |
| INV-1045 | UD Maju Terus | Lunas | 4.750.000 | |
| INV-1046 | PT Cahaya Abadi | Tertunda | 820.000 | |
| INV-1047 | Koperasi Sejahtera | Batal | 150.000 | |
| INV-1048 | PT Sumber Makmur | Lunas | 3.120.000 | |
| INV-1049 | Toko Amanah | Draft | 67.000 | |
| INV-1050 | CV Berkah Jaya | Lunas | 1.990.000 | |
| INV-1051 | PT Cahaya Abadi | Tertunda | 540.000 |
States
Placeholder kosong / error untuk area data.
Charts
Sparkline, bar, line, area, dan donut — SVG/CSS murni, tanpa dependency. Seri tunggal via data-values; multi-seri via data-series (JSON). Cartesian (line/area/bar) menggambar sumbu ber-tema (--chart-grid/--chart-tick), legenda otomatis untuk ≥2 seri, dan format nilai data-fdy-format="number|percent|currency". Warna dari token → chart ikut re-warna saat data-theme berubah. Multi-seri memakai palet kategorikal tervalidasi --chart-1…--chart-8 (urutan tetap); override lewat data-fdy-colors — nama token semantik (primary…) atau slot chart-1…chart-8 untuk mengunci warna per kategori. Untuk data reaktif: panggil FreedayChart.update(el) atau pakai <FdyChart> dari @cahyo-dimas/freeday/vue. Butuh freeday-chart.js; tiap chart role="img" + aria-label + fallback tabel di dalam elemen.
Pendapatan
6 bulan terakhir
Rp 18 jtFaktur / hari
Status faktur
Posting vs draft
Line multi-seri · sumbu persen
| Bulan | Posted | Draft |
|---|---|---|
| Jun | 88 | 16 |
Nilai faktur
Area · sumbu USD
| Hari | Nilai |
|---|---|
| Jum | 7200 |
Faktur per kanal
Bar bertumpuk (stacked)
| Kuartal | Online | Cabang | Mitra |
|---|---|---|---|
| Q4 | 52 | 19 | 14 |
Tree view
Hierarki lewat <details> native — buka/tutup dengan klik atau Enter/Space, tanpa JS. Cabang bisa dilipat; daun = baris biasa. Varian checkbox (kanan) menambah pilihan bertingkat: centang cabang → semua anak ikut; sebagian anak → cabang jadi indeterminate. Butuh freeday-tree.js untuk cascade; centang tak pernah membuka/menutup cabang.
Dasar (tanpa JS)
-
Pelanggan
-
Jakarta
- PT Sumber Makmur
- PT Cahaya Abadi
-
Bandung
- CV Berkah Jaya
-
-
Item
- Kertas A4 80gsm
- Tinta Printer Hitam
Dengan checkbox (cascading)
-
Pelanggan
-
Jakarta
- PT Sumber Makmur
- PT Cahaya Abadi
-
Bandung
- CV Berkah Jaya
-
-
Item
- Kertas A4 80gsm
- Tinta Printer Hitam
Card
--interactive = kursor + hover-lift untuk kartu yang diberi handler klik. Kalau kartunya adalah kontrol (baris pemilih yang keyboard-reachable & diumumkan), render sebagai <button> dan tambah --button — reset kotak tombol UA tanpa membuang surface/border kartu.
Faktur INV-1042
Jatuh tempo 30 Juli 2026. Total tagihan Rp 1.240.000.
Interactive
Arahkan kursor — kartu terangkat (hover-lift).
Badge
Inline untuk status; overlay untuk angka/dot yang menempel di sudut ikon, tombol, atau teks.
Badge notifikasi = overlay. Bukan komponen terpisah dari “surat”: angka/“99+”/ikon/titik yang menempel di sudut elemen apa pun — lonceng, amplop, avatar, atau tombol. Bungkus konten dengan .fdy-badge-wrap, tambahkan .fdy-badge-ov. Warna ber-teks AA (default gelap · primary · danger); titik untuk accent/info/success/warning.
Avatar
Ukuran --sm / default / --lg, plus grup bertumpuk. Untuk membedakan avatar berinisial sama (mis. banyak nama "PT …"), pakai --tone-1…--tone-8 — tint dekoratif yang tetap kontras AA di terang & gelap; hash indeks stabil dari nama lengkap (bukan dari inisial).
Chip
Tiga peran: default / hapus (tag statis atau filter aktif yang bisa dihapus), choice (toggle pilih), dan filter (toggle dengan centang). Yang interaktif = <button aria-pressed> dikelola freeday-chip.js — bungkus dalam data-fdy-chips, tambah data-single untuk pilih-satu.
Detail (key-value)
Tampilan ringkas untuk halaman detail faktur / pelanggan.
Faktur INV-1042
- Pelanggan
- PT Sumber Makmur
- Status
- Lunas
- Jatuh tempo
- 30 Juli 2026
- Total
- Rp 1.240.000
Progress & Spinner
Skeleton
--title/--text untuk teks, --circle untuk bentuk bulat, dan --avatar (+--avatar-sm/--avatar-lg) yang seukuran .fdy-avatar — jadi placeholder memesan kotak yang persis sama dan tak ada geser tata letak saat data datang.
Carousel
Scroll-snap + panah + titik indikator. Keyboard ←/→, swipe di sentuh, autoplay opsional (data-fdy-autoplay, jeda saat hover/fokus). Butuh freeday-carousel.js.
Timeline
Feed peristiwa vertikal — titik penanda pada garis penghubung, dengan waktu & deskripsi. Warna titik menandai status (didampingi teks, bukan warna saja). Pure CSS.
-
21 Jul 2026 · 09:12
Faktur dibuat
INV-1042 untuk PT Sumber Makmur, Rp 1.240.000.
-
21 Jul 2026 · 09:15
Dikirim ke pelanggan
Email terkirim ke finance@sumbermakmur.co.id.
-
25 Jul 2026 · 14:30
Jatuh tempo mendekat
Sisa 5 hari sebelum jatuh tempo.
-
28 Jul 2026 · 10:02
Lunas
Pembayaran diterima via transfer bank.
App bar
Bar atas standalone — brand · aksi · spacer. Bisa sticky, elevated, dense, atau berwarna. Tombol hamburger biasanya membuka drawer (menu samping).
Berwarna (primary) & dense — kontrol jadi on-color.
Product appbar — komposisi kaya di dalam .fdy-app__topbar (App shell), beda dari .fdy-appbar berdiri sendiri di atas. Urutan: nav toggle → .fdy-breadcrumb → judul halaman (.fdy-app__title, auto-spacer kanan) → pencarian (.fdy-input-group + __addon--icon) → menu pengguna (.fdy-menu). Salin blok ini langsung ke dalam <header class="fdy-app__topbar"> shell Anda.
Vue/React: useFreeday menghidrasi .fdy-menu otomatis lewat enhancer data-fdy-menu (mekanisme sama dengan freeday-menu.js vanilla) — cukup pasang markup di atas di dalam komponen, tak perlu wiring buka/tutup manual.
Drawer
Panel samping temporary (overlay + backdrop) di atas <dialog> native — focus-trap, Esc, dan kembalikan-fokus gratis. Kiri atau kanan. Slide masuk & keluar dengan backdrop yang mem-blur — tutup lewat Esc/backdrop untuk melihat animasi keluarnya. Butuh freeday-drawer.js. (Tombol hamburger di App bar di atas juga membukanya.)
Accordion
Native <details> — zero-JS, aksesibel bawaan. Panel me-reveal (fade + rise) saat dibuka; chevron ikut berputar.
Info pengiriman
Metode pembayaran
Kebijakan retur
Divider · Kbd
Bagian atas
Bagian bawah
Simpan cepat dengan Ctrl + S, tutup dialog dengan Esc.
Modal
Native <dialog> — focus-trap, Esc, backdrop gratis dari browser. Muncul & menghilang dengan fade + skala halus (backdrop ikut mem-blur), simetris masuk-keluar.
Contoh: satu layar bisnis
Semua komponen dirakit jadi layar faktur nyata.
Breakpoint
Utilitas tampilan responsif + provider JS. Skala (min-width): sm 600 · md 960 · lg 1280 · xl 1920. Kelas .fdy-hide-below-* / .fdy-hide-above-* menyembunyikan per rentang. freeday-breakpoint.js menstempel <html data-breakpoint> + memancarkan fdy-breakpoint-change. Ubah ukuran jendela untuk melihatnya.
Breakpoint saat ini: …
Aksesibilitas
Kontrak yang dipegang setiap komponen — bukan renungan belakangan.
- Kontras teks & kontrol lolos WCAG AA di tema terang & gelap.
:focus-visibleselalu terlihat — outline/ring 2–3px.- Status tak hanya lewat warna (ikon/teks pendamping).
- HTML native dulu; ARIA hanya saat native tak cukup.
- Komponen interaktif ikut pola WAI-ARIA APG (role/aria/keyboard).
- Modal & dialog: focus-trap, Esc, fokus kembali ke pemicu.
- Input error:
aria-invalid+aria-describedby. - Menghormati
prefers-reduced-motion. - Target sentuh memadai; label jelas pada tiap kontrol.