Komponen
Komponen guest-side
Setiap contoh di halaman ini adalah komponen Svelte asli dari src/lib, bukan tiruan. Semuanya membungkus kelas vc- yang hanya membaca token. Geometri mengikuti komponen di kode; peta ke file-nya ada di bagian Keputusan. Teks specimen memakai bahasa UI produk (Inggris); angka dan nama memakai placeholder.
Pemakaian
// +layout.svelte (sekali)
import '@vilacation/designkit/styles.css';
// komponen
import { Button, VillaCard, TabBar } from '@vilacation/designkit'; Font (Fraunces dan Montserrat) tidak ikut dibundel; frontend sudah memuatnya lewat @fontsource-variable. Harga, tanggal, dan teks dikirim sudah diformat dari pemanggil: kit tidak mengarang atau memformat data.
Button
Varian
Ukuran
State
Brand, Outline, dan Link adalah tiga varian Button di mobile (ui/button.tsx); Primary navy adalah tombol Sign In di web. Brand persis aslinya (teal-500 dengan teks putih), kontrasnya 2.16:1. Accent adalah pasangan aman (teal-800, 4.88:1). Outline di kit memakai teks teal-800 karena aslinya teks teal-500 di atas putih (2.16:1). Satu tombol utama per layar. Isi href untuk merender sebagai tautan.
Header
Tinggi 64px di mobile dan 88px mulai 768px (sama dengan --site-header-height). Prop transparent untuk di atas foto hero. Logo dimuat dari URL; kalau gagal, tampil teks pengganti.
Search bar
Komponen signature. Persempit jendela di bawah 768px untuk melihat versi mobile (segmen bertumpuk, tombol Search lebar penuh). Memakai shadow float yang sama dengan nav bawah, karena sama-sama mengambang. Isi destination, dates, dan guests dengan teks yang sudah diformat; kosong menampilkan placeholder.
Field
Helper text goes here.
Enter a complete email address.
Tinggi 48px, font 16px (mencegah auto-zoom di iOS). Error ditandai warna dan teks bantu, tidak warna saja. Fokus memakai outline teal-600. Label, id, dan aria-describedby dihubungkan otomatis.
Chip & badge
Chip (bisa diklik)
Badge status booking (usulan label, cocokkan dengan status backend)
Alert
Villa card
Villa name
Area, City
Price before taxRp X.XXX.XXX /night
Villa name with a longer title that truncates
Area, City
Price before taxRp X.XXX.XXX /night
Tiga state: default (tombol hati interaktif), sold out (opacity 65%, sama dengan kode), dan skeleton. Geometri dari home-property-card.tsx: foto radius 14, judul 13px bold, lokasi 12px, label "Price before tax" 11px, harga Fraunces 14px/800. Tombol hati memakai putih 92% (bukan blur). Badge "Instant" di web memakai emoji api; di kit diganti teks, tanpa emoji. Tidak ada komponen rating sampai ada data review nyata. Catatan kontras: harga (2.98:1), lokasi dan label (3.10:1), dan badge Sold Out (3.76:1) mengikuti nilai asli di kode. Isi image untuk foto asli.
Guest stepper
Adults
Optional hint text
Children
Optional hint text
Interaktif. Tombol menonaktifkan diri di batas min dan max; batas sebenarnya datang dari kapasitas villa, bukan dari kit.
Date range calendar
November 2026
Interaktif (klik awal lalu akhir). Tanggal tidak tersedia ditandai coret dan nonaktif, ilustrasi saja: kit tidak memvalidasi rentang yang melewati tanggal tidak tersedia, itu tugas logika booking. Minggu dimulai Senin. start, end, dan month bisa di-bind.
Price summary
Rp X.XXX.XXX × [N] nightsRp X.XXX.XXX
Fee nameRp X.XXX.XXX
TaxesRp X.XXX.XXX
TotalRp X.XXX.XXX
Rincian dan total selalu terlihat sebelum pembayaran. Komponen fee dan pajak mengikuti kebijakan harga sebenarnya; di sini hanya struktur.
Nav bawah mengambang (mobile)
Pil mengambang, bukan bar penuh: radius 30, border tipis, shadow float, item aktif berlatar teal-50 dengan teks dan ikon teal-600 (geometri dari MobileNav.svelte dan bottom-navbar.tsx). Empat tujuan: Home, Explore, Messages, Profile; Messages dan Profile meminta login, tahan lewat onnavigate dengan event.preventDefault(). Di produk pakai prop fixed (menempel 12px dari bawah plus safe area, hilang ≥ 768px). Angka "3" pada badge hanya contoh tampilan.
Modal dan Drawer
Modal mengikuti AuthDialog.svelte: lebar 480 (560 untuk form panjang), radius 30, header berlatar teal-25. Drawer mengikuti SearchFilterDrawer.svelte: penuh layar di mobile, panel 448px mulai 640px. Keduanya: Escape dan klik scrim menutup, fokus pindah ke dialog (bukan ke field, supaya keyboard layar sentuh tidak langsung muncul), fokus kembali ke pemicu saat ditutup, scroll halaman dikunci. Scrim memakai warna solid, tanpa blur.
Toast
Tampilan statis
Warna dan perilaku dari ToastContainer.svelte: hilang sendiri setelah 5 detik, tumpukan di atas tengah (maks 400px), role="alert" hanya untuk error. Pasang <ToastContainer /> sekali di layout root; panggil toasts.show({ type, title }) dari mana saja.
Checkbox, Switch, Counter
Area sentuh 44px. Kotak centang dan switch terisi teal-800 (lulus AA dengan tanda putih). Counter memakai IconButton, dan tombol kurang/tambah nonaktif di batas min/max.
Tabs
Tab terpilih: overview. Panah kiri/kanan berpindah tab. Untuk konten di dalam halaman; navigasi utama tetap TabBar.
EmptyState
No stays match your filters
Try other dates or remove a filter.
Bottom sheet
Drawer dengan side="bottom": menempel di bawah, sudut atas radius 30, ada pegangan kecil, tinggi maksimum 88% layar. Mulai 640px dipusatkan selebar 560px. Perilaku sama dengan Drawer samping.
PhotoGallery
Semua foto (5)
Mengikuti PropertyGalleryDrawer.svelte: kategori, foto utama dengan panah dan penghitung, lalu semua foto sebagai thumbnail. Tanpa src tampil kotak bernomor (foto di sini belum ada). Biasanya dibungkus Drawer atau Modal. Panah 44px, thumbnail aria-pressed.
Peta: bingkai dan penanda
Kit tidak membundel peta. Frontend memakai Leaflet; MapFrame hanya bingkai dan atribusi, MapMarker penanda harga yang bisa dipasang di penyedia mana pun. Penanda memakai teal-800 (teal-600 dengan teks putih 11px hanya 2.98:1 di kode), aktif berlatar ink-900. Popup memakai kelas vc-map-popup. Penyedia dan lisensi tile perlu diputuskan dev team: frontend saat ini memuat tile dari mt1.google.com langsung.
Pola validasi form
Aturan: validasi saat submit, bukan saat mengetik. Setiap field salah mendapat pesan di bawahnya (Field error, ditandai warna, ikon, dan teks), plus ringkasan di atas form yang menerima fokus dan menaut ke tiap field. Isian tidak dihapus saat gagal.

