Promise Design System
The language behind Promise — tokens, principles, and core components.
Overview
Promise is an enterprise e-procurement product used by procurement officers, initiators, supervisors, and evaluation committees within a single tenant. This system exists to keep the product visually calm, information-dense, and trustworthy — and to let anyone invent new components that feel like they belong.
Think of this document as a grammar, not a catalog. Memorise the primitives (§ foundations), internalise the principles (§ below), and the components will compose themselves. When you need something new, return to the tokens.
Principles
If a new component feels wrong, it usually violates one of these. Check here first.
Hairlines over shadows
Our surfaces separate by 1px hairlines. Shadows are reserved for true elevation (popovers, modals). Never stack shadows on inline surfaces.
Flat fills — never gradients
One color per region. Tints convey state (action-50, danger-50). Gradients add visual noise and fight the data.
Monochrome first, color for meaning
90% of the UI is ink on neutral surfaces. Color only appears when it communicates: action, status, warning. If it could be a chip, don't make it a button.
Typography earns its weight
13px is body. Headings jump, never creep. Use weight (500/600/700) not size for hierarchy within a block. Captions are 11–12px, uppercased.
Density is a feature
Procurement users scan thousands of rows. Pack information with rhythm (4px baseline, consistent control heights) so density doesn't become clutter.
Numbers are monospaced
IDR amounts, codes, IDs, timestamps — always in JetBrains Mono so columns of numbers align without tabular-nums gymnastics.
Role, not permission, drives the UI
The user sees the same app in a different shape based on active role. Gate actions by role; never show disabled UI where an entire view would be wrong.
One place for one thing
If two components answer the same question (e.g. Back button and breadcrumb), kill one. Duplication leaks into decision fatigue.
Color
Two-tier system: primitives hold values, semantics express intent. Always consume the semantic token in components so dark mode flips automatically.
Typography
Inter for UI, JetBrains Mono for numbers and codes. Eight sizes cover every need — resist adding a ninth.
Spacing
4px baseline grid. Named anchors for rhythm; never invent values in between.
Every interactive control snaps to one of these four heights. Combined with consistent padding, this is what creates the product's rhythm.
Radii
Smaller radii feel technical and trustworthy. We avoid fully rounded except on pills.
Elevation
Sparse by design. Inline UI uses hairlines; shadows signal true layering (popovers, drawers, modals).
Motion
Motion confirms cause-and-effect. Never decorates. Three durations, two easings.
Iconography
Lucide — 1.5px stroke, 24×24 viewBox. Render at three sizes.
Buttons
Four variants cover every case. If you need a fifth, compose from existing ones (e.g. btn-secondary + danger text).
md. Use sm in table rows and menu bars, lg only for top-of-form primary CTAs.Forms
36px control height. Labels above. Required is a subtle red dot, not a yelling asterisk.
Chips & status
Pill-shaped, 22px tall, with an optional leading status dot. Use chips for state — Draft, In Review, Approved. Never as a button.
Panels
The primary container. Panel = surface + 12px radius + hairline + optional head with title/desc + actions on the right.
Banners
Contextual information that belongs above or inside a panel. Four intents map to semantic colors.
Tables
Dense by default. Header is uppercase 11px ink-4 on surface-subtle. Rows separate by hairlines, hover on surface-subtle. Numbers right-aligned and mono.
| PR Number | Item | Category | Qty | Total |
|---|---|---|---|---|
| PR-24021 | Feasibility Study — Wellsite A | Consultancy | 1 | Rp12,000,000 |
| PR-24021 | EIA Report — Wellsite A | Consultancy | 1 | Rp8,000,000 |
| PR-24038 | Site Drone Survey | Technical | 3 | Rp4,500,000 |
Stepper
Wizard with many steps → horizontal scroll. Circle is ring-only (white fill), check for done, ring-+-number for active, neutral for pending.
Role switcher
Promise is single-tenant, multi-role per user. The switcher lives at the top of the sidebar because role is context, not account.
Detail layout
Two-column: content (1fr) + rail (320px). Title and meta at top, stepper under, then stacked panels. Rail carries summary cards (Readiness, Recent activity).
Density & rhythm
Vertical rhythm = 4px. Horizontal rhythm = 14–20px gutters. Every new component must snap to these.
- Row padding-y: 10px (table rows, menu items, nav items)
- Panel body padding: 20px; head padding: 14px 20px
- Card gutter between panels: 16px
- Section gutter on a page: 32px
- Icon-to-label gap: 6px (buttons, chips), 10px (nav items)
- Inline control gap: 8px (default), 10px (two buttons)
All tokens
Every design decision as a CSS variable. Source of truth: assets/tokens.css.
| Token | Value | Intent |
|---|
Tailwind config
Drop into your app's tailwind.config.js as a preset. Semantic colors map to CSS variables, so dark mode flips via [data-theme="dark"].