Zetta v0.10

Components · Actions

Button

Six variants plus an icon-only button, all sharing one height, radius and type treatment — only fill, border and text color change between them.

Preview

Light
Dark
Accessibility

Variants

  • primary — Brand-filled primary action. One per local action group.
  • secondary — Muted fill for subordinate actions alongside a primary.
  • outline — Bordered secondary — when a border distinction matters in context.
  • ghost — No resting background — toolbars and dense UI.
  • danger — Destructive actions only (delete / irreversible).
  • link — Inline text action; underlined with a 4px offset.
  • icon — Icon-only toolbar button, 32×32.
  • fab — Floating action button — circular, elevated; one per screen.

Spec

The resolved Zetta spec — values and token references straight from the design system. This is the contract your implementation should match.

button-primary
backgroundColor
{colors.brand}
textColor
{colors.brand-text}
borderColor
{colors.brand}
borderWidth
1px
borderRadius
{rounded.base}
fontFamily
Geist
fontSize
14px
fontWeight
500
lineHeight
1.4
height
32px
paddingX
20px
gap
8px
iconSize
{icons.sizeMap.sm}
cursor
pointer
button-primary-hover
backgroundColor
{colors.brand-hover}
borderColor
{colors.brand-hover}
button-primary-active
backgroundColor
{colors.brand-active}
borderColor
{colors.brand-active}
button-primary-focus
borderColor
{colors.border-focus}
outline
2px solid {colors.border-focus}
outlineOffset
2px
button-primary-disabled
backgroundColor
{colors.disabled-bg}
textColor
{colors.disabled-text}
borderColor
{colors.disabled-border}
cursor
not-allowed
button-secondary
backgroundColor
{colors.surface-muted}
textColor
{colors.ink}
borderColor
transparent
borderWidth
1px
borderRadius
{rounded.base}
fontFamily
Geist
fontSize
14px
fontWeight
500
lineHeight
1.4
height
32px
paddingX
20px
gap
8px
iconSize
{icons.sizeMap.sm}
cursor
pointer
button-secondary-hover
backgroundColor
{colors.surface-pressed}
button-secondary-active
backgroundColor
{colors.surface-secondary}
button-secondary-focus
outline
2px solid {colors.border-focus}
outlineOffset
2px
button-secondary-disabled
backgroundColor
{colors.disabled-bg}
textColor
{colors.disabled-text}
cursor
not-allowed
button-outline
backgroundColor
transparent
textColor
{colors.ink}
borderColor
{colors.border-strong}
borderWidth
1px
borderRadius
{rounded.base}
fontFamily
Geist
fontSize
14px
fontWeight
500
lineHeight
1.4
height
32px
paddingX
20px
gap
8px
iconSize
{icons.sizeMap.sm}
cursor
pointer
button-outline-hover
backgroundColor
{colors.surface-muted}
button-outline-active
backgroundColor
{colors.surface-pressed}
button-outline-focus
borderColor
{colors.border-focus}
outline
2px solid {colors.border-focus}
outlineOffset
2px
button-outline-disabled
textColor
{colors.disabled-text}
borderColor
{colors.disabled-border}
cursor
not-allowed
button-ghost
backgroundColor
transparent
textColor
{colors.ink}
borderColor
transparent
borderWidth
1px
borderRadius
{rounded.base}
fontFamily
Geist
fontSize
14px
fontWeight
500
lineHeight
1.4
height
32px
paddingX
16px
gap
8px
iconSize
{icons.sizeMap.sm}
cursor
pointer
button-ghost-hover
backgroundColor
{colors.surface-muted}
button-ghost-active
backgroundColor
{colors.surface-pressed}
button-ghost-focus
outline
2px solid {colors.border-focus}
outlineOffset
2px
button-ghost-disabled
textColor
{colors.disabled-text}
cursor
not-allowed
button-danger
backgroundColor
{colors.danger-solid}
textColor
{colors.on-danger}
borderColor
{colors.danger-solid}
borderWidth
1px
borderRadius
{rounded.base}
fontFamily
Geist
fontSize
14px
fontWeight
500
lineHeight
1.4
height
32px
paddingX
20px
gap
8px
iconSize
{icons.sizeMap.sm}
cursor
pointer
button-danger-hover
backgroundColor
{colors.danger-solid-hover}
borderColor
{colors.danger-solid-hover}
button-danger-active
backgroundColor
{colors.danger-solid-active}
borderColor
{colors.danger-solid-active}
button-danger-focus
outline
2px solid {colors.danger}
outlineOffset
2px
button-danger-disabled
backgroundColor
{colors.disabled-bg}
textColor
{colors.disabled-text}
borderColor
{colors.disabled-border}
cursor
not-allowed
button-link
backgroundColor
transparent
textColor
{colors.brand}
borderColor
transparent
fontFamily
Geist
fontSize
14px
fontWeight
500
lineHeight
1.4
height
32px
paddingX
0
gap
8px
iconSize
{icons.sizeMap.sm}
textDecoration
underline
textUnderlineOffset
4px
cursor
pointer
button-link-hover
opacity
0.75
textDecoration
none
button-link-focus
outline
2px solid {colors.border-focus}
outlineOffset
2px
button-link-disabled
textColor
{colors.disabled-text}
opacity
0.5
textDecoration
none
cursor
not-allowed
pointerEvents
none
button-fab
backgroundColor
{colors.brand}
iconColor
{colors.brand-text}
borderColor
{colors.brand}
borderWidth
1px
borderRadius
{rounded.full}
shadow
{shadows.md}
cursor
pointer
sizeSm
32px
sizeMd
40px
sizeLg
48px
iconSizeSm
{icons.sizeMap.sm}
iconSizeMd
{icons.sizeMap.md}
iconSizeLg
{icons.sizeMap.lg}
button-fab-hover
backgroundColor
{colors.brand-hover}
borderColor
{colors.brand-hover}
shadow
{shadows.lg}
button-fab-active
backgroundColor
{colors.brand-active}
borderColor
{colors.brand-active}
shadow
{shadows.md}
button-fab-focus
borderColor
{colors.border-focus}
outline
2px solid {colors.border-focus}
outlineOffset
2px
button-fab-disabled
backgroundColor
{colors.disabled-bg}
iconColor
{colors.disabled-text}
borderColor
{colors.disabled-border}
shadow
none
cursor
not-allowed
button-icon
backgroundColor
transparent
iconColor
{colors.ink}
borderColor
transparent
borderWidth
1px
borderRadius
{rounded.base}
width
32px
height
32px
iconSize
{icons.sizeMap.sm}
cursor
pointer
display
flex
alignItems
center
justifyContent
center
button-icon-hover
backgroundColor
{colors.surface-muted}
button-icon-active
backgroundColor
{colors.surface-pressed}
button-icon-focus
outline
2px solid {colors.border-focus}
outlineOffset
2px
button-icon-disabled
iconColor
{colors.disabled-text}
cursor
not-allowed

