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

SizeClassFontPaddingUse case
Large.btn-lg16px / 60014px 28pxHero CTAs, full-width confirm
Medium.btn-md15px / 60010px 22pxDefault — forms, cards, modals
Small.btn-sm13px / 6007px 16pxInline actions, list items
Extra small.btn-xs12px / 6005px 12pxCompact UI, table rows

States

StateVisual changeTransition
DefaultResting appearance
HoverBackground shifts to --teal-deep (primary) or --s-warm (secondary)140ms ease-out
Pressedscale(0.97)140ms ease-out
Focused2px outline in --teal, 2px offsetInstant
Disabled40% opacity, cursor: not-allowed
LoadingText hidden, centered spinner, pointer-events offSpinner: 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

Add expense
Container (pill, --r-pill)
Label
Trailing icon
PartElementNotes
Container<button> or <a>Always pill-shaped (border-radius: 999px). Never square, never rounded-rect.
LabelText nodeSentence case. 1-3 words. Verb-led for actions ("Add expense", not "Expense addition").
Leading iconOptionalFor back navigation only (). Never decorative.
Trailing iconOptionalForward arrows () and delta indicators () only.

Usage

Do Use one primary button per visible group. Pair with secondary for cancel/confirm. Verb-first labels: "Add expense", "Settle up", "Remove".
Don't Don't use multiple primary buttons side-by-side. Don't use noun-only labels ("Expense", "Settings"). Don't use ghost for primary actions.
Do Use teal exclusively for settle/pay flows. Use danger with a confirmation step. Use full-width for the single most important action on a screen.
Don't Don't use teal for non-monetary actions. Don't use danger without confirmation. Don't mix sizes in the same button group.

Accessibility

Keyboard Buttons are focusable via Tab. Activated by Enter or Space. Focus ring: 2px solid var(--teal) with 2px offset. Never remove the focus outline.
Screen readers Use <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.
Contrast Primary (ink on paper): 12.5:1 ratio. Teal on paper: 4.8:1. Both exceed WCAG AA. Disabled state at 40% opacity may fall below AA — this is intentional since disabled elements are non-interactive.

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

ClassPurpose
.btnBase — resets, flex, transitions, press state
.btn-primaryInk fill, paper text
.btn-secondaryPaper fill, hairline border
.btn-ghostTransparent, ink-body text
.btn-tealTeal fill — settle/pay actions only
.btn-dangerCoral fill — destructive actions
.btn-lg / .btn-md / .btn-sm / .btn-xsSize modifiers
.btn-blockFull-width
.btn-iconCircular, equal width/height
.btn-loadingSpinner overlay, disables interaction

Design tokens used

TokenValueRole
--ink#171513Primary button fill
--s-paper#FBF8F0Primary button text, secondary fill
--teal#0E7C66Teal button fill
--teal-deep#0A5F4FPrimary/teal hover
--error#E76F51Danger button fill
--ink-line#DDD2B8Secondary button border
--r-pill999pxBorder radius
--d-fast140msAll transitions
--ease-outcubic-bezier(0.23,1,0.32,1)Easing curve