Skip to content

Commit 099aa26

Browse files
authored
🤖 Centralize Tailwind color usage with CSS variables (#391)
## Problem The codebase had 157 instances of hardcoded hex colors scattered throughout using confusing Tailwind syntax like `text-[#808080]`, `bg-[#1e1e1e]`, and `border-[rgba(244,67,54,0.1)]`. This makes the color scheme difficult to maintain and update consistently. ## Solution Centralized all colors as semantic CSS variables in `src/styles/globals.css` using Tailwind v4's `@theme` directive. These automatically generate corresponding Tailwind utility classes. ### Color categories added (40+ new variables): - **Text variants**: `text-muted-light`, `text-placeholder`, `text-bright`, `text-label`, etc. - **Border variants**: `border-medium`, `border-darker`, `border-light`, `border-subtle`, etc. - **Background variants**: `bg-dark`, `bg-darker`, `bg-modal-bg`, `bg-code-bg`, etc. - **Accent variants**: `bg-accent-hover`, `bg-accent-dark`, `text-accent-light`, etc. - **Status colors**: `text-danger-light`, `text-danger-soft`, `text-warning`, `text-success-light` - **Toast backgrounds**: `bg-toast-success-bg`, `bg-toast-error-bg`, with proper opacity - **Semi-transparent overlays**: `bg-danger-overlay`, `bg-warning-overlay`, `bg-white-overlay` - **Code highlighting**: `text-code-type`, `text-code-keyword`, `bg-code-keyword-overlay` - **Review/diff backgrounds**: `bg-review-bg-blue`, `bg-review-bg-info`, `bg-review-bg-warning` ### Examples of replacements: ```diff - className="text-[#808080]" + className="text-text-muted-light" - className="bg-[#0e639c] hover:bg-[#1177bb]" + className="bg-accent-dark hover:bg-accent-hover" - className="bg-[rgba(244,67,54,0.1)] border-[#f44336]" + className="bg-danger-overlay border-danger" - className="bg-[var(--color-code-bg)]" + className="bg-code-bg" ``` ## Impact - ✅ All 157 hardcoded color instances replaced - ✅ Consistent naming convention across the codebase - ✅ Easier to update color scheme globally - ✅ Type checking passes - ✅ Better readability: `text-text-muted-light` is clearer than `text-[#808080]` ## Testing - `make typecheck` passes - `make fmt` applied - All existing colors mapped to semantic equivalents with matching HSL values _Generated with `cmux`_
1 parent 3106a56 commit 099aa26

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+289
-214
lines changed

src/components/AIView.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,12 +245,12 @@ const AIViewInner: React.FC<AIViewProps> = ({
245245
return (
246246
<div
247247
className={cn(
248-
"flex flex-1 flex-row bg-bg-dark text-text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
248+
"flex flex-1 flex-row bg-dark text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
249249
className
250250
)}
251251
style={{ containerType: "inline-size" }}
252252
>
253-
<div className="flex-1 flex flex-col items-center justify-center h-full text-[#6b6b6b] text-center">
253+
<div className="flex-1 flex flex-col items-center justify-center h-full text-placeholder text-center">
254254
<h3 className="m-0 mb-2.5 text-base font-medium">Loading workspace...</h3>
255255
</div>
256256
</div>
@@ -286,12 +286,12 @@ const AIViewInner: React.FC<AIViewProps> = ({
286286
return (
287287
<div
288288
className={cn(
289-
"flex flex-1 flex-row bg-bg-dark text-text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
289+
"flex flex-1 flex-row bg-dark text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
290290
className
291291
)}
292292
style={{ containerType: "inline-size" }}
293293
>
294-
<div className="flex-1 flex flex-col items-center justify-center h-full text-[#6b6b6b] text-center">
294+
<div className="flex-1 flex flex-col items-center justify-center h-full text-placeholder text-center">
295295
<h3 className="m-0 mb-2.5 text-base font-medium">Loading workspace...</h3>
296296
</div>
297297
</div>
@@ -302,12 +302,12 @@ const AIViewInner: React.FC<AIViewProps> = ({
302302
return (
303303
<div
304304
className={cn(
305-
"flex flex-1 flex-row bg-bg-dark text-text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
305+
"flex flex-1 flex-row bg-dark text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
306306
className
307307
)}
308308
style={{ containerType: "inline-size" }}
309309
>
310-
<div className="flex-1 flex flex-col items-center justify-center h-full text-[#6b6b6b] text-center">
310+
<div className="flex-1 flex flex-col items-center justify-center h-full text-placeholder text-center">
311311
<h3 className="m-0 mb-2.5 text-base font-medium">No Workspace Selected</h3>
312312
<p className="m-0 text-[13px]">
313313
Select a workspace from the sidebar to view and interact with Claude
@@ -320,7 +320,7 @@ const AIViewInner: React.FC<AIViewProps> = ({
320320
return (
321321
<div
322322
className={cn(
323-
"flex flex-1 flex-row bg-bg-dark text-text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
323+
"flex flex-1 flex-row bg-dark text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
324324
className
325325
)}
326326
style={{ containerType: "inline-size" }}
@@ -378,7 +378,7 @@ const AIViewInner: React.FC<AIViewProps> = ({
378378
className="h-full overflow-y-auto p-[15px] whitespace-pre-wrap break-words leading-[1.5]"
379379
>
380380
{mergedMessages.length === 0 ? (
381-
<div className="flex-1 flex flex-col items-center justify-center h-full text-[#6b6b6b] text-center [&_h3]:m-0 [&_h3]:mb-2.5 [&_h3]:text-base [&_h3]:font-medium [&_p]:m-0 [&_p]:text-[13px]">
381+
<div className="flex-1 flex flex-col items-center justify-center h-full text-placeholder text-center [&_h3]:m-0 [&_h3]:mb-2.5 [&_h3]:text-base [&_h3]:font-medium [&_p]:m-0 [&_p]:text-[13px]">
382382
<h3>No Messages Yet</h3>
383383
<p>Send a message below to begin</p>
384384
</div>

src/components/ChatInputToast.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ import React, { useEffect, useCallback } from "react";
33
import { cn } from "@/lib/utils";
44

55
const toastTypeStyles: Record<"success" | "error", string> = {
6-
success: "bg-[#0e639c20] border border-[#0e639c] text-[#3794ff]",
7-
error: "bg-[#f1483620] border border-[#f14836] text-[#f14836]",
6+
success: "bg-toast-success-bg border border-accent-dark text-toast-success-text",
7+
error: "bg-toast-error-bg border border-toast-error-border text-toast-error-text",
88
};
99

1010
export interface Toast {
@@ -22,7 +22,7 @@ interface ChatInputToastProps {
2222
}
2323

2424
export const SolutionLabel: React.FC<{ children: ReactNode }> = ({ children }) => (
25-
<div className="text-[#808080] text-[10px] mb-1 uppercase">{children}</div>
25+
<div className="text-muted-light text-[10px] mb-1 uppercase">{children}</div>
2626
);
2727

2828
export const ChatInputToast: React.FC<ChatInputToastProps> = ({ toast, onDismiss }) => {
@@ -65,15 +65,15 @@ export const ChatInputToast: React.FC<ChatInputToastProps> = ({ toast, onDismiss
6565
<div
6666
role="alert"
6767
aria-live="assertive"
68-
className="bg-[#2d1f1f] border border-[#5a2c2c] rounded px-3 py-2.5 text-xs text-[#f48771] animate-[toastSlideIn_0.2s_ease-out] shadow-[0_4px_12px_rgba(0,0,0,0.3)]"
68+
className="bg-toast-fatal-bg border border-toast-fatal-border rounded px-3 py-2.5 text-xs text-danger-soft animate-[toastSlideIn_0.2s_ease-out] shadow-[0_4px_12px_rgba(0,0,0,0.3)]"
6969
>
7070
<div className="flex items-start gap-1.5">
7171
<span className="text-sm leading-none"></span>
7272
<div className="flex-1">
7373
{toast.title && <div className="font-semibold mb-1.5">{toast.title}</div>}
74-
<div className="text-text-light leading-[1.4] mt-1.5">{toast.message}</div>
74+
<div className="text-light leading-[1.4] mt-1.5">{toast.message}</div>
7575
{toast.solution && (
76-
<div className="bg-bg-dark rounded px-2 py-1.5 mt-2 font-monospace text-[11px] text-[#9cdcfe]">
76+
<div className="bg-dark rounded px-2 py-1.5 mt-2 font-monospace text-[11px] text-code-type">
7777
{toast.solution}
7878
</div>
7979
)}

src/components/CommandPalette.stories.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -132,7 +132,7 @@ const PaletteDemo: React.FC<{ autoOpen?: boolean }> = ({ autoOpen = true }) => {
132132
<>
133133
<button
134134
onClick={() => open()}
135-
className="py-2 px-4 bg-[#0e639c] text-white border-none rounded cursor-pointer font-primary text-[13px] self-start hover:bg-[#1177bb] active:bg-[#0d5689]"
135+
className="py-2 px-4 bg-accent-dark text-white border-none rounded cursor-pointer font-primary text-[13px] self-start hover:bg-accent-hover active:bg-accent-dark"
136136
>
137137
Open Command Palette (⌘⇧P)
138138
</button>
@@ -174,8 +174,8 @@ type Story = StoryObj<typeof meta>;
174174

175175
export const Default: Story = {
176176
render: () => (
177-
<div className="min-h-[600px] bg-[#1e1e1e] p-5 flex flex-col gap-5">
178-
<div className="p-4 bg-[#252526] border border-[#3e3e42] rounded text-[#cccccc] font-primary text-[13px] leading-[1.6] [&_kbd]:py-0.5 [&_kbd]:px-1.5 [&_kbd]:bg-[#1e1e1e] [&_kbd]:border [&_kbd]:border-[#3e3e42] [&_kbd]:rounded-[3px] [&_kbd]:font-monospace [&_kbd]:text-[11px]">
177+
<div className="min-h-[600px] bg-dark p-5 flex flex-col gap-5">
178+
<div className="p-4 bg-separator border border-border-light rounded text-bright font-primary text-[13px] leading-[1.6] [&_kbd]:py-0.5 [&_kbd]:px-1.5 [&_kbd]:bg-dark [&_kbd]:border [&_kbd]:border-border-light [&_kbd]:rounded-[3px] [&_kbd]:font-monospace [&_kbd]:text-[11px]">
179179
<strong>Command Palette</strong>
180180
<br />
181181
<br />

src/components/CommandPalette.tsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -364,12 +364,12 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({ getSlashContext
364364
}}
365365
>
366366
<Command
367-
className="w-[min(720px,92vw)] bg-[#1f1f1f] border border-[#333] rounded-lg shadow-[0_10px_40px_rgba(0,0,0,0.4)] text-[#e5e5e5] font-primary overflow-hidden"
367+
className="w-[min(720px,92vw)] bg-separator border border-border rounded-lg shadow-[0_10px_40px_rgba(0,0,0,0.4)] text-lighter font-primary overflow-hidden"
368368
onMouseDown={(e: React.MouseEvent) => e.stopPropagation()}
369369
shouldFilter={shouldUseCmdkFilter}
370370
>
371371
<Command.Input
372-
className="w-full py-3 px-3.5 bg-[#161616] text-[#e5e5e5] border-none outline-none text-sm border-b border-[#2a2a2a]"
372+
className="w-full py-3 px-3.5 bg-darker text-lighter border-none outline-none text-sm border-b border-hover"
373373
value={query}
374374
onValueChange={handleQueryChange}
375375
placeholder={
@@ -405,12 +405,12 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({ getSlashContext
405405
<Command.Group
406406
key={group.name}
407407
heading={group.name}
408-
className="[&[cmdk-group]]:py-2 [&[cmdk-group]]:px-1.5 [&[cmdk-group-heading]]:py-1 [&[cmdk-group-heading]]:px-2.5 [&[cmdk-group-heading]]:text-[#9a9a9a] [&[cmdk-group-heading]]:text-[11px] [&[cmdk-group-heading]]:uppercase [&[cmdk-group-heading]]:tracking-[0.08em]"
408+
className="[&[cmdk-group]]:py-2 [&[cmdk-group]]:px-1.5 [&[cmdk-group-heading]]:py-1 [&[cmdk-group-heading]]:px-2.5 [&[cmdk-group-heading]]:text-subdued [&[cmdk-group-heading]]:text-[11px] [&[cmdk-group-heading]]:uppercase [&[cmdk-group-heading]]:tracking-[0.08em]"
409409
>
410410
{group.items.map((item) => (
411411
<Command.Item
412412
key={item.id}
413-
className="grid grid-cols-[1fr_auto] items-center gap-2 py-2 px-3 text-[13px] cursor-pointer rounded-md my-0.5 mx-1 hover:bg-[#2a2a2a] aria-selected:bg-[#2f2f2f]"
413+
className="grid grid-cols-[1fr_auto] items-center gap-2 py-2 px-3 text-[13px] cursor-pointer rounded-md my-0.5 mx-1 hover:bg-hover aria-selected:bg-hover"
414414
onSelect={() => {
415415
if ("prompt" in item && item.prompt) {
416416
addRecent(item.id);
@@ -435,12 +435,12 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({ getSlashContext
435435
{"subtitle" in item && item.subtitle && (
436436
<>
437437
<br />
438-
<span className="text-[#9a9a9a] text-xs">{item.subtitle}</span>
438+
<span className="text-subdued text-xs">{item.subtitle}</span>
439439
</>
440440
)}
441441
</div>
442442
{"shortcutHint" in item && item.shortcutHint && (
443-
<span className="text-[#9a9a9a] text-[11px] font-monospace">
443+
<span className="text-subdued text-[11px] font-monospace">
444444
{item.shortcutHint}
445445
</span>
446446
)}
@@ -449,7 +449,7 @@ export const CommandPalette: React.FC<CommandPaletteProps> = ({ getSlashContext
449449
</Command.Group>
450450
))}
451451
{!hasAnyItems && (
452-
<div className="p-4 text-[#7a7a7a] text-[13px]">{emptyText ?? "No results"}</div>
452+
<div className="p-4 text-gray text-[13px]">{emptyText ?? "No results"}</div>
453453
)}
454454
</Command.List>
455455
</Command>

src/components/CommandSuggestions.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,19 +104,19 @@ export const CommandSuggestions: React.FC<CommandSuggestionsProps> = ({
104104
role="option"
105105
aria-selected={index === selectedIndex}
106106
className={cn(
107-
"px-2.5 py-1.5 cursor-pointer transition-colors duration-150 flex items-center justify-between gap-3 hover:bg-[#094771]",
108-
index === selectedIndex ? "bg-[#094771]" : "bg-transparent"
107+
"px-2.5 py-1.5 cursor-pointer transition-colors duration-150 flex items-center justify-between gap-3 hover:bg-accent-darker",
108+
index === selectedIndex ? "bg-accent-darker" : "bg-transparent"
109109
)}
110110
>
111-
<div className="text-[#569cd6] font-monospace text-xs flex-shrink-0">
111+
<div className="text-accent font-monospace text-xs flex-shrink-0">
112112
{suggestion.display}
113113
</div>
114-
<div className="text-[#969696] text-[11px] text-right overflow-hidden text-ellipsis whitespace-nowrap">
114+
<div className="text-medium text-[11px] text-right overflow-hidden text-ellipsis whitespace-nowrap">
115115
{suggestion.description}
116116
</div>
117117
</div>
118118
))}
119-
<div className="px-2.5 py-1 border-t border-border-light bg-bg-dark text-[#6b6b6b] text-[10px] text-center flex-shrink-0 [&_span]:text-[#969696] [&_span]:font-medium">
119+
<div className="px-2.5 py-1 border-t border-border-light bg-dark text-placeholder text-[10px] text-center flex-shrink-0 [&_span]:text-medium [&_span]:font-medium">
120120
<span>Tab</span> to complete • <span>↑↓</span> to navigate • <span>Esc</span> to dismiss
121121
</div>
122122
</div>

src/components/Context1MCheckbox.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export const Context1MCheckbox: React.FC<Context1MCheckboxProps> = ({ modelStrin
2222
type="checkbox"
2323
checked={use1M}
2424
onChange={(e) => setUse1M(e.target.checked)}
25-
className="cursor-pointer w-[11px] h-[11px] m-0 appearance-none border border-border-light rounded-sm bg-bg-dark relative hover:border-accent checked:bg-accent checked:border-accent checked:after:content-[''] checked:after:absolute checked:after:left-[3px] checked:after:top-0 checked:after:w-[3px] checked:after:h-[6px] checked:after:border-solid checked:after:border-white checked:after:border-r-[1.5px] checked:after:border-b-[1.5px] checked:after:rotate-45"
25+
className="cursor-pointer w-[11px] h-[11px] m-0 appearance-none border border-border-light rounded-sm bg-dark relative hover:border-accent checked:bg-accent checked:border-accent checked:after:content-[''] checked:after:absolute checked:after:left-[3px] checked:after:top-0 checked:after:w-[3px] checked:after:h-[6px] checked:after:border-solid checked:after:border-white checked:after:border-r-[1.5px] checked:after:border-b-[1.5px] checked:after:rotate-45"
2626
/>
2727
1M Context
2828
</label>

src/components/DirectorySelectModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export const DirectorySelectModal: React.FC = () => {
8282
onKeyDown={handleKeyDown}
8383
placeholder="/home/user/projects/my-project"
8484
autoFocus
85-
className="w-full px-3 py-2 bg-modal-bg border border-[#444] rounded text-white text-sm font-mono mb-5 focus:outline-none focus:border-accent placeholder:text-muted"
85+
className="w-full px-3 py-2 bg-modal-bg border border-border-medium rounded text-white text-sm font-mono mb-5 focus:outline-none focus:border-accent placeholder:text-muted"
8686
/>
8787
{error && <div className="text-error text-xs -mt-3 mb-3">{error}</div>}
8888
<ModalActions>

src/components/ErrorBoundary.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export class ErrorBoundary extends Component<Props, State> {
4242
}
4343

4444
return (
45-
<div className="p-5 bg-[#3c1f1f] border border-[#f48771] rounded text-[#f48771] m-5">
45+
<div className="p-5 bg-error-bg-dark border border-danger-soft rounded text-danger-soft m-5">
4646
<h3 className="m-0 mb-2.5 text-base">
4747
Something went wrong{this.props.workspaceInfo && ` in ${this.props.workspaceInfo}`}
4848
</h3>
@@ -59,7 +59,7 @@ export class ErrorBoundary extends Component<Props, State> {
5959
)}
6060
<button
6161
onClick={this.handleReset}
62-
className="py-2 px-4 bg-[#f48771] text-white border-none rounded-sm cursor-pointer text-sm hover:bg-[#ff9980]"
62+
className="py-2 px-4 bg-danger-soft text-white border-none rounded-sm cursor-pointer text-sm hover:bg-info-light"
6363
>
6464
Reset
6565
</button>

0 commit comments

Comments
 (0)