Build with the skill

Zetta is a skill, not a package — there are no classes to import. Hand this prompt to your AI to generate the button in your own project's stack.

Prompt for your AI
Implement the Zetta "Button" component in this project's stack using the zetta-design-md skill.

- Apply the `button` spec from the skill (variants: primary, secondary, outline, ghost, danger, link, icon, fab).
- Use this project's own component conventions and framework idioms.
- Reference the Zetta design tokens (CSS variables) for every color, radius, and shadow — never hardcode values.
- Implement all states (hover, focus, active, disabled, and invalid where applicable) with a visible 2px focus ring.
- Honor the Zetta guardrails (brand never shifts hue, shadows for overlays only, etc.) and verify in Light, Dark, and Accessibility.

Anatomy & rules

  • All variants: 32px tall, 8px radius, Geist 14 / 500, 8px icon gap, 16px icons.
  • Focus shows a 2px ring at 2px offset (danger uses the danger color). Hover changes fill/border only.
  • One primary per local action group; reserve danger for irreversible actions.

Accessibility

  • Use a real <button> — keyboard operability and role come for free. Targets are ≥32×32px (44×44 in the Accessibility theme).
  • The icon-only variant requires an accessible name via aria-label.

Known gaps

  • Loading / submitting state (spinner-in-button) — not yet specified.
  • Button group / segmented control, split button, and speed-dial FAB — not yet specified.