---
description: Button system — Primary, Secondary, Text, Pill, and Icon Button kinds. Per-surface color keys.
variables:
  # =========================================================================
  # Primary button (always on — the mandatory base)
  # =========================================================================
  - key: button.primary.radius
    type: number
    label: Primary Radius (px)
    value: "8"
    section: visual
  - key: button.primary.padding
    type: number
    label: Primary Padding (px)
    value: "16"
    section: visual
  - key: button.primary.icon
    type: select
    label: Primary Icon
    value: "no"
    options: [yes, no]
    section: visual
  - key: button.primary.icon.position
    type: select
    label: Primary Icon Position
    value: right
    options: [left, right]
    section: visual
  - key: button.primary.icon.name
    type: text
    label: Primary Icon Name
    value: ChevronRight
    section: visual
  - key: button.primary.border.width
    type: number
    label: Primary Border (px)
    value: "0"
    section: visual
  # Primary — Fill (per surface)
  - key: button.primary.fill.light
    type: color
    label: Primary Fill (Light surface)
    value: ""
    section: visual
  - key: button.primary.fill.light.opacity
    type: number
    label: Primary Fill Opacity (Light surface)
    value: "100"
    section: visual
  - key: button.primary.fill.dark
    type: color
    label: Primary Fill (Dark surface)
    value: ""
    section: visual
  - key: button.primary.fill.dark.opacity
    type: number
    label: Primary Fill Opacity (Dark surface)
    value: "100"
    section: visual
  # Primary — Border color (per surface)
  - key: button.primary.border.color.light
    type: color
    label: Primary Border Color (Light surface)
    value: ""
    section: visual
  - key: button.primary.border.color.light.opacity
    type: number
    label: Primary Border Opacity (Light surface)
    value: "100"
    section: visual
  - key: button.primary.border.color.dark
    type: color
    label: Primary Border Color (Dark surface)
    value: ""
    section: visual
  - key: button.primary.border.color.dark.opacity
    type: number
    label: Primary Border Opacity (Dark surface)
    value: "100"
    section: visual
  # Primary — Text color (per surface)
  - key: button.primary.text.color.light
    type: color
    label: Primary Text Color (Light surface)
    value: ""
    section: visual
  - key: button.primary.text.color.light.opacity
    type: number
    label: Primary Text Opacity (Light surface)
    value: "100"
    section: visual
  - key: button.primary.text.color.dark
    type: color
    label: Primary Text Color (Dark surface)
    value: ""
    section: visual
  - key: button.primary.text.color.dark.opacity
    type: number
    label: Primary Text Opacity (Dark surface)
    value: "100"
    section: visual

  # =========================================================================
  # Secondary button
  # =========================================================================
  - key: button.secondary.enabled
    type: select
    label: Secondary Enabled
    value: "yes"
    options: [yes, no]
    section: visual
  - key: button.secondary.radius
    type: number
    label: Secondary Radius (px)
    value: "8"
    section: visual
  - key: button.secondary.padding
    type: number
    label: Secondary Padding (px)
    value: "16"
    section: visual
  - key: button.secondary.icon
    type: select
    label: Secondary Icon
    value: "no"
    options: [yes, no]
    section: visual
  - key: button.secondary.icon.position
    type: select
    label: Secondary Icon Position
    value: right
    options: [left, right]
    section: visual
  - key: button.secondary.icon.name
    type: text
    label: Secondary Icon Name
    value: ChevronRight
    section: visual
  - key: button.secondary.border.width
    type: number
    label: Secondary Border (px)
    value: "1"
    section: visual
  # Secondary — Fill (per surface)
  - key: button.secondary.fill.light
    type: color
    label: Secondary Fill (Light surface)
    value: ""
    section: visual
  - key: button.secondary.fill.light.opacity
    type: number
    label: Secondary Fill Opacity (Light surface)
    value: "100"
    section: visual
  - key: button.secondary.fill.dark
    type: color
    label: Secondary Fill (Dark surface)
    value: ""
    section: visual
  - key: button.secondary.fill.dark.opacity
    type: number
    label: Secondary Fill Opacity (Dark surface)
    value: "100"
    section: visual
  # Secondary — Border color (per surface)
  - key: button.secondary.border.color.light
    type: color
    label: Secondary Border Color (Light surface)
    value: ""
    section: visual
  - key: button.secondary.border.color.light.opacity
    type: number
    label: Secondary Border Opacity (Light surface)
    value: "100"
    section: visual
  - key: button.secondary.border.color.dark
    type: color
    label: Secondary Border Color (Dark surface)
    value: ""
    section: visual
  - key: button.secondary.border.color.dark.opacity
    type: number
    label: Secondary Border Opacity (Dark surface)
    value: "100"
    section: visual
  # Secondary — Text color (per surface)
  - key: button.secondary.text.color.light
    type: color
    label: Secondary Text Color (Light surface)
    value: ""
    section: visual
  - key: button.secondary.text.color.light.opacity
    type: number
    label: Secondary Text Opacity (Light surface)
    value: "100"
    section: visual
  - key: button.secondary.text.color.dark
    type: color
    label: Secondary Text Color (Dark surface)
    value: ""
    section: visual
  - key: button.secondary.text.color.dark.opacity
    type: number
    label: Secondary Text Opacity (Dark surface)
    value: "100"
    section: visual

  # =========================================================================
  # Text button — no fill, no border, just text + optional icon
  # =========================================================================
  - key: button.text.enabled
    type: select
    label: Text Button Enabled
    value: "yes"
    options: [yes, no]
    section: visual
  - key: button.text.icon
    type: select
    label: Text Icon
    value: "no"
    options: [yes, no]
    section: visual
  - key: button.text.icon.position
    type: select
    label: Text Icon Position
    value: right
    options: [left, right]
    section: visual
  - key: button.text.icon.name
    type: text
    label: Text Icon Name
    value: ChevronRight
    section: visual
  - key: button.text.padding
    type: number
    label: Text Padding (px)
    value: "8"
    section: visual
  # Text — Text color (per surface)
  - key: button.text.text.color.light
    type: color
    label: Text Button Color (Light surface)
    value: ""
    section: visual
  - key: button.text.text.color.light.opacity
    type: number
    label: Text Button Opacity (Light surface)
    value: "100"
    section: visual
  - key: button.text.text.color.dark
    type: color
    label: Text Button Color (Dark surface)
    value: ""
    section: visual
  - key: button.text.text.color.dark.opacity
    type: number
    label: Text Button Opacity (Dark surface)
    value: "100"
    section: visual

  # =========================================================================
  # Pills — toggleable, two visual states: active / inactive
  # =========================================================================
  - key: pill.enabled
    type: select
    label: Pills Enabled
    value: "yes"
    options: [yes, no]
    section: visual
  - key: pill.radius
    type: number
    label: Pill Radius (px)
    value: "999"
    section: visual
  - key: pill.padding
    type: number
    label: Pill Padding (px)
    value: "10"
    section: visual
  - key: pill.icon
    type: select
    label: Pill Icon
    value: "no"
    options: [yes, no]
    section: visual
  - key: pill.icon.position
    type: select
    label: Pill Icon Position
    value: left
    options: [left, right]
    section: visual
  - key: pill.icon.name
    type: text
    label: Pill Icon Name
    value: ChevronRight
    section: visual
  - key: pill.border.width
    type: number
    label: Pill Border (px)
    value: "1"
    section: visual
  # Pill Active — Fill / Border / Text (per surface)
  - key: pill.active.fill.light
    type: color
    label: Pill Active Fill (Light surface)
    value: ""
    section: visual
  - key: pill.active.fill.light.opacity
    type: number
    label: Pill Active Fill Opacity (Light surface)
    value: "100"
    section: visual
  - key: pill.active.fill.dark
    type: color
    label: Pill Active Fill (Dark surface)
    value: ""
    section: visual
  - key: pill.active.fill.dark.opacity
    type: number
    label: Pill Active Fill Opacity (Dark surface)
    value: "100"
    section: visual
  - key: pill.active.border.color.light
    type: color
    label: Pill Active Border (Light surface)
    value: ""
    section: visual
  - key: pill.active.border.color.light.opacity
    type: number
    label: Pill Active Border Opacity (Light surface)
    value: "100"
    section: visual
  - key: pill.active.border.color.dark
    type: color
    label: Pill Active Border (Dark surface)
    value: ""
    section: visual
  - key: pill.active.border.color.dark.opacity
    type: number
    label: Pill Active Border Opacity (Dark surface)
    value: "100"
    section: visual
  - key: pill.active.text.color.light
    type: color
    label: Pill Active Text (Light surface)
    value: ""
    section: visual
  - key: pill.active.text.color.light.opacity
    type: number
    label: Pill Active Text Opacity (Light surface)
    value: "100"
    section: visual
  - key: pill.active.text.color.dark
    type: color
    label: Pill Active Text (Dark surface)
    value: ""
    section: visual
  - key: pill.active.text.color.dark.opacity
    type: number
    label: Pill Active Text Opacity (Dark surface)
    value: "100"
    section: visual
  # Pill Inactive — Fill / Border / Text (per surface)
  - key: pill.inactive.fill.light
    type: color
    label: Pill Inactive Fill (Light surface)
    value: ""
    section: visual
  - key: pill.inactive.fill.light.opacity
    type: number
    label: Pill Inactive Fill Opacity (Light surface)
    value: "100"
    section: visual
  - key: pill.inactive.fill.dark
    type: color
    label: Pill Inactive Fill (Dark surface)
    value: ""
    section: visual
  - key: pill.inactive.fill.dark.opacity
    type: number
    label: Pill Inactive Fill Opacity (Dark surface)
    value: "100"
    section: visual
  - key: pill.inactive.border.color.light
    type: color
    label: Pill Inactive Border (Light surface)
    value: ""
    section: visual
  - key: pill.inactive.border.color.light.opacity
    type: number
    label: Pill Inactive Border Opacity (Light surface)
    value: "100"
    section: visual
  - key: pill.inactive.border.color.dark
    type: color
    label: Pill Inactive Border (Dark surface)
    value: ""
    section: visual
  - key: pill.inactive.border.color.dark.opacity
    type: number
    label: Pill Inactive Border Opacity (Dark surface)
    value: "100"
    section: visual
  - key: pill.inactive.text.color.light
    type: color
    label: Pill Inactive Text (Light surface)
    value: ""
    section: visual
  - key: pill.inactive.text.color.light.opacity
    type: number
    label: Pill Inactive Text Opacity (Light surface)
    value: "100"
    section: visual
  - key: pill.inactive.text.color.dark
    type: color
    label: Pill Inactive Text (Dark surface)
    value: ""
    section: visual
  - key: pill.inactive.text.color.dark.opacity
    type: number
    label: Pill Inactive Text Opacity (Dark surface)
    value: "100"
    section: visual

  # =========================================================================
  # Icon Button — square button containing only an icon. Renders three
  # specimens side-by-side at icon.size.sm / .md / .lg from the Icons section.
  # =========================================================================
  - key: iconbutton.enabled
    type: select
    label: Icon Button Enabled
    value: "yes"
    options: [yes, no]
    section: visual
  - key: iconbutton.radius
    type: number
    label: Icon Button Radius (px)
    value: "8"
    section: visual
  - key: iconbutton.padding
    type: number
    label: Icon Button Padding (px)
    value: "8"
    section: visual
  - key: iconbutton.icon.name
    type: text
    label: Icon Button Icon Name
    value: Settings
    section: visual
  - key: iconbutton.border.width
    type: number
    label: Icon Button Border (px)
    value: "0"
    section: visual
  # Icon Button — Fill (per surface)
  - key: iconbutton.fill.light
    type: color
    label: Icon Button Fill (Light surface)
    value: ""
    section: visual
  - key: iconbutton.fill.light.opacity
    type: number
    label: Icon Button Fill Opacity (Light surface)
    value: "100"
    section: visual
  - key: iconbutton.fill.dark
    type: color
    label: Icon Button Fill (Dark surface)
    value: ""
    section: visual
  - key: iconbutton.fill.dark.opacity
    type: number
    label: Icon Button Fill Opacity (Dark surface)
    value: "100"
    section: visual
  # Icon Button — Border color (per surface)
  - key: iconbutton.border.color.light
    type: color
    label: Icon Button Border Color (Light surface)
    value: ""
    section: visual
  - key: iconbutton.border.color.light.opacity
    type: number
    label: Icon Button Border Opacity (Light surface)
    value: "100"
    section: visual
  - key: iconbutton.border.color.dark
    type: color
    label: Icon Button Border Color (Dark surface)
    value: ""
    section: visual
  - key: iconbutton.border.color.dark.opacity
    type: number
    label: Icon Button Border Opacity (Dark surface)
    value: "100"
    section: visual
  # Icon Button — Icon color (per surface)
  - key: iconbutton.icon.color.light
    type: color
    label: Icon Button Icon Color (Light surface)
    value: ""
    section: visual
  - key: iconbutton.icon.color.light.opacity
    type: number
    label: Icon Button Icon Color Opacity (Light surface)
    value: "100"
    section: visual
  - key: iconbutton.icon.color.dark
    type: color
    label: Icon Button Icon Color (Dark surface)
    value: ""
    section: visual
  - key: iconbutton.icon.color.dark.opacity
    type: number
    label: Icon Button Icon Color Opacity (Dark surface)
    value: "100"
    section: visual
---

# Buttons

Three button styles (Primary, Secondary, Text), Pills, and Icon Buttons. Primary is always on. Secondary, Text, Pills, and Icon Buttons can be toggled per brand.

## Per-surface key shape

Every color property (`fill`, `border.color`, `text.color`, `icon.color`) is keyed per-surface:

```
<kind>.<colorprop>.<surface>
<kind>.<colorprop>.<surface>.opacity
```

`<surface>` is either:
- `light` or `dark` (the two universal surfaces, always present), or
- the slugified label of a brand color from `colors.md` (e.g. `Accent Color` → `accent-color`)

This file seeds the universal `light` / `dark` surfaces only. The scraper roles fill in additional brand-color surfaces when ingesting a brand. The brand modal preview (`packages/app/src/components/brands/preview/buttons-preview.tsx`) derives the full surface set dynamically — it will only render specimens for surfaces that have populated values.

There is no base-key fallback. `button.primary.fill` (without a surface suffix) is never read.

See `packages/server/src/server/brand/CANON.md` for the full canon.
