Files
openchamber/.opencode/skills/theme-system/SKILL.md
T
Bohdan Triapitsyn ddedc02687 feat: introduced a token-based theming system across the UI
* feat: added themes system

* feat: smart sidebar auto-hide for files/diff tabs + lower files sidebar threshold

* feat: added Checkbox component and update theming

- Add reusable Checkbox component for toggles across UI
- Replace several inputs with Checkbox in settings and commands panels
- Add DiffIcon and apply surface/border theming to key UI areas

* feat: Add convert-vscode-theme.cjs to convert VS Code themes to OpenChamber format

* refactor: remove unused permission logic from ChatInput

- Remove unused permission rules parsing logic from ChatInput
- Memoize renderTheme in DiffWorkerProvider to avoid unnecessary recalculations
- Remove forceOpaque helper in vscode theme adapter

* fix: guard VSCode theme loading in MarkdownRenderer

* feat: add custom user themes loading and reload

- Load user themes from ~/.config/openchamber/themes at runtime
- Expose /api/config/themes to fetch custom themes
- Allow theme reloading from Settings → Theme → Reload themes in the UI
2026-02-01 18:29:34 +02:00

206 lines
5.6 KiB
Markdown

---
name: theme-system
description: 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.
license: MIT
compatibility: opencode
---
## Overview
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.
## When to Use
- Creating or modifying UI components
- Working with colors, backgrounds, borders, or text
## Quick Decision Tree
1. **Code display?**`syntax.*`
2. **Feedback/status?**`status.*`
3. **Primary CTA?**`primary.*`
4. **Interactive/clickable?**`interactive.*`
5. **Background layer?**`surface.*`
6. **Text?**`surface.foreground` or `surface.mutedForeground`
## Critical Rules
- `surface.elevated` = inputs, cards, panels
- `interactive.hover` = **ONLY on clickable elements**
- `interactive.selection` = active/selected states (not primary!)
- Status colors = **ONLY for actual feedback** (errors, warnings, success)
- Input footers = `bg-transparent` on elevated background
### Never Use
- Hardcoded hex colors (`#FF0000`)
- Tailwind colors (`bg-white`, `text-blue-500`, `bg-gray-*`)
- Deprecated: `bg-secondary`, `bg-muted`
## Usage
### Via Hook
```tsx
import { useThemeSystem } from '@/contexts/useThemeSystem';
const { currentTheme } = useThemeSystem();
<div style={{ backgroundColor: currentTheme.colors.surface.elevated }}>
```
### Via CSS Variables
```tsx
<div className="bg-[var(--surface-elevated)] hover:bg-[var(--interactive-hover)]">
```
## Color Tokens
### Surface Colors
| 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 |
### Interactive Colors
| 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 |
### Status Colors
| 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`.
### Primary Colors
| 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).
### Syntax Colors
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 |
## Examples
### Input Area
```tsx
const { currentTheme } = useThemeSystem();
<div style={{ backgroundColor: currentTheme.colors.surface.elevated }}>
<textarea className="bg-transparent" />
<div className="bg-transparent">{/* Footer - transparent! */}</div>
</div>
```
### Active Tab
```tsx
<button className={isActive
? 'bg-interactive-selection text-interactive-selection-foreground'
: 'hover:bg-interactive-hover/50'
}>
```
### Error Message
```tsx
<div style={{
color: currentTheme.colors.status.error,
backgroundColor: currentTheme.colors.status.errorBackground
}}>
```
### Card
```tsx
<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>
```
## Wrong vs Right
### Wrong
```tsx
// 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>
```
### Right
```tsx
// 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>
```
## References
- **[Adding Themes](references/adding-themes.md)** - Built-in and custom themes
## Key Files
- Theme types: `packages/ui/src/types/theme.ts`
- Theme hook: `packages/ui/src/contexts/useThemeSystem.ts`
- CSS generator: `packages/ui/src/lib/theme/cssGenerator.ts`
- Built-in themes: `packages/ui/src/lib/theme/themes/`