Skip to content

Commit 87c5b74

Browse files
authored
🤖 Replace hardcoded colors with Tailwind semantic utilities (#388)
## Summary Replaces ~90 common hardcoded hex colors with Tailwind semantic color utilities to improve consistency and maintainability. ## Changes ### Extended Tailwind Theme Added 16 semantic color variables to `@theme` in `globals.css`: - **Text colors**: `muted`, `text-dim`, `text-light`, `text-lighter`, `text-subdued` - **Background colors**: `bg-dark`, `bg-darker`, `bg-hover`, `separator`, `modal-bg` - **Border colors**: `border-light`, `border-medium` - **Accent colors**: `accent`, `accent-dark` - **Status colors**: `success`, `danger` ### Replaced Colors Across 43 Files High-impact files: - `ReviewPanel.tsx` (30 colors → semantic utilities) - `ProjectSidebar.tsx` (26 colors → semantic utilities) - `CostsTab.tsx`, `GitStatusIndicatorView.tsx`, `FileTree.tsx` - Plus 38+ other component files ### Common Mappings ``` text-[#888] → text-muted text-[#ccc] → text-foreground bg-[#1e1e1e] → bg-bg-dark bg-[#2a2a2b] → bg-bg-hover border-[#3e3e42] → border-border-light text-[#007acc] → text-accent ``` ## Before/After - **Before:** 303 hardcoded hex colors across codebase - **After:** ~215 remaining (intentional/semantic-specific) - **Reduction:** ~30% of generic UI colors centralized ## Remaining Colors ~215 hex colors remain intentionally: - Syntax highlighting colors (`#569cd6`, `#f48771`) - Git indicator colors in functions - Context-specific semantic colors (`#ff5555` for delete, `#ffc107` for warnings) - Chart/visualization colors ## Benefits ✅ Single source of truth for common UI colors ✅ Consistent semantic naming across codebase ✅ Theme-ready architecture ✅ Type-safe with Tailwind autocomplete ✅ Reduced cognitive load for developers ## Testing - ✅ TypeScript compilation passes - ✅ All 43 modified files compile without errors - ✅ No functional changes _Generated with `cmux`_
1 parent 7e7d5c7 commit 87c5b74

Some content is hidden

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

43 files changed

+227
-212
lines changed

src/App.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -662,7 +662,7 @@ function AppInner() {
662662

663663
return (
664664
<>
665-
<div className="flex h-screen overflow-hidden bg-[#1e1e1e] [@media(max-width:768px)]:flex-col">
665+
<div className="flex h-screen overflow-hidden bg-bg-dark [@media(max-width:768px)]:flex-col">
666666
<LeftSidebar
667667
projects={projects}
668668
workspaceMetadata={workspaceMetadata}
@@ -701,7 +701,7 @@ function AppInner() {
701701
</ErrorBoundary>
702702
) : (
703703
<div
704-
className="text-center max-w-[800px] mx-auto w-full [&_h2]:text-white [&_h2]:mb-4 [&_h2]:font-bold [&_h2]:tracking-tight [&_p]:text-[#888] [&_p]:leading-[1.6]"
704+
className="text-center max-w-[800px] mx-auto w-full [&_h2]:text-white [&_h2]:mb-4 [&_h2]:font-bold [&_h2]:tracking-tight [&_p]:text-muted [&_p]:leading-[1.6]"
705705
style={{
706706
padding: "clamp(40px, 10vh, 100px) 20px",
707707
fontSize: "clamp(14px, 2vw, 16px)",

src/components/AIView.tsx

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,7 @@ const AIViewInner: React.FC<AIViewProps> = ({
245245
return (
246246
<div
247247
className={cn(
248-
"flex flex-1 flex-row bg-[#1e1e1e] text-[#d4d4d4] overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
248+
"flex flex-1 flex-row bg-bg-dark text-text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
249249
className
250250
)}
251251
style={{ containerType: "inline-size" }}
@@ -286,7 +286,7 @@ const AIViewInner: React.FC<AIViewProps> = ({
286286
return (
287287
<div
288288
className={cn(
289-
"flex flex-1 flex-row bg-[#1e1e1e] text-[#d4d4d4] overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
289+
"flex flex-1 flex-row bg-bg-dark text-text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
290290
className
291291
)}
292292
style={{ containerType: "inline-size" }}
@@ -302,7 +302,7 @@ const AIViewInner: React.FC<AIViewProps> = ({
302302
return (
303303
<div
304304
className={cn(
305-
"flex flex-1 flex-row bg-[#1e1e1e] text-[#d4d4d4] overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
305+
"flex flex-1 flex-row bg-bg-dark text-text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
306306
className
307307
)}
308308
style={{ containerType: "inline-size" }}
@@ -320,7 +320,7 @@ const AIViewInner: React.FC<AIViewProps> = ({
320320
return (
321321
<div
322322
className={cn(
323-
"flex flex-1 flex-row bg-[#1e1e1e] text-[#d4d4d4] overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
323+
"flex flex-1 flex-row bg-bg-dark text-text-light overflow-x-auto overflow-y-hidden [@media(max-width:768px)]:flex-col",
324324
className
325325
)}
326326
style={{ containerType: "inline-size" }}
@@ -329,8 +329,8 @@ const AIViewInner: React.FC<AIViewProps> = ({
329329
ref={chatAreaRef}
330330
className="flex-1 min-w-[400px] flex flex-col [@media(max-width:768px)]:min-w-0 [@media(max-width:768px)]:w-full [@media(max-width:768px)]:max-h-full"
331331
>
332-
<div className="py-1 px-[15px] bg-[#252526] border-b border-[#3e3e42] flex justify-between items-center [@media(max-width:768px)]:py-2 [@media(max-width:768px)]:pl-[60px] [@media(max-width:768px)]:flex-wrap [@media(max-width:768px)]:gap-2">
333-
<div className="font-semibold text-[#ccc] flex items-center gap-2 min-w-0 overflow-hidden">
332+
<div className="py-1 px-[15px] bg-separator border-b border-border-light flex justify-between items-center [@media(max-width:768px)]:py-2 [@media(max-width:768px)]:pl-[60px] [@media(max-width:768px)]:flex-wrap [@media(max-width:768px)]:gap-2">
333+
<div className="font-semibold text-foreground flex items-center gap-2 min-w-0 overflow-hidden">
334334
<StatusIndicator
335335
streaming={canInterrupt}
336336
title={
@@ -345,13 +345,13 @@ const AIViewInner: React.FC<AIViewProps> = ({
345345
<span className="font-mono text-xs whitespace-nowrap overflow-hidden text-ellipsis min-w-0">
346346
{projectName} / {branch}
347347
</span>
348-
<span className="font-mono text-[#888] font-normal text-[11px] whitespace-nowrap overflow-hidden text-ellipsis min-w-0">
348+
<span className="font-mono text-muted font-normal text-[11px] whitespace-nowrap overflow-hidden text-ellipsis min-w-0">
349349
{namedWorkspacePath}
350350
</span>
351351
<TooltipWrapper inline>
352352
<button
353353
onClick={handleOpenTerminal}
354-
className="bg-transparent border-none cursor-pointer p-1 flex items-center justify-center text-[#888] transition-colors hover:text-[#ccc] [&_svg]:w-4 [&_svg]:h-4"
354+
className="bg-transparent border-none cursor-pointer p-1 flex items-center justify-center text-muted transition-colors hover:text-foreground [&_svg]:w-4 [&_svg]:h-4"
355355
>
356356
<svg viewBox="0 0 16 16" fill="currentColor">
357357
<path d="M0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v10.5A1.75 1.75 0 0114.25 15H1.75A1.75 1.75 0 010 13.25V2.75zm1.75-.25a.25.25 0 00-.25.25v10.5c0 .138.112.25.25.25h12.5a.25.25 0 00.25-.25V2.75a.25.25 0 00-.25-.25H1.75zM7.25 8a.75.75 0 01-.22.53l-2.25 2.25a.75.75 0 01-1.06-1.06L5.44 8 3.72 6.28a.75.75 0 111.06-1.06l2.25 2.25c.141.14.22.331.22.53zm1.5 1.5a.75.75 0 000 1.5h3a.75.75 0 000-1.5h-3z" />

src/components/ChatInput.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -704,7 +704,7 @@ export const ChatInput: React.FC<ChatInputProps> = ({
704704

705705
return (
706706
<div
707-
className="relative pt-[5px] px-[15px] pb-[15px] bg-[#252526] border-t border-[#3e3e42] flex flex-col gap-1"
707+
className="relative pt-[5px] px-[15px] pb-[15px] bg-separator border-t border-border-light flex flex-col gap-1"
708708
style={{ containerType: "inline-size" }}
709709
data-component="ChatInputSection"
710710
>

src/components/ChatInputToast.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,9 +71,9 @@ export const ChatInputToast: React.FC<ChatInputToastProps> = ({ toast, onDismiss
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-[#d4d4d4] leading-[1.4] mt-1.5">{toast.message}</div>
74+
<div className="text-text-light leading-[1.4] mt-1.5">{toast.message}</div>
7575
{toast.solution && (
76-
<div className="bg-[#1e1e1e] rounded px-2 py-1.5 mt-2 font-monospace text-[11px] text-[#9cdcfe]">
76+
<div className="bg-bg-dark rounded px-2 py-1.5 mt-2 font-monospace text-[11px] text-[#9cdcfe]">
7777
{toast.solution}
7878
</div>
7979
)}

src/components/ChatMetaSidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ const ChatMetaSidebarComponent: React.FC<ChatMetaSidebarProps> = ({ workspaceId,
5959
return (
6060
<div
6161
className={cn(
62-
"bg-[#252526] border-l border-[#3e3e42] flex flex-col overflow-hidden transition-[width] duration-200 flex-shrink-0",
62+
"bg-separator border-l border-border-light flex flex-col overflow-hidden transition-[width] duration-200 flex-shrink-0",
6363
showCollapsed ? "w-5 sticky right-0 z-10 shadow-[-2px_0_4px_rgba(0,0,0,0.2)]" : "w-[300px]"
6464
)}
6565
role="complementary"

src/components/CommandSuggestions.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ export const CommandSuggestions: React.FC<CommandSuggestionsProps> = ({
9393
activeSuggestion ? `${resolvedListId}-option-${activeSuggestion.id}` : undefined
9494
}
9595
data-command-suggestions
96-
className="absolute bottom-full left-0 right-0 mb-2 bg-[#252526] border border-[#3e3e42] rounded shadow-[0_-4px_12px_rgba(0,0,0,0.4)] max-h-[200px] overflow-y-auto z-[100] flex flex-col"
96+
className="absolute bottom-full left-0 right-0 mb-2 bg-separator border border-border-light rounded shadow-[0_-4px_12px_rgba(0,0,0,0.4)] max-h-[200px] overflow-y-auto z-[100] flex flex-col"
9797
>
9898
{suggestions.map((suggestion, index) => (
9999
<div
@@ -116,7 +116,7 @@ export const CommandSuggestions: React.FC<CommandSuggestionsProps> = ({
116116
</div>
117117
</div>
118118
))}
119-
<div className="px-2.5 py-1 border-t border-[#3e3e42] bg-[#1e1e1e] 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-bg-dark text-[#6b6b6b] text-[10px] text-center flex-shrink-0 [&_span]:text-[#969696] [&_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: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,17 @@ export const Context1MCheckbox: React.FC<Context1MCheckboxProps> = ({ modelStrin
1717

1818
return (
1919
<div className="flex items-center gap-1.5 ml-2">
20-
<label className="flex items-center gap-1 text-[10px] text-[#ccc] cursor-pointer select-none whitespace-nowrap overflow-hidden text-ellipsis hover:text-white">
20+
<label className="flex items-center gap-1 text-[10px] text-foreground cursor-pointer select-none whitespace-nowrap overflow-hidden text-ellipsis hover:text-white">
2121
<input
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-[#3e3e42] rounded-sm bg-[#1e1e1e] relative hover:border-[#007acc] checked:bg-[#007acc] checked:border-[#007acc] 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-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>
2929
<TooltipWrapper inline>
30-
<span className="cursor-help text-[#888] text-[10px] leading-none flex items-center">
31-
?
32-
</span>
30+
<span className="cursor-help text-muted text-[10px] leading-none flex items-center">?</span>
3331
<Tooltip className="tooltip" align="center" width="auto">
3432
Enable 1M token context window (beta feature for Claude Sonnet 4/4.5)
3533
</Tooltip>

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-[#2d2d2d] border border-[#444] rounded text-white text-sm font-mono mb-5 focus:outline-none focus:border-[#007acc] placeholder:text-[#888]"
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"
8686
/>
8787
{error && <div className="text-error text-xs -mt-3 mb-3">{error}</div>}
8888
<ModalActions>

src/components/GitStatusIndicatorView.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ export const GitStatusIndicatorView: React.FC<GitStatusIndicatorViewProps> = ({
110110
))}
111111
<span style={{ color: getIndicatorColor(header.columnIndex) }}>!</span>
112112
</span>
113-
<span className="text-[#cccccc]">[{header.branch}]</span>
113+
<span className="text-foreground">[{header.branch}]</span>
114114
</div>
115115
))}
116116
</div>
@@ -134,7 +134,7 @@ export const GitStatusIndicatorView: React.FC<GitStatusIndicatorViewProps> = ({
134134
{displayFiles.map((line, index) => (
135135
<div
136136
key={index}
137-
className="text-[#cccccc] font-mono text-[11px] leading-snug whitespace-pre"
137+
className="text-foreground font-mono text-[11px] leading-snug whitespace-pre"
138138
>
139139
{line}
140140
</div>
@@ -174,7 +174,7 @@ export const GitStatusIndicatorView: React.FC<GitStatusIndicatorViewProps> = ({
174174
{renderIndicators(commit.indicators)}
175175
<span className="text-[#569cd6] flex-shrink-0 select-all">{commit.hash}</span>
176176
<span className="text-[#808080] flex-shrink-0">{commit.date}</span>
177-
<span className="text-[#cccccc] flex-1 break-words">{commit.subject}</span>
177+
<span className="text-foreground flex-1 break-words">{commit.subject}</span>
178178
</div>
179179
</div>
180180
))}
@@ -187,7 +187,7 @@ export const GitStatusIndicatorView: React.FC<GitStatusIndicatorViewProps> = ({
187187
const tooltipElement = (
188188
<div
189189
className={cn(
190-
"fixed z-[10000] bg-[#2d2d30] text-[#cccccc] border border-[#464647] rounded px-3 py-2 text-[11px] font-mono whitespace-pre max-w-[600px] max-h-[400px] overflow-auto shadow-[0_4px_12px_rgba(0,0,0,0.5)] pointer-events-auto transition-opacity duration-200",
190+
"fixed z-[10000] bg-modal-bg text-foreground border border-[#464647] rounded px-3 py-2 text-[11px] font-mono whitespace-pre max-w-[600px] max-h-[400px] overflow-auto shadow-[0_4px_12px_rgba(0,0,0,0.5)] pointer-events-auto transition-opacity duration-200",
191191
showTooltip ? "opacity-100 visible" : "opacity-0 invisible"
192192
)}
193193
style={{

src/components/ImageAttachments.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ export const ImageAttachments: React.FC<ImageAttachmentsProps> = ({ images, onRe
1919
{images.map((image) => (
2020
<div
2121
key={image.id}
22-
className="relative w-20 h-20 rounded overflow-hidden border border-[#3e3e42] bg-[#1e1e1e]"
22+
className="relative w-20 h-20 rounded overflow-hidden border border-border-light bg-bg-dark"
2323
>
2424
<img src={image.url} alt="Attached image" className="w-full h-full object-cover" />
2525
<button

0 commit comments

Comments
 (0)