Skip to content

Settings Page

The settings page pattern uses horizontal tabs with key-value form layouts. It provides a consistent structure for application configuration across portals.

Toko
Printer
Koneksi
Informasi Toko
Pengaturan dasar untuk identitas toko
Nama Toko
Sudirman
Kode Toko
STR-JKT-001
IP Printer
Mode Training

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 2px brand
  • 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:

ElementStyleWidth
Label14px, text-2, font-sans40% (min 200px)
Description (optional)12px, text-3, below labelPart of label column
Input / ControlStandard input styling60% (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

AppTabs
ho-financeUmum, Notifikasi, Integrasi, Keamanan
store-adminToko, Perangkat, Notifikasi, Akses
stock-app-webWarehouse, 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

RetailOS - Sistem ERP Retail Modern untuk Indonesia