Settings Page
The settings page pattern uses horizontal tabs with key-value form layouts. It provides a consistent structure for application configuration across portals.
Anatomy
+---------------------------------------------------------------+
| [Tab 1] [Tab 2] [Tab 3] [Tab 4] |
+---------------------------------------------------------------+
| |
| Section Title |
| Description text in text-2 |
| |
| Label (text-2, left) [Input / Value] (right) |
| Label [Toggle switch] |
| Label [Select dropdown] |
| |
| --- separator --- |
| |
| Section Title |
| ... |
+---------------------------------------------------------------+Tab Bar
- Horizontal tabs below the page header
- Active tab:
text-1,font-semibold, bottom border 2pxbrand - Inactive tab:
text-2, no border - Tab padding:
12px 16px - Tab bar bottom border: 1px
border
Form Layout
Each setting row is a horizontal flex container:
| Element | Style | Width |
|---|---|---|
| Label | 14px, text-2, font-sans | 40% (min 200px) |
| Description (optional) | 12px, text-3, below label | Part of label column |
| Input / Control | Standard input styling | 60% (max 400px) |
Row spacing: 16px between rows. Sections separated by a <hr> with 24px margin.
Control Types
Text Input
html
<input class="w-full px-3 py-2 text-sm border border-border rounded-lg bg-card
focus:outline-none focus:border-brand focus:ring-1 focus:ring-brand/20" />Toggle Switch
Green when on (#2D6B4A), bg-hover when off. Size: 44x24px. Transition: 150ms ease.
Select Dropdown
Same styling as text input with a Lucide ChevronDown icon on the right.
Notification Matrix
A checkbox grid for notification preferences:
| Email | Push | SMS |
| Transaksi baru | [x] | [x] | [ ] |
| Stok rendah | [x] | [ ] | [ ] |
| Approval request | [x] | [x] | [x] |
| Laporan harian | [x] | [ ] | [ ] |- Column headers: uppercase, 11px,
text-2 - Checkboxes: 18x18px, rounded-[4px], green check when selected
Usage
| App | Tabs |
|---|---|
| ho-finance | Umum, Notifikasi, Integrasi, Keamanan |
| store-admin | Toko, Perangkat, Notifikasi, Akses |
| stock-app-web | Warehouse, Alerts, Users |
React Component
tsx
interface SettingsSection {
title: string
description?: string
fields: SettingsField[]
}
type SettingsField =
| { type: 'text'; label: string; value: string; onChange: (v: string) => void; placeholder?: string }
| { type: 'toggle'; label: string; description?: string; value: boolean; onChange: (v: boolean) => void }
| { type: 'select'; label: string; value: string; options: { value: string; label: string }[]; onChange: (v: string) => void }
export function SettingsPage({ tabs, activeTab, onTabChange, sections }: {
tabs: { id: string; label: string }[]
activeTab: string
onTabChange: (id: string) => void
sections: SettingsSection[]
}) {
return (
<div>
{/* Tab Bar */}
<div className="flex border-b border-border mb-6">
{tabs.map(tab => (
<button
key={tab.id}
onClick={() => onTabChange(tab.id)}
className={cn(
'px-4 py-3 text-sm transition-colors',
activeTab === tab.id
? 'text-1 font-semibold border-b-2 border-brand'
: 'text-2 hover:text-1'
)}
>
{tab.label}
</button>
))}
</div>
{/* Sections */}
<div className="space-y-8 max-w-2xl">
{sections.map((section, i) => (
<div key={i}>
{i > 0 && <hr className="border-border mb-8" />}
<h3 className="text-base font-semibold text-1 mb-1">{section.title}</h3>
{section.description && (
<p className="text-sm text-2 mb-4">{section.description}</p>
)}
<div className="space-y-4">
{section.fields.map((field, j) => (
<SettingsRow key={j} field={field} />
))}
</div>
</div>
))}
</div>
</div>
)
}Save Behavior
- Auto-save with debounce (500ms) for toggles and selects
- Explicit "Simpan" button for text inputs (appears only when changed)
- Show "Tersimpan" toast briefly after successful save
Keyboard navigation
All form controls must be reachable via Tab key. Toggle switches respond to Space. Select dropdowns open with Enter or Space.
Don't
- Don't use vertical tabs --- always horizontal on settings pages
- Don't nest settings more than 2 levels deep
- Don't put destructive actions (delete account, reset data) in regular settings sections --- isolate them in a "Zona Bahaya" section at the bottom with red styling