Use when creating or modifying UI components, styling, or visual elements in OpenChamber. All UI colors must use theme tokens - never hardcoded values or Tailwind color classes.
OpenChamber uses a JSON-based theme system. Themes are defined in packages/ui/src/lib/theme/themes/. Users can also add custom themes via ~/.config/openchamber/themes/.
Core principle: UI colors must use theme tokens - never hardcoded hex colors or Tailwind color classes.
syntax.*status.*primary.*interactive.*surface.*surface.foreground or surface.mutedForegroundsurface.elevated = inputs, cards, panelsinteractive.hover = ONLY on clickable elementsinteractive.selection = active/selected states (not primary!)bg-transparent on elevated backgroundUse only the shared Button component from packages/ui/src/components/ui/button.tsx.
ButtonLarge, ButtonSmall).size variant exists.| Variant | Use for | Token direction |
|---|---|---|
default | Primary action in a local section/dialog | primary.* |
outline | Secondary visible action | surface.elevated + interactive.* |
secondary | Soft secondary action | interactive.hover / interactive.active |
ghost | Low-emphasis row/toolbar action | transparent + interactive.hover |
destructive | Destructive actions (Delete, Revert all) | status.error* |
link | Rare inline text action only | text-link style |
| Size | Use for |
|---|---|
xs | Dense controls in rows/lists |
sm | Default compact action buttons |
default | Standard form/page actions |
lg | Prominent large actions |
icon | Icon-only square button |
defaultoutlineghostdestructivesize="xs"#FF0000)bg-white, text-blue-500, bg-gray-*)bg-secondary, bg-mutedimport { useThemeSystem } from '@/contexts/useThemeSystem';
const { currentTheme } = useThemeSystem();
<div style={{ backgroundColor: currentTheme.colors.surface.elevated }}>
<div className="bg-[var(--surface-elevated)] hover:bg-[var(--interactive-hover)]">
| Token | Usage |
|---|---|
surface.background | Main app background |
surface.elevated | Inputs, cards, panels, popovers |
surface.muted | Secondary backgrounds, sidebars |
surface.foreground | Primary text |
surface.mutedForeground | Secondary text, hints |
surface.subtle | Subtle dividers |
| Token | Usage |
|---|---|
interactive.border | Default borders |
interactive.hover | Hover on clickable elements only |
interactive.selection | Active/selected items |
interactive.selectionForeground | Text on selection |
interactive.focusRing | Focus indicators |
| Token | Usage |
|---|---|
status.error | Errors, validation failures |
status.warning | Warnings, cautions |
status.success | Success messages |
status.info | Informational messages |
Each has variants: *, *Foreground, *Background, *Border.
| Token | Usage |
|---|---|
primary.base | Primary CTA buttons |
primary.hover | Hover on primary elements |
primary.foreground | Text on primary background |
Primary vs Selection: Primary = "click me" (CTA), Selection = "currently active" (state).
For code display only. Never use for UI elements.
| Token | Usage |
|---|---|
syntax.base.background | Code block background |
syntax.base.foreground | Default code text |
syntax.base.keyword | Keywords |
syntax.base.string | Strings |
syntax.highlights.diffAdded | Added lines |
syntax.highlights.diffRemoved | Removed lines |
const { currentTheme } = useThemeSystem();
<div style={{ backgroundColor: currentTheme.colors.surface.elevated }}>
<textarea className="bg-transparent" />
<div className="bg-transparent">{/* Footer - transparent! */}</div>
</div>
<button className={isActive
? 'bg-interactive-selection text-interactive-selection-foreground'
: 'hover:bg-interactive-hover/50'
}>
<div style={{
color: currentTheme.colors.status.error,
backgroundColor: currentTheme.colors.status.errorBackground
}}>
<div style={{ backgroundColor: currentTheme.colors.surface.elevated }}>
<h3 style={{ color: currentTheme.colors.surface.foreground }}>Title</h3>
<p style={{ color: currentTheme.colors.surface.mutedForeground }}>Description</p>
</div>
// Hardcoded colors
<div style={{ backgroundColor: '#F2F0E5' }}>
<button className="bg-blue-500">
// Primary for active tab
<Tab className="bg-primary">Active</Tab>
// Hover on static element
<div className="hover:bg-interactive-hover">Static card</div>
// Colored footer on input
<div style={{ backgroundColor: currentTheme.colors.surface.elevated }}>
<textarea />
<div style={{ backgroundColor: currentTheme.colors.surface.muted }}>Footer</div>
</div>
// Theme tokens
<div style={{ backgroundColor: currentTheme.colors.surface.elevated }}>
<button style={{ backgroundColor: currentTheme.colors.primary.base }}>
// Selection for active tab
<Tab style={{ backgroundColor: currentTheme.colors.interactive.selection }}>Active</Tab>
// Hover only on clickable
<button className="hover:bg-[var(--interactive-hover)]">Click</button>
// Transparent footer
<div style={{ backgroundColor: currentTheme.colors.surface.elevated }}>
<textarea className="bg-transparent" />
<div className="bg-transparent">Footer</div>
</div>
packages/ui/src/types/theme.tspackages/ui/src/contexts/useThemeSystem.tspackages/ui/src/lib/theme/cssGenerator.tspackages/ui/src/lib/theme/themes/