Frame Design System

Buttons

PrimaryButton

#MasterUsed in: Wingman 29 · WorkPal 15

Tier 1 — gradient background, white text, strongest CTA. Use sparingly: one per surface.

Variant × state matrix (1×4 = 4 cells)
variant ↓ / state →defaulthoveractivedisabled
default

hover / active force-state mirror the .gradient-btn box-shadow lift; live :hover still owns the precise pixel definition.

SecondaryButton

#MasterUsed in: Wingman 6 · WorkPal 4

Secondary action button for prompt or workflow decisions.

View source

Requires React 19, ReactDOM 19, TypeScript and lucide-react 1.14.0. Import frame.css once and use a frame-root wrapper; set its background to var(--color-bg-page) and color to var(--color-text-primary), or supply a matching host surface; add dark to an ancestor for dark mode.

Tier 2 — solid inverted surface (black in light mode, white in dark).

Variant × state matrix (1×4 = 4 cells)
variant ↓ / state →defaulthoveractivedisabled
default

AppleSignInButton

#MasterUsed in: Wingman 0 · WorkPal 1

DS-162 — Apple's own Continue with Apple button: black in light mode, white in dark, Apple's logo, title 43% of height. Use only for Sign in with Apple; never restyle it.

TertiaryButton

#MasterUsed in: Wingman 26 · WorkPal 15

Outlined action button for quieter conversation and workflow actions.

View source

Requires React 19, ReactDOM 19, TypeScript and lucide-react 1.14.0. Import frame.css once and use a frame-root wrapper; set its background to var(--color-bg-page) and color to var(--color-text-primary), or supply a matching host surface; add dark to an ancestor for dark mode.

Tier 3 — bordered, default text color. Lightest-weight option.

Variant × state matrix (1×4 = 4 cells)
variant ↓ / state →defaulthoveractivedisabled
default

GhostPillButton

#MasterUsed in: Wingman 6 · WorkPal 13

Pill-shaped secondary action with optional icon and disabled state.

View source

Requires React 19, ReactDOM 19, TypeScript and lucide-react 1.14.0. Import frame.css once and use a frame-root wrapper; set its background to var(--color-bg-page) and color to var(--color-text-primary), or supply a matching host surface; add dark to an ancestor for dark mode.

Pill-shaped bordered secondary — used in page headers next to FilterChip rows. size='sm' gives a tighter ~32px pill (pair with a 12px glyph) for inline Edit / Reveal-in-Finder actions; default 'md' is unchanged.

HeaderIconButton

#MasterUsed in: Wingman 4 · WorkPal 11

Named icon action for conversation headers; size the mobile hit area to at least 44 pixels.

View source

Requires React 19, ReactDOM 19, TypeScript and lucide-react 1.14.0. Import frame.css once and use a frame-root wrapper; set its background to var(--color-bg-page) and color to var(--color-text-primary), or supply a matching host surface; add dark to an ancestor for dark mode.

Named icon button for page headers: 44×44 on mobile, 40×40 on desktop (sidebar toggle, panel close, modal close).

IconButton

#MasterUsed in: Wingman 12 · WorkPal 9

Square / round icon-only button, 24–44px — the mid/small inline buttons between ToolbarIconButton (composer) and HeaderIconButton (44px mobile / 40px desktop). Four tones: default (transparent→hover), filled (MiniSidebar keys), overlay (white glyph on a dark bg-scrim, for buttons on photos/video), inverted (dark glyph on a light bg-scrim-inverted). Consumer sizes the glyph ≈ box×0.5; className is the escape hatch for responsive sizing + one-off active rings.

tone="default" · sizes 24 / 28 / 32 / 40 / 44 (square)
shape="round" · active · filled · disabled · secondary glyph
tone="overlay" / "inverted" — shown on a sample media surface

ToolbarIconButton

#MasterUsed in: Wingman 0 · WorkPal 1

Composer-scale icon button (uses --toolbar-btn-h token), pairs with ToolbarPill / ToolbarSegmented in the same row.

AddRowButton

#MasterUsed in: Wingman 1 · WorkPal 2

Inline 'add a new row' affordance — sits at the end of an editable list (file list, tag list).

existing-file.md

TextButton

#MasterUsed in: Wingman 1 · WorkPal 12

Quiet text action with disabled and disclosure attributes.

View source

Requires React 19, ReactDOM 19, TypeScript and lucide-react 1.14.0. Import frame.css once and use a frame-root wrapper; set its background to var(--color-bg-page) and color to var(--color-text-primary), or supply a matching host surface; add dark to an ancestor for dark mode.

Bare text-link button — no border / fill, secondary text → primary on hover. The lightest action (Not now, clear, Replace…). tone='primary' for a stronger primary-text link.