Buttons

mandatory

Button system — 7 variants (filled, tonal, outlined, text, elevated, icon, FAB), button groups, segmented buttons. Material parity (`/components/all-buttons`, `/components/buttons`, `/components/button-groups`). Covers anatomy, hierarchy, states, accessibility, and per-preset shape variation.

Spec — Buttons

Facet *isual*do Koder Design. Material parity: https://m3.material.io/components/all-buttons.

Covers #049.21 (button groups), #049.22 (segmented), #049.23 (icon button), #049.24 (FAB / extended FAB).

7 button variants

Variant Visual Use
*illed* Solid accent bg + accent-on text Primary action (1 per surface)
*onal* Surface-variant bg + text Secondary action, less emphasis than filled
*utlined* Transparent bg + accent border + accent text Secondary, with stronger structure than text
*ext* No bg, no border, accent text Tertiary action, footer links, dismissive
*levated* Surface bg + shadow level 1 + accent text "Picked up" button when surrounding flat
*con* 24px icon, no label, surface or transparent bg Action without a label (toolbar)
*AB* Round (radius-full or xl) + accent fill + icon Primary action shortcut, floats above

Plus 1 derivative: *xtended FAB*= FAB with text label beside the icon. Same visual treatment, broader.

Anatomy

┌──────────────────────────┐
│  [icon?]  Label  [icon?] │   ← container (radius-sm default)
└──────────────────────────┘
   pad-x       pad-x
  • *ontainer* 40 px height default (32 px small, 56 px large)
  • *abel* label-large type role (14/20, weight 500)
  • *ptional leading icon* 18 px, 8 px gap to label
  • *ptional trailing icon* 18 px, 8 px gap from label
  • *adding* 24 px horizontal (16 px if icon-only or compact)

FAB anatomy:

  • 56×56 px (default) or 96×96 (large) or 40×40 (small)
  • Single icon, no label (extended FAB adds label)
  • Radius: radius-xl (default) or radius-full (per preset)
  • Elevation: level 3 default, level 4 on hover

R1 — Hierarchy per surface

Allowed count Variant Role
At most 1 Filled OR Elevated Primary action
0..N Tonal OR Outlined Secondary actions
0..N Text Tertiary, dismissive
At most 1 FAB Surface-wide primary action (overrides Filled hierarchy on mobile)

Destructive actions: use Outlined OR Text style with error color token, never Filled red (Filledred overemphasizes; the action itself is enough warning).

R2 — Sizes

Size Height Pad-x Label role
Small 32 16 label-medium
Default 40 24 label-large
Large 56 32 title-medium

Tap target: minimum 48 px touch area enforced via outer hit-zone expansion even on Small (visible 32 px, hit zone 48 px).

R3 — States

Per interaction/states.kmd:

State Filled Tonal Outlined Text Elevated Icon FAB
Hover +8% overlay +8% overlay accent 8% bg 8% bg elevation +1 8% bg elevation +1
Focus + focus ring + focus ring thicker border + ring accent bg + focus ring + focus ring + focus ring
Press +12% overlay + ripple +12% + ripple +12% bg + ripple 12% bg + ripple elevation 0 + ripple 12% bg + ripple elevation 2 + ripple
Disabled 38% opacity 38% opacity 38% opacity 38% opacity 38% opacity 38% opacity 38% opacity

R4 — Button groups

Multiple related buttons rendered together as a unit.

┌──────┬──────┬──────┐
│ Day  │ Week │ Month│   ← single rounded container, dividers between
└──────┴──────┴──────┘

*ules*

  • Buttons share visual container (corner radius applies to OUTER container, not each button)
  • Inner dividers 1px solid border
  • All buttons same variant (don't mix filled + outlined in one group)
  • Same height across all buttons in group
  • Equal width (stretch to fill) OR content-sized (per design)

R5 — Segmented button

A button group used for single-select (exclusive choice).

  • Visual: same as button group
  • Selected state: accenttinted background (`accenttint` 12%) + accent text + checkmark icon (Material adds checkmark)
  • One must always be selected; can't all be off
  • role="radiogroup" + role="radio" on each segment (or native radio + label styling)

R6 — Icon-only buttons

  • 40×40 px square (default size), 32×32 small, 56×56 large
  • Single icon centered, 24 px (default)
  • aria-label REQUIRED (no visible label means accessible name must come from aria-label)
  • Tooltip on hover/focus showing the action name (per future tooltips.kmd)

R7 — FAB placement

Position Convention
Bottom-right Default mobile (avoids overlapping bottomnavbar)
Bottom-center Within a bottom app bar (centered FAB pattern)
Top-right (desktop) Optional on Expanded/Large class

Margin from screen edge: 16 dp (Compact), 24 dp (Medium+). Avoid overlapping content; reserve safearea space per `safearea.kmd`.

R8 — Accessibility

  • Minimum touch target 48×48 px (R2)
  • <button> element (native; not <div onclick>)
  • Visible focus ring (focus-visible, 2 px solid focus color, 2 px offset)
  • aria-label for icon-only
  • aria-pressed for toggle buttons
  • aria-disabled for disabled (still focusable for keyboard announcement)
  • Loading state: aria-busy="true" + visible spinner

R9 — Per-preset variation

Each preset (per themes/ui-style.kmd) varies button shape:

  • material3: radius-sm (8 px)
  • material2: radius-sm (4 px — sharper)
  • windows_95: radius-none (0 px) + 3D bevel borders
  • ios_cupertino: radius-md (14 px — softer)
  • brutalist: radius-none + thick border (3 px)
  • glassmorphism: radius-md + backdrop blur background

Full table: themes/shape.kmd R3.

R10 — Forbidden patterns

  • ❌ More than 1 Filled button per surface
  • ❌ Filled red for destructive (use Outlined+error)
  • ❌ Buttons inside buttons
  • ❌ Variable button heights within a button group
  • ❌ Custom hex colors (must come from accenterrorsuccess tokens)
  • ❌ Tap targets smaller than 48 px effective hit area
  • interaction/states.kmd — state overlay opacities
  • themes/color-roles.kmd — accent + accent-on bindings
  • themes/shape.kmd — radius per preset
  • themes/elevation.kmd — elevated variant + FAB elevation
  • themes/typography.kmd — label-large default role
  • foundations/elements.kmd — Control family

Source: ../home/koder/dev/koder/meta/docs/stack/specs/components/buttons.kmd