Tokens
Tokens are the contract between Caret and the visual layer. Components never write hex codes or magic numbers — they read tokens through the active theme. Re-skin the entire system by overriding a single object.
Categories
| Token | Lives at | Override entry |
|---|---|---|
| colors | registry/tokens/colors.ts | theme.colors |
| motion | registry/tokens/motion.ts | theme.motion |
| symbols | registry/tokens/symbols.ts | theme.symbols |
| spacing | registry/tokens/spacing.ts | theme.spacing |
| typography | registry/tokens/typography.ts | theme.typography |
Colors
Caret separates brand color (truecolor, fixed — your CLI's recognizable accent) from semantic colors (ANSI-named, so they harmonize with the user's terminal theme).
| Key | Default | Purpose |
|---|---|---|
| accent.default | #5882f7 | Brand accent — used for prompts, anchors, primary CTAs |
| accent.muted | #3a5fb8 | Lower-emphasis accent for hover or disabled states |
| semantic.success.ansi | green | Success messages, completed steps |
| semantic.warning.ansi | yellow | Deprecation, soft warnings |
| semantic.danger.ansi | red | Errors, failures, destructive actions |
| semantic.info.ansi | blue | Informational messages |
| fg | terminal default | Foreground — Caret never overrides |
| dim | ANSI dim | Muted text — descriptions, labels |
green, red) for semantic colors so they pick up the user's theme. Only accent is a fixed truecolor value — that's the part that makes a Caret CLI recognizable across any terminal.Motion
Every animation is bounded and gated by reduced-motion detection. Tokens come in two flavors: durations (how long a transition runs) and frame rates (how often a stepped animation ticks).
| Key | Default (ms) | Used by |
|---|---|---|
| duration.instant | 60 | Tight feedback (cursor blink window) |
| duration.fast | 120 | Color/border transitions |
| duration.default | 200 | Spinner morph, prompt resolve |
| duration.slow | 300 | Reveals, modal enters |
| spinnerFrameMs | 80 | Braille spinner step interval |
| blinkMs | 1050 | Block cursor blink cycle |
Symbols
Symbols are part of the brand. The manifesto says: never customize them. They are listed here for reference, not because they're meant to be replaced.
| Key | Glyph | Where used |
|---|---|---|
| anchor | ^ | Brand mark — banner heads, prompt frames |
| state.success | ✓ | Successful steps, success() messages |
| state.failure | ✗ | Failed steps, error() messages |
| state.warning | ⚠ | warning() messages, alert(kind: warning) |
| state.info | ℹ | info() messages, alert(kind: info) |
| state.cancelled | — | User-cancelled prompts |
| marker.selected | ● | Selected radio / multi-select item |
| marker.unselected | ○ | Unselected radio / multi-select item |
| progress.arrow | ▸ | List arrow variant, focus indicator |
| structure.gutter | │ | Quote, error, alert left gutter |
Overriding tokens
Two ways to apply a custom theme:
import { caret } from './caret'
// 1. Globally — affects every Caret call from this point on
caret.theme.set({
colors: {
accent: { default: '#FF6B35' },
},
symbols: {
anchor: '◆',
},
})
// 2. Per call — one-off override, no global state
spinner('Deploying', deploy, {
theme: { colors: { accent: { default: '#10B981' } } },
})Theme overrides are merged shallowly per top-level key. You only specify the leaves you want to change — Caret fills the rest from the default theme.
Continue with the Principles the tokens reflect, or jump to the component catalog to see the tokens applied in real previews.