Button
Buttons trigger actions. Settld buttons are pill-shaped by default, use warm ink as the primary surface, and scale down 3% on press to feel physical. Every button gets a press state — no exceptions.
Variants
Primary is the default action — ink surface, paper text. Used once per visible group (one primary per form, one per card). On dark surfaces, primary inverts to paper-on-ink.
Secondary is the alternative — paper surface with a hairline border. Pairs with primary for cancel/confirm patterns.
Ghost is the quiet option — no fill, no border. Used for tertiary actions, "skip" flows, inline actions in lists.
Teal is reserved for the settle action. There should only be one teal button visible at any time. It means "money moves now."
Danger is reserved for destructive actions — remove from group, delete expense. Always requires confirmation.
Sizes
| Size | Class | Font | Padding | Use case |
|---|---|---|---|---|
| Large | .btn-lg | 16px / 600 | 14px 28px | Hero CTAs, full-width confirm |
| Medium | .btn-md | 15px / 600 | 10px 22px | Default — forms, cards, modals |
| Small | .btn-sm | 13px / 600 | 7px 16px | Inline actions, list items |
| Extra small | .btn-xs | 12px / 600 | 5px 12px | Compact UI, table rows |
States
| State | Visual change | Transition |
|---|---|---|
| Default | Resting appearance | — |
| Hover | Background shifts to --teal-deep (primary) or --s-warm (secondary) | 140ms ease-out |
| Pressed | scale(0.97) | 140ms ease-out |
| Focused | 2px outline in --teal, 2px offset | Instant |
| Disabled | 40% opacity, cursor: not-allowed | — |
| Loading | Text hidden, centered spinner, pointer-events off | Spinner: 600ms linear infinite |
On dark surfaces
On dark surfaces (moments section, dark cards), primary inverts: paper background with ink text. Secondary gets a translucent border. Ghost lightens. Teal remains teal — it has enough contrast on both surfaces.
With icons and full width
Icons go after the label for forward actions (→), before for back actions (←). Settld prefers ASCII arrows over icon libraries. Full-width buttons use .btn-block and are always large size.
Anatomy
| Part | Element | Notes |
|---|---|---|
| Container | <button> or <a> | Always pill-shaped (border-radius: 999px). Never square, never rounded-rect. |
| Label | Text node | Sentence case. 1-3 words. Verb-led for actions ("Add expense", not "Expense addition"). |
| Leading icon | Optional | For back navigation only (←). Never decorative. |
| Trailing icon | Optional | Forward arrows (→) and delta indicators (↗) only. |
Usage
Accessibility
Tab. Activated by Enter or Space. Focus ring: 2px solid var(--teal) with 2px offset. Never remove the focus outline.
<button> for actions, <a> for navigation. Icon-only buttons require aria-label. Loading buttons use aria-busy="true" and keep the original label in aria-label.
Code
HTML
<!-- Primary button --> <button class="btn btn-primary btn-md">Add expense</button> <!-- With trailing arrow --> <button class="btn btn-teal btn-lg">Settle up <span>→</span></button> <!-- Full width --> <button class="btn btn-primary btn-lg btn-block">Confirm</button> <!-- Disabled --> <button class="btn btn-primary btn-md" disabled>Add expense</button> <!-- Loading --> <button class="btn btn-primary btn-md btn-loading" aria-busy="true" aria-label="Adding expense">Add expense</button>
CSS classes
| Class | Purpose |
|---|---|
.btn | Base — resets, flex, transitions, press state |
.btn-primary | Ink fill, paper text |
.btn-secondary | Paper fill, hairline border |
.btn-ghost | Transparent, ink-body text |
.btn-teal | Teal fill — settle/pay actions only |
.btn-danger | Coral fill — destructive actions |
.btn-lg / .btn-md / .btn-sm / .btn-xs | Size modifiers |
.btn-block | Full-width |
.btn-icon | Circular, equal width/height |
.btn-loading | Spinner overlay, disables interaction |
Design tokens used
| Token | Value | Role |
|---|---|---|
--ink | #171513 | Primary button fill |
--s-paper | #FBF8F0 | Primary button text, secondary fill |
--teal | #0E7C66 | Teal button fill |
--teal-deep | #0A5F4F | Primary/teal hover |
--error | #E76F51 | Danger button fill |
--ink-line | #DDD2B8 | Secondary button border |
--r-pill | 999px | Border radius |
--d-fast | 140ms | All transitions |
--ease-out | cubic-bezier(0.23,1,0.32,1) | Easing curve |