Design System Panduan visual dan komponen untuk LabaBersih v2 — 31 Maret 2026

Dokumen ini adalah satu-satunya referensi untuk semua keputusan visual di LabaBersih v2. Setiap rule ada karena masalah nyata dari v1 atau prinsip UX yang terbukti. Tujuannya bukan bikin "cantik" — tujuannya bikin Nelly (admin, 300 order/hari) dan Hafish (finance, cek laporan tiap pagi) bisa kerja cepat dan tanpa mikir.

Referensi industri: Shopify Polaris (structure & density), Stripe Dashboard (financial data), Xero (accounting reports). Bukan karena ikut-ikutan — karena jutaan user sudah terbiasa dengan pattern mereka (Jakob's Law).

1. Filosofi & Prinsip

Unified Surface

Pattern utama LabaBersih: satu container luar membungkus data terkait. Internal separation pakai divider tipis, bukan border/card terpisah. Ini mengurangi visual noise dan membuat data terasa sebagai satu kesatuan.

v1 punya masalah inkonsistensi — badge beda style di setiap halaman. Design system ini solve masalah itu dengan mendefinisikan satu sumber kebenaran untuk setiap komponen.

Benar — Unified Surface
Pesanan Hari Ini
PS2603-001Rp 200.000
PS2603-002Rp 135.000
PS2603-003Rp 450.000

1 container, divider tipis antar row. Clean, kohesif.

Salah — Card Per Item
PS2603-001Rp 200.000
PS2603-002Rp 135.000
PS2603-003Rp 450.000

Terlalu banyak border dan shadow. Visual noise. "AI banget".

Content-First

Setiap elemen di halaman harus punya alasan. Kalau gak bantu user selesaikan tugasnya — buang. Dashboard v1 punya 8 stat cards + chart + cash flow + alert. Hafish bilang: "saya buka dashboard, gak tau harus lihat apa dulu." v2: 4 stat cards saja. Less is more.

Mobile-First

Semua komponen dirancang untuk mobile terlebih dahulu, kemudian di-enhance untuk layar besar. Bottom nav di mobile, sidebar di desktop. Touch target minimal 44px.

Prinsip Inti

PrinsipArtinya untuk LabaBersihSumber
Consistency1 component = 1 style di SEMUA halaman. Badge "dikirim" harus warna yang sama di order, RTS, rekonsil.Nielsen #4
Recognition > RecallTampilkan nama produk + SKU, jangan suruh user hafal kode. Akun tampil nama, bukan cuma kode.Nielsen #6
7 ± 2 itemsTabel max 7 kolom. Stat cards max 4 per row. Sidebar max 8 menu utama.Miller's Law
Response < 400msProductivity naik drastis. Target TTFB < 300ms. Loading state kalau > 200ms.Doherty Threshold
Don't Make Me Think5-second test: buka halaman, langsung tau ini apa dan mau ngapain.Steve Krug

2. Warna

Brand Green Palette

Warna utama LabaBersih: #00674F (green-600). Semua shade di-derive dari value ini. Di codebase, SELALU gunakan CSS variable / Tailwind token — jangan hardcode hex.

50#eefbf5
100#d0f5e3
200#a4eacd
300#6ad8b0
400#30be8e
500#0d9e73
600#00674F
700#005742
800#004536
900#00392d
950#001f1a

green-600 = primary. green-50 = hover background. green-700 = button hover.

Badge Tones (Emerald-Harmonic)

Warna badge lembut tapi tetap jelas perbedaannya. Background soft, text gelap. Bukan warna solid mencolok. Dibuat harmonis dengan brand green.

5 Varian Badge
Success Error Warning Info Neutral

Status Mapping

Setiap status punya warna FIXED. Gak boleh beda antar halaman — ini yang bikin v1 "AI banget".

StatusToneRendered
DibuatNeutralDibuat
DikemasInfoDikemas
DikirimWarningDikirim
SelesaiSuccessSelesai
RTSErrorRTS
DibatalkanErrorDibatalkan
LunasSuccessLunas
Belum BayarErrorBelum Bayar

Platform Colors

Platform
Shopee
#ee4d2d
Shopee Light
#fff0ec
TikTok
#010101
TikTok Accent
#00f2ea

Special & Gray

Profit, Loss & Gray
Profit #00674F
Loss #dc2626
gray-100 #e5e7eb
gray-200 #d5d7db
gray-300 #b0b3ba

CSS Variables

Kenapa token penting: kalau warna berubah, cukup ganti di 1 tempat. Kalau dark mode nanti, override di 1 tempat. Tanpa token = hardcode di 100 tempat = inkonsisten.

:root {
  --green-600: #00674F;          /* Primary brand */
  --badge-success-bg: #BCF1DD;   /* Badge: selesai, lunas */
  --badge-error-bg: #FFD8D8;     /* Badge: rts, error */
  --badge-warning-bg: #FDDEB3;   /* Badge: dikirim */
  --badge-info-bg: #DFE3EA;      /* Badge: dikemas */
  --badge-neutral-bg: #E2E7E5;   /* Badge: dibuat */
  --profit: #00674F;
  --loss: #dc2626;
}

3. Tipografi

Font

Geist (Google Fonts) untuk sans-serif. Geist Mono untuk monospace (ID, SKU, kode akun, angka uang). Fallback: system-ui, -apple-system, sans-serif. Mono fallback: SF Mono, Menlo, monospace.

Scale

Setiap level punya penggunaan spesifik. Jangan campur level — hierarchy harus jelas.

Typography Scale
Page title Pesanan text-lg font-semibold text-gray-900
Section title Info Pesanan text-sm font-semibold text-gray-900
Card title Detail Item text-sm font-semibold text-gray-900
Stat label Total Order text-xs font-medium text-gray-500
Stat value 2.847 text-2xl font-bold text-gray-900
Body Pesanan ini dibuat pada 30 Mar 2026 text-sm text-gray-700
Form label Nama Produk text-sm font-medium text-gray-700
Helper Maksimal 100 karakter text-xs text-gray-400
Error Nama produk wajib diisi text-xs text-red-600
Badge Dikirim text-[13px] font-semibold
Monospace PS2603-00001 font-mono
Delta +12.5% text-[10px] font-semibold

Named Tokens (v2)

Gunakan nama semantik di code, bukan raw class. Contoh: bukan text-2xl font-bold, tapi wrap dalam component <.stat_value>.

TokenTailwind ClassKapan
page-titletext-lg font-semibold text-gray-9001x per halaman, paling atas
stat-valuetext-2xl font-bold text-gray-900Angka di stat card
stat-labeltext-xs font-medium text-gray-500Label di stat card
table-headertext-xs font-medium text-gray-500Header kolom tabel
mono-idfont-mono text-sm text-gray-700ID, SKU, kode akun
moneyfont-mono tabular-nums text-sm text-rightSemua angka uang

4. Spacing & Layout

Spacing Constraints

Pakai Tailwind spacing scale (kelipatan 4px). JANGAN arbitrary values. Constraint = konsistensi.

Spacing Scale
1 — 4px
Icon + text gap
2 — 8px
Badge padding, small gap
3 — 12px
Table cell padding
4 — 16px
Card padding, form gap
5 — 20px
Card padding (larger)
6 — 24px
Between cards / sections
8 — 32px
Between major sections

App Shell

Desktop: sidebar kiri (w-60 = 240px), konten utama push kanan (lg:pl-60). Mobile: tanpa sidebar, bottom nav, konten full-width dengan pb-24 untuk clearance bottom nav.

App Shell Layout
Sidebar
Dashboard
Pesanan
Produk
Jurnal
Konten Utama
37
Dikemas
2.313
Dikirim
2.271
Selesai
22
RTS
Table / Data

Z-Index Scale

LayerZ-IndexContoh
Base0Konten utama
Dropdown10Combobox list, tooltip
Sticky20Table header, form footer
Sidebar30Desktop sidebar
Overlay40Modal backdrop
Modal50Dialog content
Toast60Notification flash

Touch Rules

Minimum touch target: 44px. Gunakan touch-action: manipulation pada semua interactive elements. Viewport: maximum-scale=1, user-scalable=no untuk mencegah zoom tidak sengaja pada form.

5. Border, Radius & Shadow

Radius

Border Radius
rounded-xl
Container
rounded-lg
Controls
rounded-full
Badge
L
rx="8"
Logo

Shadow

Shadow Levels
shadow-sm
Card default
shadow-lg
Dropdown
shadow-2xl
Modal
none
Flat card

Border & Divider

Divider Hierarchy
Section divider — gray-100
Row divider — gray-50
Row divider — gray-50
Last row — tanpa divider

gray-200 hanya untuk emphasis divider atau border yang butuh kontras lebih (jarang dipakai).

6a. Button

4 varian, 2 ukuran. Primary untuk aksi utama, secondary untuk aksi pendukung, danger untuk destructive, ghost untuk aksi minor tanpa emphasis.

Button Variants — md
Button Variants — sm
States: Disabled & Loading

Placement Rules

2 Buttons
3 Buttons

Primary (Simpan) selalu di kanan. Cancel (Batal) di kiri primary. Danger (Hapus) di far left, terpisah jauh dari primary — Fitts's Law.

6b. Badge

PENTING: Warna badge via inline style, BUKAN Tailwind classes. Ini memastikan konsistensi di semua konteks termasuk dark mode dan SSR.
Status Badges
Dibuat Dikemas Dikirim Selesai RTS Dibatalkan
Payment & Platform Badges
Lunas Belum Bayar TikTok Shopee
<!-- Badge inline style pattern -->
<span style="
  display:inline-flex;align-items:center;
  padding:2px 8px;border-radius:999px;
  font-size:13px;font-weight:600;
  background:#BCF1DD;color:#004E38;
">Selesai</span>

6c. Card

Card Variants
Default Card
rounded-xl, border-gray-100, bg-white, p-5, shadow-sm
Profit Card
+ border-l-4 border-l-[#00674F]
Loss Card
+ border-l-4 border-l-[#dc2626]

6d. CollapsibleCard

Collapsible Card (klik untuk toggle)
Fee Estimasi
Admin PlatformRp 6.000
Komisi DinamisRp 10.000
Total FeeRp 16.000

Klik header untuk toggle konten. Chevron rotate 180 derajat. Gunakan untuk detail yang gak selalu perlu dilihat.

6e. Stats Row

Aturan: SELALU 4 kolom. JANGAN pernah 3. Stat cards pakai divide-x dalam 1 container, bukan card terpisah.
Stats Row
Dikemas
37
Dikirim
2.313
Selesai
2.271
RTS
22

6f. Segmented Control

Tab Filter

Container: bg-gray-100 rounded-lg p-1. Active tab: bg-white shadow-sm. Inactive: text-gray-500.

6g. Unified Surface (Filter + Table)

CSS Grid, BUKAN <table>. Data tabular pakai grid-cols-[...]. Ini memberikan kontrol lebih untuk responsive dan styling.
Filter + Table
ID Pesanan Customer Platform Status Total
PS2603-001 Budi Santoso TikTok Selesai Rp 200.000
PS2603-002 Siti Rahayu Shopee Dikirim Rp 135.000
PS2603-003 Andi Wijaya TikTok RTS Rp 450.000

6h. Form & Input

FormField — Normal & Error
Maksimal 100 karakter
SKU wajib diisi

Input Variants

Text, Currency, Percentage, Select
Rp
%

6i. SearchableCombobox

WAJIB untuk daftar > 7 items dari database (akun, produk, supplier).
Searchable Combobox
12-201 Piutang TikTok Bestari
12-202 Piutang Shopee Bestari
12-400 Piutang Klaim Kurir

Active item: bg-green-50 text-green-700. Keyboard: Arrow up/down, Enter select, Escape close.

6j. Toggle

Toggle ON = gray-900, BUKAN green. Green hanya untuk brand action/CTA.
Toggle States
ON
OFF
Confirm Modal (Danger)
Hapus Pesanan?
Pesanan PS2603-001 akan dihapus permanen. Stok dan jurnal terkait akan di-reverse. Aksi ini tidak bisa dibatalkan.

6l. Empty & Loading State

Empty State — Full
Belum ada pesanan
Buat pesanan pertama atau import dari XLSX
Empty State — Compact
Belum ada data
Data akan muncul setelah order diproses
Loading State
Memuat...

6m. Save Indicator & Tooltip

SaveIndicator States
Menyimpan...
Tersimpan
Gagal menyimpan
Tooltip (CSS only, hover)
HPP ?

Hover pada (?) untuk melihat tooltip. Di production, gunakan CSS :hover selector.

6n. Error States

SectionErrorBoundary
Gagal memuat data jurnal
Terjadi kesalahan saat mengambil data. Silakan coba lagi.
NotFoundCard
Data tidak ditemukan
Halaman yang Anda cari tidak ada atau sudah dihapus.
← Kembali ke daftar

6o. Infinite Scroll

InfiniteScrollSentinel
Semua data sudah ditampilkan

6p. Offline Banner

Status Banners
Koneksi terputus. Perubahan akan disimpan saat online.
Menyinkronkan perubahan...

6q. DateRangeFilter

Date picker dengan preset shortcuts (Hari Ini, 7 Hari, 30 Hari, Bulan Ini) + custom date range. HANYA untuk daftar pendek (< 8 items) pakai Select biasa. Untuk range tanggal, pakai component ini.

Trigger (Closed)
7 Hari Terakhir
Dropdown (Open)
Hari Ini
7 Hari Terakhir
30 Hari Terakhir
Bulan Ini
Custom Range
Classes (v1 reference):
Trigger closed: flex items-center gap-2 rounded-lg border border-gray-200 px-3 py-2 text-sm font-medium text-gray-700 hover:bg-gray-50
Trigger open:   border-green-500 ring-2 ring-green-500/20
Dropdown:       absolute z-20 mt-1.5 w-80 rounded-xl border border-gray-100 bg-white shadow-lg
Active preset:  bg-green-50 font-medium text-green-700
Inactive:       text-gray-700 hover:bg-gray-50
Date input:     rounded-lg border border-gray-200 px-2.5 py-1.5 text-xs focus:border-green-500 focus:ring-2 focus:ring-green-500/20

6r. MetricCardChart (Marketing)

Digunakan di halaman marketing — BUKAN di list page (list page pakai Stats Row). Pattern: grid metric cards yang bisa diklik (max 2 aktif), connected ke area chart dual Y-axis.

Metric Cards (2 active, 3 inactive)
Spend
Rp 12,5jt
+18.2% L
Revenue
Rp 45,8jt
+24.1% R
ROAS
3.66x
+5.0%
Orders
847
+12.3%
CPC
Rp 850
+8.7%
Delta coloring: Hijau = bagus, merah = buruk. Tapi untuk cost metrics (Spend, CPC), naik = buruk (merah). Ini dikontrol via invertDelta. CPC naik 8.7% → merah, karena biaya naik itu negatif.
Chart Area (placeholder)
Area chart dual Y-axis — Spend (left, green) + Revenue (right, blue). Gradient fill, no dots, active dot on hover.
Pattern:
  Active card:   border-color: {metric.color}40, bg: {metric.color}08, L/R badge
  Inactive card: border-gray-100 bg-white
  Delta badge:   text-[10px] font-semibold, green=good red=bad, invertDelta untuk cost
  Format:        rp → "Rp 1,2jt", persen → "12.5%", angka → locale int

7. Ikon

Stroke-based SVG, Heroicons style. viewBox="0 0 24 24", stroke-width="1.5" atau "2". Gunakan currentColor supaya warna ikut text color parent.

home
plus
trash
search
check
chevron-down
shopping-bag
user
exclamation
cog

8. Pola Keuangan (Accounting UI)

Pattern spesifik untuk halaman akuntansi. Semua angka pakai font-mono tabular-nums. Negatif = text-red-600. Nol = em dash "—". Referensi: Xero, QuickBooks, A2X, Zoho Books.

8a. Laporan Keuangan — Report Tree

Laporan Laba Rugi dan Neraca menggunakan tree hierarchy. Group header collapsible, child rows indented, total rows bold dengan border atas tebal.

Laba Rugi — Tree Report
Laba Rugi — Maret 2026
Akun Jumlah %
Pendapatan Rp 715.000.000 100%
Pendapatan TikTok Bestari Rp 450.000.000 62.9%
Pendapatan Shopee Bestari Rp 200.000.000 28.0%
Pendapatan Mengantar Rp 65.000.000 9.1%
Harga Pokok Penjualan -Rp 361.000.000 50.5%
LABA KOTOR Rp 354.000.000 49.5%
Beban Operasional -Rp 224.000.000 31.3%
Biaya Platform -Rp 120.000.000 16.8%
Biaya Pengiriman -Rp 50.000.000 7.0%
Beban Kerugian -Rp 54.000.000 7.6%
LABA BERSIH Rp 130.000.000 18.2%

8b. Bagan Akun — Tree View

Chart of Accounts — Grouped Tree
ASET (23 akun) Rp 49.847.720
11-000 KAS & BANK Rp 15.584.000
11-100 Kas Rp 15.400.000 Edit
11-311 Saldo TikTok Bestari Rp 184.000 Edit
12-201 Piutang TikTok -Rp 54.280 Edit
12-400 Piutang Klaim Kurir Edit

8c. Buku Besar — Running Balance

General Ledger — 12-201 Piutang TikTok
Tanggal Referensi Deskripsi Debit Credit Saldo
Saldo Awal Rp 0
15 Mar JE2603-042 Penjualan TikTok 184.000 184.000
20 Mar JE2603-101 Rekonsiliasi 184.000 Rp 0
Total 184.000 184.000 Rp 0

8d. Rekonsiliasi — Fee Breakdown

Fee Matching Detail — PS2603-00042
Gross
Rp 200.000
Piutang Estimasi
Rp 184.000
Settlement Aktual
Rp 182.500
Selisih
-Rp 1.500
Fee Estimasi Aktual Selisih Status
Admin Platform 6.000 6.500 -500 Selisih
Komisi Dinamis 10.000 10.000 0 Cocok
Proses Pesanan Fee baru
1.000 -1.000 Unknown
Total Fee 16.000 17.500 -1.500

8e. Konsolidasi Multi-Entity

Laba Rugi Konsolidasi — 2 Entitas
Akun PT Bestari CV Maju Konsolidasi
Pendapatan Rp 500jt Rp 215jt Rp 715jt
HPP -Rp 250jt -Rp 111jt -Rp 361jt
LABA BERSIH Rp 100jt Rp 30jt Rp 130jt

8f. Format Angka

Semua Format Angka
Normal Rp 1.250.000
Negatif -Rp 500.000
Nol
Positif (sign) +Rp 150.000
Short (dashboard) Rp 45,8jt
Persentase 18,2%
Delta positif +12.5%
Delta negatif -8.7%

9. Pola Halaman

8a. List Page

Urutan FIXED: Header → Stats Row → Segmented Control → Filter Bar → Table → Pagination. Jangan dibolak-balik.

List Page Wireframe
Pesanan
+ Buat Pesanan
Dikemas
37
Dikirim
2.313
Selesai
2.271
RTS
22
Semua Dikemas Dikirim
Cari...
Platform
IDCustomerStatusTotal
PS2603-001 · Budi · Selesai · Rp 200k
PS2603-002 · Siti · Dikirim · Rp 135k
PS2603-003 · Andi · RTS · Rp 450k

8b. Detail Page

Back nav → Header card (title + actions) → Info grid (2-3 kolom) → Detail sections → Activity timeline.

8c. Form Page

PageLayout → Card dengan form fields (grid 2 kolom) → Sticky footer: [Hapus] ← | → [Batal] [Simpan].

Untuk form panjang yang bisa discroll, footer sticky di bawah memastikan user selalu bisa akses action button tanpa scroll.

9a. Desktop Sidebar

Sidebar (w-60, border-r)
LabaBersih
Dashboard
PESANAN
Pesanan
Packing
AKUNTANSI
Jurnal

Active nav item: bg-green-50/60 text-green-700 font-medium. Expandable sub-items: pl-11 indent.

9b. Mobile Bottom Nav

Bottom Navigation (5 tabs)
Dashboard
Produk
Pesanan
Gudang
Lainnya

11. Interaksi

SEMUA elemen interaktif harus punya transition-colors (duration 150ms). Jangan pernah ada perubahan warna yang "instan".

Hover States

ElementNormalHover
Primary buttonbg-green-600bg-green-700
Secondary buttonbg-white border-gray-200bg-gray-50
Table rowbg-transparentbg-gray-50
Nav linktext-gray-500bg-gray-50 text-gray-900
Ghost buttontext-green-600underline

Focus States

Input focus: ring-2 ring-green-600/20 border-green-400. Error focus: ring-2 ring-red-600/10 border-red-500. Button focus: ring-2 ring-green-600/20.

Disabled

opacity-50 cursor-not-allowed pointer-events-none. Berlaku untuk semua element interaktif.

12. Responsif

BreakpointWidthLayout
Default (mobile)< 640pxFull width, bottom nav, stack semua
sm≥ 640px2-col form grid
lg≥ 1024pxSidebar visible, lg:pl-60

Pattern Umum

13. Lokalisasi

AspekAturanContoh
BahasaBahasa Indonesia"Simpan", "Batal", "Pesanan"
AngkaLocale id-ID, titik pemisah ribuan123.456
Mata uang"Rp " + angkaRp 150.000
Mata uang pendekJutaan disingkatRp 1,2jt
TanggalDD MMM YYYY25 Mar 2026
BulanJan, Feb, Mar, Apr, Mei, Jun, Jul, Ags, Sep, Okt, Nov, Des

14. Aksesibilitas

Polaris-level accessibility. Bukan "nice to have" — ini wajib. Terutama untuk keyboard navigation karena Nelly sering pakai barcode scanner yang mengirim input sebagai keyboard event.

AturanImplementasi
aria-label pada icon-only buttonSemua button tanpa text WAJIB punya aria-label
role="alert" pada errorError message, toast, flash — supaya screen reader baca langsung
Focus trap di modalTab cycle dalam modal, tidak keluar ke background
Focus visible ringKeyboard focus: ring-2 ring-offset-2 ring-green-600
Keyboard navTab = next, Shift+Tab = prev, Enter = activate, Escape = close/cancel
Contrast ratioWCAG AA minimum: 4.5:1 untuk text, 3:1 untuk large text
Contoh: Button dengan dan tanpa aria-label
Benar <button aria-label="Hapus pesanan"><TrashIcon /></button>
Salah <button><TrashIcon /></button>

15. Aturan Wajib

Do's

Unified Surface — 1 container, internal dividers
rounded-xl containers, rounded-lg controls
shadow-sm + border-gray-100 untuk card
green-600 sebagai primary color
Green focus ring pada semua input
transition-colors pada semua interaktif
Badge via inline style, bukan Tailwind classes
divide-gray-50 rows, divide-gray-100 sections
bg-gray-50/60 untuk table header
CSS Grid untuk data tabular
Stats 4-col selalu, tanpa exception
aria-label pada icon-only buttons

Don'ts

Card per row — gunakan Unified Surface
rounded-sm untuk containers — terlalu kecil
Gray focus rings — gunakan green
text-[10px] untuk body text — terlalu kecil
uppercase tracking-wider untuk body — hanya header
Raw hex di Tailwind — gunakan CSS variable/token
StatCard individual di list pages — gunakan Stats Row
border-gray-200 row separator — terlalu tebal
3-column stats — selalu 4
Emoji di UI — gunakan icon SVG
window.confirm() — gunakan ConfirmModal
<table> HTML — gunakan CSS Grid

16. 18 Keputusan Kunci

Setiap keputusan di bawah sudah final. Jangan deviate tanpa persetujuan Hafish.

  1. Unified Surface > Card-per-Row. Mengurangi visual noise. Data yang terkait harus dalam 1 container dengan divider tipis.
  2. Stats selalu 4 kolom, bukan 3. Konsistensi di semua halaman. Kalau data cuma 3, tambah 1 total/placeholder.
  3. "Batal" bukan "Kembali" di form. "Kembali" ambigu (navigasi?). "Batal" jelas: cancel action ini.
  4. Cancel = secondary button, bukan ghost. Ghost terlalu subtle. User harus bisa menemukan cancel dengan mudah.
  5. Toggle ON = gray-900, bukan green. Green reserved untuk brand action. Toggle cukup on/off visual.
  6. Auto-save indicator = gray. Informational, bukan action. Gak perlu warna mencolok.
  7. Delta coloring: invertDelta untuk cost metrics. Biaya naik = merah (buruk). Revenue naik = hijau (bagus). Konteks menentukan warna.
  8. CSS Grid > HTML table. Kontrol lebih baik untuk responsive, alignment, dan custom styling.
  9. Sticky footer untuk form panjang. User selalu bisa akses Simpan/Batal tanpa scroll ke bawah.
  10. Badge warna via inline style. Menjamin konsistensi di semua konteks (SSR, dark mode, PDF export).
  11. touch-action: manipulation. Disable double-tap zoom di semua interactive elements. Penting untuk barcode scanner.
  12. Viewport: maximumScale=1, userScalable=false. Mencegah zoom tidak sengaja di form input pada iOS.
  13. Font: Geist. Modern, clean, excellent readability. Monospace variant untuk angka dan ID.
  14. Max 7 kolom tabel. Miller's Law. Sisanya di detail page.
  15. Bahasa Indonesia 100%. Label, placeholder, error message, tooltip — semua Bahasa Indonesia.
  16. WIB timezone. Semua datetime tampil dalam WIB (+07:00), bukan UTC.
  17. Money format: "Rp " + titik ribuan. Negatif = "-Rp". Warna merah untuk negatif, hijau untuk positif (kalau pakai sign).
  18. Error message = bahasa user. "Stok tidak cukup (ada: 3, butuh: 5)" bukan "constraint violation".

17. Logo

Logo LabaBersih: Green square "L" + gold coin "$". SVG, rx="8" pada square. Render di 3 ukuran.

Logo Sizes — Light Background
L $
48px
L $
36px
L $
24px
Logo — Dark Background
L $
48px
L $
36px
L $
24px