Theme
Themes are how you re-skin Caret without rewriting the components. A theme is a deeply-nested object whose leaves are token values — colors, motion durations, symbols, spacing, typography. Three application surfaces: globally, per React subtree, or per call.
The default theme
defaultTheme lands in your project after npx caret add theme — the file at caret/theme/default.ts exports it. You almost never read it directly: call useTheme() inside an Ink component or rely on Caret's components doing the lookup for you.
import { defaultTheme } from '../caret/theme/default.js'
console.log(defaultTheme.colors.accent.default) // '#5882f7'
console.log(defaultTheme.motion.duration.default) // 200Globally re-skin with setTheme
Call once at startup before any Caret component renders. Subsequent calls take effect for the next render but don't roll back what's already painted.
import { caret } from './caret'
caret.theme.set({
colors: {
accent: { default: '#FF6B35' },
},
symbols: {
anchor: '◆',
},
})Overrides are merged shallowly per top-level key. You only specify leaves you want to change — Caret fills the rest from the default.
Subtree overrides with ThemeProvider
For React-tree scoped overrides — useful when one section of a CLI needs a different palette without affecting the rest.
import { ThemeProvider } from '../caret/theme/context.js'
function DangerZone() {
return (
<ThemeProvider theme={{ colors: { accent: { default: '#e5482d' } } }}>
<DestructiveActions />
</ThemeProvider>
)
}Reading the active theme with useTheme
Inside an Ink component, useTheme() returns the merged theme that's active at this point in the tree.
import { useTheme, Box, Text } from 'ink'
function Heading({ text }: { text: string }) {
const theme = useTheme()
return (
<Box>
<Text color={theme.colors.accent.default}>{theme.symbols.anchor} </Text>
<Text bold>{text}</Text>
</Box>
)
}Per-call overrides
Every Caret component accepts an optional theme option. It's merged with the active theme just for that single call — no global side-effects.
spinner('Deploying', deploy, {
theme: {
colors: { accent: { default: '#10B981' } },
},
})Precedence
From lowest to highest priority — later wins:
| Source | Priority |
|---|---|
| defaultTheme | Lowest — applied if nothing else overrides |
| caret.theme.set(...) | Global override, replaces defaultTheme leaves |
| <ThemeProvider theme={...}> | React subtree override |
| component({ theme: ... }) | Highest — wins for that single call |
Token reference is at Tokens; capability detection that gates which tokens are actually rendered is at Capability detection.