1# Design System from Crush
2
3The visual language of Crush, extracted from `internal/ui/styles/styles.go` and component rendering code.
4
5## Table of Contents
6
71. [Charmtone Color Palette](#charmtone-color-palette)
82. [Semantic Color Roles](#semantic-color-roles)
93. [The Styles Struct](#the-styles-struct)
104. [Icon System](#icon-system)
115. [Typography Patterns](#typography-patterns)
126. [Layout Constants](#layout-constants)
137. [Gradient Effects](#gradient-effects)
14
15## Charmtone Color Palette
16
17**Source:** `github.com/charmbracelet/x/exp/charmtone`
18
19Crush uses Charm's proprietary color palette "Charmtone" with food-themed names. These are true-color values designed to look good on dark terminal backgrounds.
20
21| Role | Charmtone Name | Usage |
22|------|---------------|-------|
23| Primary | `Charple` | Focus borders, highlights, accent |
24| Secondary | `Dolly` | Cursor color, secondary highlights |
25| Tertiary | `Bok` | Editor prompt, tertiary accent |
26| Background | `Pepper` | Main background |
27| Background lighter | `BBQ` | Slightly lighter bg for contrast |
28| Background subtle | `Charcoal` | Borders, separators |
29| Background overlay | `Iron` | Dialog overlays |
30| Foreground | `Ash` | Primary text |
31| Foreground muted | `Squid` | Secondary text |
32| Foreground half-muted | `Smoke` | Between muted and base |
33| Foreground subtle | `Oyster` | Placeholders, hints |
34| Error | `Sriracha` | Error states |
35| Warning | `Zest` | Warning states |
36| Info | `Malibu` | Info states, blue accents |
37| White | `Butter` | High-contrast text |
38| Blue | `Malibu` | Tool names, links |
39| Blue light | `Sardine` | Light blue accents |
40| Blue dark | `Damson` | Dark blue accents |
41| Green | `Julep` | Success states |
42| Green light | `Bok` | Light green accents |
43| Green dark | `Guac` | Pending job icons |
44| Red | `Coral` | Error text |
45| Red dark | `Sriracha` | Error backgrounds |
46| Yellow | `Mustard` | Warning text, note tags |
47
48## Semantic Color Roles
49
50**Source:** `internal/ui/styles/styles.go`
51
52Colors are organized by semantic role, not by hue:
53
54```go
55var (
56 primary = charmtone.Charple // "brand" color
57 secondary = charmtone.Dolly // cursor, secondary accent
58 tertiary = charmtone.Bok // editor prompt
59
60 bgBase = charmtone.Pepper // app background
61 bgBaseLighter = charmtone.BBQ // content blocks
62 bgSubtle = charmtone.Charcoal // borders
63 bgOverlay = charmtone.Iron // dialog bg
64
65 fgBase = charmtone.Ash // text
66 fgMuted = charmtone.Squid // secondary text
67 fgHalfMuted = charmtone.Smoke // in-between
68 fgSubtle = charmtone.Oyster // hints
69
70 border = charmtone.Charcoal // unfocused borders
71 borderFocus = charmtone.Charple // focused borders
72
73 error = charmtone.Sriracha
74 warning = charmtone.Zest
75 info = charmtone.Malibu
76)
77```
78
79Every component references these semantic colors rather than hard-coding values. This makes theming possible by swapping the palette.
80
81## The Styles Struct
82
83**Source:** `internal/ui/styles/styles.go`
84
85The `Styles` struct is a massive (~500 fields) centralized style registry. It's organized in nested groups:
86
87```go
88type Styles struct {
89 // Base text
90 Base, Muted, HalfMuted, Subtle lipgloss.Style
91
92 // Tags
93 TagBase, TagError, TagInfo lipgloss.Style
94
95 // Semantic colors (as color.Color for components that need raw colors)
96 Primary, Secondary, Tertiary color.Color
97 BgBase, BgSubtle, BgOverlay color.Color
98 FgBase, FgMuted, FgSubtle color.Color
99 Error, Warning, Info color.Color
100
101 // Nested groups
102 Header struct { Charm, Diagonals, Percentage, Keystroke, WorkingDir lipgloss.Style }
103 Chat struct { Message struct { UserFocused, AssistantFocused, Thinking... } }
104 Tool struct { IconPending, IconSuccess, NameNormal, ContentLine, Body... }
105 Dialog struct { Title, View, Help... }
106 Pills struct { ... }
107
108 // Component-specific styles
109 TextInput textinput.Styles
110 TextArea textarea.Styles
111 Help help.Styles
112 Diff diffview.Style
113 FilePicker filepicker.Styles
114
115 // Markdown rendering config
116 Markdown ansi.StyleConfig
117 PlainMarkdown ansi.StyleConfig
118}
119```
120
121Styles are created once in `DefaultStyles()` and passed via `common.Common`:
122
123```go
124type Common struct {
125 App *app.App
126 Styles *styles.Styles
127}
128```
129
130Every component receives `*common.Common` at creation time.
131
132## Icon System
133
134**Source:** `internal/ui/styles/styles.go`
135
136Crush uses Unicode characters as icons, defined as constants:
137
138```go
139const (
140 CheckIcon = "✓"
141 SpinnerIcon = "⋯"
142 LoadingIcon = "⟳"
143 ModelIcon = "◇"
144 ArrowRightIcon = "→"
145
146 // Tool status
147 ToolPending = "●"
148 ToolSuccess = "✓"
149 ToolError = "×"
150
151 // Radio buttons
152 RadioOn = "◉"
153 RadioOff = "○"
154
155 // Borders
156 BorderThin = "│"
157 BorderThick = "▌"
158
159 // Separators
160 SectionSeparator = "─"
161
162 // Todos
163 TodoCompletedIcon = "✓"
164 TodoPendingIcon = "•"
165 TodoInProgressIcon = "→"
166
167 // Attachments
168 ImageIcon = "■"
169 TextIcon = "≡"
170
171 // Scrollbar
172 ScrollbarThumb = "┃"
173 ScrollbarTrack = "│"
174
175 // LSP diagnostics
176 LSPErrorIcon = "E"
177 LSPWarningIcon = "W"
178 LSPInfoIcon = "I"
179 LSPHintIcon = "H"
180)
181```
182
183Icons are styled using the corresponding `lipgloss.Style` from the Styles struct. For example:
184
185```go
186s.Tool.IconPending = lipgloss.NewStyle().Foreground(blue)
187s.Tool.IconSuccess = lipgloss.NewStyle().Foreground(green)
188s.Tool.IconError = lipgloss.NewStyle().Foreground(red)
189```
190
191## Typography Patterns
192
193### Text Hierarchy
194
195| Level | Style | Usage |
196|-------|-------|-------|
197| Primary | `Base` (fgBase/Ash) | Main content, user messages |
198| Secondary | `Muted` (fgMuted/Squid) | Metadata, timestamps, secondary info |
199| Tertiary | `HalfMuted` (fgHalfMuted/Smoke) | Less important metadata |
200| Hint | `Subtle` (fgSubtle/Oyster) | Placeholders, keystroke hints |
201
202### Message Rendering
203
204User messages and assistant messages have distinct focused/blurred styles:
205
206```go
207Chat.Message.UserFocused // highlight border on left
208Chat.Message.UserBlurred // muted border on left
209Chat.Message.AssistantFocused
210Chat.Message.AssistantBlurred
211```
212
213Tool calls get three states: focused, blurred, and compact (nested tools):
214
215```go
216Chat.Message.ToolCallFocused // full detail
217Chat.Message.ToolCallBlurred // muted
218Chat.Message.ToolCallCompact // minimal, for nested agent tools
219```
220
221### Tags
222
223Small colored labels for status indicators:
224
225```go
226TagBase // default tag
227TagError // red background
228TagInfo // blue/info background
229Tool.ErrorTag // "ERROR" label
230Tool.NoteTag // "NOTE" label (yellow bg)
231Tool.AgentTaskTag // "TASK" label (blue bg)
232```
233
234### Section Headers
235
236Used to separate logical sections within tool output:
237
238```go
239Chat.Message.SectionHeader // styled divider text
240Section.Title // section title style
241Section.Line // horizontal line style
242```
243
244## Layout Constants
245
246```go
247// Global
248const defaultMargin = 2
249const defaultListIndent = 2
250
251// Compact mode breakpoints
252const compactModeWidthBreakpoint = 120
253const compactModeHeightBreakpoint = 30
254
255// Text
256const maxTextWidth = 120 // max readable width for chat messages
257const MessageLeftPaddingTotal = 2 // border + padding
258
259// Dialogs
260const defaultDialogMaxWidth = 70
261const defaultDialogHeight = 20
262
263// Permissions dialog
264const diffMaxWidth = 180
265const simpleMaxWidth = 100
266const splitModeMinWidth = 140
267
268// Sidebar
269const sidebarWidth = 30
270
271// Editor
272const editorHeight = 5
273
274// Paste threshold
275const pasteLinesThreshold = 10 // lines before treating paste as attachment
276```
277
278## Gradient Effects
279
280**Source:** `internal/ui/styles/grad.go`
281
282Dialog titles use color gradients rendered character-by-character:
283
284```go
285type RenderContext struct {
286 TitleGradientFromColor color.Color // defaults to Primary
287 TitleGradientToColor color.Color // defaults to Secondary
288}
289```
290
291The title text is rendered with a smooth gradient from the primary color (Charple) to the secondary color (Dolly), giving dialogs their distinctive polished look.
292
293The logo also uses gradient colors:
294
295```go
296LogoTitleColorA color.Color // gradient start
297LogoTitleColorB color.Color // gradient end
298LogoCharmColor color.Color // "Charm" text
299LogoVersionColor color.Color // version number
300```
301
302This creates the signature Charm aesthetic - dark backgrounds with warm, saturated accent gradients.