Skip to content

Commit c43e2c4

Browse files
committed
feat(ui): add professional light/dark theme system
- Implement theme toggle with localStorage persistence - Add monochromatic black/white/gray color scheme - Update all components for theme consistency - Enhance tooltips and accessibility
1 parent 86bef8d commit c43e2c4

14 files changed

Lines changed: 288 additions & 64 deletions

frontend/src/components/StackConverter.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,12 @@ const StackConverter: React.FC = () => {
113113

114114
return (
115115
<>
116-
<Tooltip id="upload-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !bg-gray-900 !text-white !px-3 !py-2" />
117-
<Tooltip id="reset-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !bg-gray-900 !text-white !px-3 !py-2" />
118-
<Tooltip id="edit-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !bg-gray-900 !text-white !px-3 !py-2" />
119-
<Tooltip id="copy-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !bg-gray-900 !text-white !px-3 !py-2" />
120-
<Tooltip id="download-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !bg-gray-900 !text-white !px-3 !py-2" />
121-
<div className="min-h-screen bg-gradient-to-br from-gray-900 via-blue-900 to-purple-900 p-4">
116+
<Tooltip id="upload-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !px-3 !py-2 tooltip-light dark:tooltip-dark" />
117+
<Tooltip id="reset-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !px-3 !py-2 tooltip-light dark:tooltip-dark" />
118+
<Tooltip id="edit-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !px-3 !py-2 tooltip-light dark:tooltip-dark" />
119+
<Tooltip id="copy-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !px-3 !py-2 tooltip-light dark:tooltip-dark" />
120+
<Tooltip id="download-tooltip" place="top" className="!z-50 !text-sm !rounded-lg !px-3 !py-2 tooltip-light dark:tooltip-dark" />
121+
<div className="min-h-screen bg-gradient-light dark:bg-gradient-dark p-4">
122122
<div className="max-w-7xl mx-auto">
123123
<Header />
124124

frontend/src/components/constants.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,7 @@ export class CounterComponent {
193193
const Counter = (props) => {
194194
const [count, setCount] = createSignal(props.initialValue || 0);
195195
const increment = () => setCount(count() + 1);
196-
const decrement = () => setCount(count() - 1);
196+
const decrement = () => setCount(count() + 1);
197197
return (
198198
<div class="counter">
199199
<h2>Counter: {count()}</h2>
@@ -230,18 +230,18 @@ export const features = [
230230
icon: '🧠',
231231
title: 'Smart Conversion',
232232
description: 'AI-powered analysis converts components, state management, and lifecycle methods intelligently.',
233-
color: 'text-blue-400'
233+
color: 'text-gray-600 dark:text-gray-400'
234234
},
235235
{
236236
icon: '⚡',
237237
title: 'Full Stack Support',
238238
description: 'Convert entire projects including build configs, routing, state management, and testing.',
239-
color: 'text-green-400'
239+
color: 'text-gray-600 dark:text-gray-400'
240240
},
241241
{
242242
icon: '🎯',
243243
title: 'Best Practices',
244244
description: 'Generated code follows framework conventions and modern best practices out of the box.',
245-
color: 'text-purple-400'
245+
color: 'text-gray-600 dark:text-gray-400'
246246
}
247247
];

frontend/src/components/ui/CodePanel.tsx

Lines changed: 25 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -147,18 +147,18 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
147147
};
148148

149149
return (
150-
<div className="bg-gray-800/30 backdrop-blur-sm rounded-2xl overflow-hidden border border-gray-700 flex flex-col h-full">
151-
<div className="bg-gray-700/50 px-6 py-4 border-b border-gray-600 flex items-center justify-between">
152-
<h3 className="text-white font-semibold text-lg flex items-center gap-2">
150+
<div className="bg-card border border-border rounded-2xl overflow-hidden shadow-sm flex flex-col h-full">
151+
<div className="bg-muted/50 px-6 py-4 border-b border-border flex items-center justify-between">
152+
<h3 className="text-card-foreground font-semibold text-lg flex items-center gap-2">
153153
{title}
154154
{!isConverting && (
155155
<>
156-
<span className="text-gray-300">{selectedStack?.icon}</span>
156+
<span className="text-muted-foreground">{selectedStack?.icon}</span>
157157
<span>{selectedStack?.label}</span>
158158
</>
159159
)}
160160
{isEditable && isEditing && (
161-
<span className="text-xs bg-blue-600 text-white px-2 py-1 rounded-full">
161+
<span className="text-xs bg-slate-600 dark:bg-slate-400 text-white dark:text-slate-900 px-2 py-1 rounded-full">
162162
Editing
163163
</span>
164164
)}
@@ -170,8 +170,8 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
170170
onClick={toggleEditMode}
171171
className={`transition-colors ${
172172
effectiveDisableEdit
173-
? 'text-gray-600 cursor-not-allowed'
174-
: 'text-gray-400 hover:text-white'
173+
? 'text-muted-foreground/50 cursor-not-allowed'
174+
: 'text-muted-foreground hover:text-card-foreground'
175175
}`}
176176
disabled={!!effectiveDisableEdit}
177177
data-tooltip-id="edit-tooltip"
@@ -194,8 +194,8 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
194194
onClick={onFileUpload}
195195
className={`transition-colors ${
196196
isUploading || uploadDisabled
197-
? 'text-gray-600 cursor-not-allowed'
198-
: 'text-gray-400 hover:text-white'
197+
? 'text-muted-foreground/50 cursor-not-allowed'
198+
: 'text-muted-foreground hover:text-card-foreground'
199199
}`}
200200
disabled={isUploading || uploadDisabled}
201201
data-tooltip-id="upload-tooltip"
@@ -211,8 +211,8 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
211211
onClick={onReset}
212212
className={`transition-colors ${
213213
disableEdit
214-
? 'text-gray-600 cursor-not-allowed'
215-
: 'text-gray-400 hover:text-white'
214+
? 'text-muted-foreground/50 cursor-not-allowed'
215+
: 'text-muted-foreground hover:text-card-foreground'
216216
}`}
217217
disabled={disableEdit}
218218
data-tooltip-id="reset-tooltip"
@@ -225,7 +225,7 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
225225
{code && !isConverting && onCopy && (
226226
<button
227227
onClick={handleCopyToClipboard}
228-
className="text-gray-400 hover:text-white transition-colors"
228+
className="text-muted-foreground hover:text-card-foreground transition-colors"
229229
data-tooltip-id="copy-tooltip"
230230
data-tooltip-content="Copy to clipboard"
231231
>
@@ -235,7 +235,7 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
235235
{code && !isConverting && onDownload && (
236236
<button
237237
onClick={handleDownload}
238-
className="text-gray-400 hover:text-white transition-colors"
238+
className="text-muted-foreground hover:text-card-foreground transition-colors"
239239
data-tooltip-id="download-tooltip"
240240
data-tooltip-content="Download converted code"
241241
>
@@ -247,18 +247,18 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
247247

248248
<div className="relative flex-1 h-full min-h-[20rem]">
249249
{isConverting ? (
250-
<div className="p-6 bg-gray-800/30 backdrop-blur-sm rounded-2xl">
250+
<div className="p-6 bg-muted/30">
251251
<Skeleton
252252
count={16}
253253
height={18}
254254
style={{ marginBottom: 6, borderRadius: 3 }}
255-
baseColor="rgba(30,41,59,0.6)"
256-
highlightColor="rgba(59,130,246,0.15)"
255+
baseColor="hsl(var(--muted))"
256+
highlightColor="hsl(var(--accent))"
257257
/>
258258
</div>
259259
) : isUploading || isDetectingStack ? (
260-
<div className="px-6 py-8 flex flex-col items-center justify-center text-blue-300 text-sm min-h-[20rem]">
261-
<Loader2 className="h-10 w-10 mb-2 text-blue-400 animate-spin" />
260+
<div className="px-6 py-8 flex flex-col items-center justify-center text-primary text-sm min-h-[20rem]">
261+
<Loader2 className="h-10 w-10 mb-2 text-primary animate-spin" />
262262
<div>{isUploading ? 'Uploading file...' : 'Detecting stack...'}</div>
263263
</div>
264264
) : uploadedFile && (
@@ -270,7 +270,7 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
270270
ref={textareaRef}
271271
value={safeCode}
272272
onChange={handleCodeChange}
273-
className="w-full h-full min-h-[20rem] bg-gray-900/50 text-gray-100 font-mono text-sm leading-relaxed p-4 rounded-lg border border-gray-600 focus:border-blue-500 focus:outline-none resize-none"
273+
className="w-full h-full min-h-[20rem] bg-muted/50 text-card-foreground font-mono text-sm leading-relaxed p-4 rounded-lg border border-border focus:border-ring focus:outline-none resize-none"
274274
style={{ fontSize: 14 }}
275275
placeholder={`// Enter your ${stack} code here...\n// You can edit this code directly and then convert it to another framework`}
276276
spellCheck={false}
@@ -288,21 +288,21 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
288288
)
289289
) : (
290290
// Otherwise, show batch upload UI (zip)
291-
<div className="px-6 py-8 flex flex-col items-center justify-center text-blue-300 text-sm min-h-[20rem]">
292-
<Upload className="h-10 w-10 mb-2 text-blue-400" />
291+
<div className="px-6 py-8 flex flex-col items-center justify-center text-primary text-sm min-h-[20rem]">
292+
<Upload className="h-10 w-10 mb-2 text-primary" />
293293
<div className="mb-2">
294294
<strong>Uploaded file:</strong> {uploadedFile.name} ({(uploadedFile.size / 1024).toFixed(1)} KB)
295295
</div>
296296
<div className="mb-4">Ready for batch conversion.</div>
297297
{uploadMessage && (
298-
<div className={uploadMessage.includes('success') ? 'text-green-400 mb-2' : 'text-red-400 mb-2'}>
298+
<div className={uploadMessage.includes('success') ? 'text-emerald-600 dark:text-emerald-400 mb-2' : 'text-red-600 dark:text-red-400 mb-2'}>
299299
{uploadMessage}
300300
</div>
301301
)}
302302
{onRemoveFile && (
303303
<button
304304
onClick={onRemoveFile}
305-
className="bg-gray-700 hover:bg-gray-600 text-white px-4 py-2 rounded transition-colors"
305+
className="bg-secondary hover:bg-secondary/80 text-secondary-foreground px-4 py-2 rounded transition-colors"
306306
disabled={isUploading}
307307
>
308308
Remove File
@@ -319,7 +319,7 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
319319
ref={textareaRef}
320320
value={safeCode}
321321
onChange={handleCodeChange}
322-
className="w-full h-full min-h-[20rem] bg-gray-900/50 text-gray-100 font-mono text-sm leading-relaxed p-4 rounded-lg border border-gray-600 focus:border-blue-500 focus:outline-none resize-none"
322+
className="w-full h-full min-h-[20rem] bg-muted/50 text-card-foreground font-mono text-sm leading-relaxed p-4 rounded-lg border border-border focus:border-ring focus:outline-none resize-none"
323323
style={{ fontSize: 12 }}
324324
placeholder={`// Enter your ${stack} code here...\n// You can edit this code directly and then convert it to another framework`}
325325
spellCheck={false}
@@ -336,7 +336,7 @@ const CodePanel: React.FC<CodePanelPropsWithTooltips> = ({
336336
</SyntaxHighlighter>
337337
)}
338338
{safeShowEmptyState && !code && (
339-
<div className="absolute inset-0 h-full w-full flex items-center justify-center text-gray-500 pointer-events-none">
339+
<div className="absolute inset-0 h-full w-full flex items-center justify-center text-muted-foreground pointer-events-none">
340340
<div className="text-center">
341341
{emptyStateIcon}
342342
<p>{emptyStateMessage}</p>

frontend/src/components/ui/ConvertButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ const ConvertButton: React.FC<ConvertButtonProps> = ({
1212
<button
1313
onClick={onClick}
1414
disabled={disabled}
15-
className="bg-gradient-to-r from-blue-500 to-purple-600 hover:from-blue-600 hover:to-purple-700 disabled:from-gray-500 disabled:to-gray-600 text-white font-bold py-4 px-8 rounded-2xl text-lg transition-all duration-200 transform hover:scale-105 disabled:scale-100 shadow-lg flex items-center space-x-2"
15+
className="bg-gradient-to-r from-gray-800 to-black dark:from-gray-200 dark:to-white hover:from-gray-900 hover:to-gray-800 dark:hover:from-gray-100 dark:hover:to-gray-200 disabled:from-gray-400 disabled:to-gray-500 text-white dark:text-black font-bold py-4 px-8 rounded-2xl text-lg transition-all duration-200 transform hover:scale-105 disabled:scale-100 shadow-lg flex items-center space-x-2 disabled:cursor-not-allowed"
1616
>
1717
{isConverting ? (
1818
<>

frontend/src/components/ui/ErrorBanner.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,12 @@ const ErrorBanner: React.FC<ErrorBannerProps> = ({ error, onDismiss }) => {
66
if (!error) return null;
77

88
return (
9-
<div className="max-w-2xl mx-auto mb-4 flex items-center bg-red-600/90 text-white px-4 py-3 rounded-lg shadow-lg">
9+
<div className="max-w-2xl mx-auto mb-4 flex items-center bg-red-500/90 dark:bg-red-600/90 text-white px-4 py-3 rounded-lg shadow-lg border border-red-600 dark:border-red-700">
1010
<XCircle className="mr-2 h-5 w-5" />
1111
<span className="flex-1">{error}</span>
1212
<button
1313
onClick={onDismiss}
14-
className="ml-4 text-white hover:text-gray-200 focus:outline-none"
14+
className="ml-4 text-white hover:text-red-100 focus:outline-none focus:ring-2 focus:ring-red-300 rounded"
1515
>
1616
1717
</button>

frontend/src/components/ui/FeaturesSection.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ const FeaturesSection: React.FC<FeaturesSectionProps> = ({ features }) => {
55
return (
66
<div className="mt-12 grid grid-cols-1 md:grid-cols-3 gap-6">
77
{features.map((feature, index) => (
8-
<div key={index} className="bg-white/5 backdrop-blur-sm rounded-xl p-6 border border-gray-700">
8+
<div key={index} className="bg-card border border-border rounded-xl p-6 shadow-sm">
99
<div className={`text-2xl mb-4 ${feature.color}`}>{feature.icon}</div>
10-
<h3 className="text-white font-semibold mb-2">{feature.title}</h3>
11-
<p className="text-gray-300 text-sm">{feature.description}</p>
10+
<h3 className="text-card-foreground font-semibold mb-2">{feature.title}</h3>
11+
<p className="text-muted-foreground text-sm">{feature.description}</p>
1212
</div>
1313
))}
1414
</div>

frontend/src/components/ui/Header.tsx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,21 @@
11
import React from 'react';
22
import { Zap } from 'lucide-react';
3+
import ThemeToggle from './ThemeToggle';
34

45
const Header: React.FC = () => {
56
return (
67
<div className="text-center mb-8">
7-
<div className="flex items-center justify-center mb-4">
8-
<div className="bg-white/10 backdrop-blur-sm rounded-2xl p-3 mr-4">
9-
<Zap className="h-8 w-8 text-yellow-400" />
8+
<div className="flex items-center justify-between mb-4">
9+
<div className="flex items-center justify-center flex-1">
10+
<div className="bg-gradient-to-br from-amber-50 to-yellow-50 dark:from-amber-900/20 dark:to-yellow-900/20 rounded-2xl p-3 mr-4 border border-amber-200 dark:border-amber-800/30">
11+
<Zap className="h-8 w-8 text-yellow-400 dark:text-yellow-300" />
12+
</div>
13+
<h1 className="text-4xl font-bold text-foreground">StackConverter</h1>
1014
</div>
11-
<h1 className="text-4xl font-bold text-white">StackConverter</h1>
15+
<ThemeToggle />
1216
</div>
13-
<p className="text-gray-300 text-md">Multi-Framework AI Codebase Converter</p>
14-
<div className="mt-2 px-4 py-2 bg-blue-500/20 rounded-full text-blue-300 text-sm inline-block">
17+
<p className="text-muted-foreground text-md">Multi-Framework AI Codebase Converter</p>
18+
<div className="mt-2 px-4 py-2 bg-gradient-to-r from-gray-100 to-gray-200 dark:from-gray-800 dark:to-gray-700 rounded-full text-gray-700 dark:text-gray-300 text-sm inline-block border border-gray-300 dark:border-gray-600">
1519
🚀 Transform your entire codebase between different tech stacks
1620
</div>
1721
</div>

frontend/src/components/ui/StackSelection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ const StackSelection: React.FC<StackSelectionProps> = ({
4040
)}
4141
</div>
4242

43-
<div className="bg-white/10 backdrop-blur-sm rounded-full p-3">
44-
<ArrowRight className="h-6 w-6 text-white" />
43+
<div className="bg-gradient-to-r from-gray-100 to-gray-200 dark:from-gray-800 dark:to-gray-700 backdrop-blur-sm rounded-full p-3 border border-gray-300 dark:border-gray-600">
44+
<ArrowRight className="h-6 w-6 text-gray-600 dark:text-gray-400" />
4545
</div>
4646

4747
<StackSelector

frontend/src/components/ui/StackSelector.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,18 @@ const StackSelector: React.FC<StackSelectorProps> = ({
1616
value={value}
1717
onChange={onChange}
1818
disabled={disabled}
19-
className="appearance-none bg-gray-800/50 backdrop-blur-sm border border-gray-600 rounded-xl px-6 py-3 text-white text-lg font-medium focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent pr-12 pl-12"
19+
className="appearance-none bg-card border border-border rounded-xl px-6 py-3 text-card-foreground text-lg font-medium focus:outline-none focus:ring-2 focus:ring-ring focus:border-transparent pr-12 pl-12 disabled:opacity-50 disabled:cursor-not-allowed"
2020
>
2121
{stackOptions.map(option => (
2222
<option key={option.value} value={option.value}>
2323
{option.label}
2424
</option>
2525
))}
2626
</select>
27-
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-300">
27+
<div className="absolute left-3 top-1/2 transform -translate-y-1/2 text-muted-foreground">
2828
{selectedStack?.icon}
2929
</div>
30-
<FileCode className="absolute right-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-gray-400 pointer-events-none" />
30+
<FileCode className="absolute right-3 top-1/2 transform -translate-y-1/2 h-5 w-5 text-muted-foreground pointer-events-none" />
3131
</div>
3232
);
3333
};
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import React from 'react';
2+
import { Sun, Moon } from 'lucide-react';
3+
import { useTheme } from '../../contexts/ThemeContext';
4+
5+
const ThemeToggle: React.FC = () => {
6+
const { theme, toggleTheme } = useTheme();
7+
8+
return (
9+
<button
10+
onClick={toggleTheme}
11+
className="inline-flex items-center justify-center rounded-lg text-sm font-medium transition-all duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:opacity-50 disabled:pointer-events-none ring-offset-background h-10 w-10 hover:bg-accent hover:text-accent-foreground border border-border bg-card/50 backdrop-blur-sm"
12+
aria-label="Toggle theme"
13+
>
14+
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all duration-200 dark:-rotate-90 dark:scale-0" />
15+
<Moon className="absolute h-[1.2rem] w-[1.2rem] rotate-90 scale-0 transition-all duration-200 dark:rotate-0 dark:scale-100" />
16+
<span className="sr-only">Toggle theme</span>
17+
</button>
18+
);
19+
};
20+
21+
export default ThemeToggle;

0 commit comments

Comments
 (0)