Skip to content

Commit bb794df

Browse files
authored
css fixes (#43)
* Updated design system with new color tokens for improved visual consistency across the interface * Refined component spacing and layout throughout the application * Enhanced notification center, toast notifications, and admin panel styling * Improved color palette with new surface overlays, interactive states, and theme-aware tokens * Optimized scrollbar and border styling for better visual hierarchy
1 parent 3bf94ed commit bb794df

18 files changed

+324
-300
lines changed

frontend/src/App.svelte

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,16 @@
9090
{:else}
9191
<div class="flex flex-col min-h-screen bg-bg-default dark:bg-dark-bg-default pt-16">
9292
<Header/>
93-
<div class="flex-grow flex flex-col">
94-
<ToastContainer/>
95-
<main class="flex-grow">
96-
{#if !authInitialized}
97-
<div class="flex items-center justify-center min-h-[50vh]">
98-
<Spinner size="large" />
99-
</div>
100-
{:else}
101-
<Router base="/" {routes} />
102-
{/if}
103-
</main>
104-
</div>
93+
<ToastContainer/>
94+
<main class="flex-grow">
95+
{#if !authInitialized}
96+
<div class="flex items-center justify-center min-h-[50vh]">
97+
<Spinner size="large" />
98+
</div>
99+
{:else}
100+
<Router base="/" {routes} />
101+
{/if}
102+
</main>
105103
<Footer/>
106104
</div>
107105
{/if}

frontend/src/app.css

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,22 @@
5252

5353
--color-code-bg: #0f172a;
5454

55+
/* Surface tokens for layered UI */
56+
--color-surface-overlay: #ffffff;
57+
--color-dark-surface-overlay: #1f2937;
58+
59+
/* Interactive states */
60+
--color-interactive-hover: #f9fafb;
61+
--color-dark-interactive-hover: #374151;
62+
63+
/* Skeleton/loading states */
64+
--color-skeleton: #e5e7eb;
65+
--color-dark-skeleton: #374151;
66+
67+
/* Tooltip */
68+
--color-tooltip-bg: #111827;
69+
--color-tooltip-fg: #ffffff;
70+
5571
/* Font Families */
5672
--font-sans: 'Inter', ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, 'Noto Sans', sans-serif;
5773
--font-mono: 'Fira Code', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
@@ -413,13 +429,6 @@
413429
Editor Component Styles
414430
========================================================================== */
415431

416-
/* Bare input (no border/background) for inline editing */
417-
.form-input-bare {
418-
@apply bg-transparent border-0 focus:ring-0 w-full text-sm font-medium
419-
text-fg-default dark:text-dark-fg-default
420-
placeholder-fg-muted dark:placeholder-dark-fg-muted;
421-
}
422-
423432
/* Output container for code execution results */
424433
.output-container {
425434
@apply bg-bg-alt dark:bg-dark-bg-alt border border-border-default
@@ -440,7 +449,13 @@
440449
.output-pre,
441450
.custom-scrollbar {
442451
scrollbar-width: thin;
443-
scrollbar-color: #9ca3af #e5e7eb; /* neutral-400 neutral-200 */
452+
scrollbar-color: var(--color-fg-subtle) var(--color-skeleton);
453+
}
454+
455+
:is(.dark .output-content),
456+
:is(.dark .output-pre),
457+
:is(.dark .custom-scrollbar) {
458+
scrollbar-color: var(--color-dark-fg-subtle) var(--color-dark-skeleton);
444459
}
445460

446461
.output-content::-webkit-scrollbar,
@@ -501,7 +516,7 @@
501516

502517
/* Small input variant for admin tables */
503518
.input-sm {
504-
@apply px-2 py-1 text-sm border border-gray-300 dark:border-gray-600
505-
rounded bg-white dark:bg-gray-700 text-fg-default dark:text-dark-fg-default;
519+
@apply px-2 py-1 text-sm border border-border-input dark:border-dark-border-input
520+
rounded bg-surface-overlay dark:bg-dark-surface-overlay text-fg-default dark:text-dark-fg-default;
506521
}
507522
}

frontend/src/components/ErrorDisplay.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
</button>
6060
<button
6161
onclick={goHome}
62-
class="flex-1 btn py-2.5 text-sm font-medium bg-bg-default dark:bg-dark-bg-default border border-border-default dark:border-dark-border-default text-fg-default dark:text-dark-fg-default hover:bg-gray-100 dark:hover:bg-gray-800 transition-colors"
62+
class="flex-1 btn py-2.5 text-sm font-medium bg-bg-default dark:bg-dark-bg-default border border-border-default dark:border-dark-border-default text-fg-default dark:text-dark-fg-default hover:bg-interactive-hover dark:hover:bg-dark-interactive-hover transition-colors"
6363
>
6464
Go to Home
6565
</button>

frontend/src/components/Footer.svelte

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44
const githubIcon = `<svg class="h-5 w-5" fill="currentColor" viewBox="0 0 24 24"><path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clip-rule="evenodd"/></svg>`;
55
</script>
66

7-
<footer class="relative z-30 bg-bg-alt dark:bg-dark-bg-alt border-t border-border-default dark:border-dark-border-default">
8-
<div class="app-container py-12">
7+
<footer class="relative z-30 mt-8 pt-8 pb-6 bg-bg-alt dark:bg-dark-bg-alt border-t border-border-default dark:border-dark-border-default">
8+
<div class="app-container py-6">
99

1010
<div class="grid grid-cols-1 md:grid-cols-12 gap-8 mb-8">
1111

frontend/src/components/NotificationCenter.svelte

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131
}
3232
3333
const priorityColors = {
34-
low: 'text-gray-600 dark:text-gray-400',
34+
low: 'text-fg-muted dark:text-dark-fg-muted',
3535
medium: 'text-blue-600 dark:text-blue-400',
3636
high: 'text-orange-600 dark:text-orange-400',
3737
urgent: 'text-red-600 dark:text-red-400'
@@ -212,7 +212,7 @@
212212
}
213213
</script>
214214

215-
<div class="relative">
215+
<div class="relative z-40">
216216
<button
217217
onclick={toggleDropdown}
218218
class="btn btn-ghost btn-icon relative"
@@ -228,10 +228,10 @@
228228

229229
{#if showDropdown}
230230
<div
231-
class="absolute right-0 mt-2 w-96 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 z-50"
231+
class="absolute right-0 mt-2 w-96 bg-surface-overlay dark:bg-dark-surface-overlay rounded-lg shadow-lg border border-border-default dark:border-dark-border-default z-50"
232232
transition:fly={{ y: -10, duration: 200 }}
233233
>
234-
<div class="p-4 border-b border-gray-200 dark:border-gray-700">
234+
<div class="p-4 border-b border-border-default dark:border-dark-border-default">
235235
<div class="flex justify-between items-center">
236236
<h3 class="font-semibold text-lg">Notifications</h3>
237237
{#if $unreadCount > 0}
@@ -251,13 +251,13 @@
251251
<span class="loading loading-spinner loading-sm"></span>
252252
</div>
253253
{:else if $notifications.length === 0}
254-
<div class="p-8 text-center text-gray-500">
254+
<div class="p-8 text-center text-fg-muted dark:text-dark-fg-muted">
255255
No notifications yet
256256
</div>
257257
{:else}
258258
{#each $notifications as notification}
259259
<div
260-
class="p-4 border-b border-gray-100 dark:border-gray-700 hover:bg-gray-50 dark:hover:bg-gray-700 cursor-pointer transition-colors"
260+
class="p-4 border-b border-border-default/50 dark:border-dark-border-default hover:bg-interactive-hover dark:hover:bg-dark-interactive-hover cursor-pointer transition-colors"
261261
class:bg-blue-50={notification.status !== 'read'}
262262
class:dark:bg-blue-900={notification.status !== 'read'}
263263
onclick={() => {
@@ -297,10 +297,10 @@
297297
<p class="font-medium text-sm">
298298
{notification.subject}
299299
</p>
300-
<p class="text-sm text-gray-600 dark:text-gray-400 mt-1">
300+
<p class="text-sm text-fg-muted dark:text-dark-fg-muted mt-1">
301301
{notification.body}
302302
</p>
303-
<p class="text-xs text-gray-500 dark:text-gray-500 mt-2">
303+
<p class="text-xs text-fg-subtle dark:text-dark-fg-subtle mt-2">
304304
{formatTime(notification.created_at)}
305305
</p>
306306
</div>
@@ -313,7 +313,7 @@
313313
{/if}
314314
</div>
315315

316-
<div class="p-3 border-t border-gray-200 dark:border-gray-700">
316+
<div class="p-3 border-t border-border-default dark:border-dark-border-default">
317317
<button
318318
onclick={() => {
319319
showDropdown = false;
@@ -327,10 +327,3 @@
327327
</div>
328328
{/if}
329329
</div>
330-
331-
<style>
332-
/* Ensure dropdown is above other content */
333-
.relative {
334-
z-index: 40;
335-
}
336-
</style>

frontend/src/components/Spinner.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
primary: 'text-primary dark:text-primary-light',
1717
white: 'text-white',
1818
current: 'text-current',
19-
muted: 'text-gray-400 dark:text-gray-500'
19+
muted: 'text-fg-subtle dark:text-dark-fg-subtle'
2020
};
2121
2222
let sizeClass = $derived(sizeClasses[size] || sizeClasses.medium);

frontend/src/components/ToastContainer.svelte

Lines changed: 4 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@
6464
});
6565
6666
function getToastClasses(type: ToastType): string {
67-
const base = "toast";
67+
const base = "toast-item";
6868
switch (type) {
6969
case 'success':
7070
return `${base} bg-green-50 border-green-200 text-green-800 dark:bg-green-950 dark:border-green-800 dark:text-green-200`;
@@ -101,7 +101,7 @@
101101
}
102102
103103
function getTimerClasses(type: ToastType): string {
104-
const base = "timer";
104+
const base = "toast-timer";
105105
switch (type) {
106106
case 'success': return `${base} bg-green-300 dark:bg-green-600`;
107107
case 'error': return `${base} bg-red-300 dark:bg-red-600`;
@@ -112,7 +112,7 @@
112112
}
113113
</script>
114114

115-
<div class="toasts-container">
115+
<div class="toast-container">
116116
{#each toastList as toast (toast.id)}
117117
<div
118118
role="alert"
@@ -149,47 +149,4 @@
149149
{/if}
150150
</div>
151151
{/each}
152-
</div>
153-
154-
<style>
155-
.toasts-container {
156-
position: fixed;
157-
top: 5rem;
158-
right: 1.5rem;
159-
z-index: 100;
160-
width: 100%;
161-
max-width: 24rem;
162-
pointer-events: none;
163-
}
164-
165-
.toast {
166-
position: relative;
167-
display: flex;
168-
align-items: flex-start;
169-
padding: 1rem;
170-
margin-bottom: 0.75rem;
171-
border-radius: 0.5rem;
172-
box-shadow: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
173-
overflow: hidden;
174-
pointer-events: auto;
175-
border-width: 1px;
176-
}
177-
178-
.timer {
179-
position: absolute;
180-
bottom: 0;
181-
left: 0;
182-
height: 3px;
183-
transform-origin: left;
184-
transition: transform 0.1s linear;
185-
}
186-
187-
@media (max-width: 640px) {
188-
.toasts-container {
189-
top: 4.5rem;
190-
left: 1rem;
191-
right: 1rem;
192-
max-width: none;
193-
}
194-
}
195-
</style>
152+
</div>

frontend/src/components/__tests__/NotificationCenter.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ const iconTestCases = [
211211
];
212212

213213
const priorityTestCases = [
214-
{ severity: 'low' as const, css: '.text-gray-600' },
214+
{ severity: 'low' as const, css: '.text-fg-muted' },
215215
{ severity: 'medium' as const, css: '.text-blue-600' },
216216
{ severity: 'high' as const, css: '.text-orange-600' },
217217
{ severity: 'urgent' as const, css: '.text-red-600' },

frontend/src/components/__tests__/Spinner.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ describe('Spinner', () => {
4242
{ color: 'primary', expectedClass: 'text-primary' },
4343
{ color: 'white', expectedClass: 'text-white' },
4444
{ color: 'current', expectedClass: 'text-current' },
45-
{ color: 'muted', expectedClass: 'text-gray-400' },
45+
{ color: 'muted', expectedClass: 'text-fg-subtle' },
4646
] as const)('applies $expectedClass for color="$color"', ({ color, expectedClass }) => {
4747
render(Spinner, { props: { color } });
4848
expect(getSpinner().classList.contains(expectedClass)).toBe(true);

frontend/src/components/__tests__/ToastContainer.test.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ describe('ToastContainer', () => {
2020
describe('rendering', () => {
2121
it('renders empty container when no toasts', () => {
2222
const { container } = render(ToastContainer);
23-
const toastContainer = container.querySelector('.toasts-container');
23+
const toastContainer = container.querySelector('.toast-container');
2424
expect(toastContainer).toBeInTheDocument();
2525
expect(toastContainer?.children.length).toBe(0);
2626
});
@@ -83,11 +83,11 @@ describe('ToastContainer', () => {
8383
addToast('Timed toast', 'info');
8484

8585
await waitFor(() => {
86-
const timer = screen.getByRole('alert').querySelector('.timer');
86+
const timer = screen.getByRole('alert').querySelector('.toast-timer');
8787
expect(timer).toBeInTheDocument();
8888
});
8989

90-
const timer = screen.getByRole('alert').querySelector('.timer') as HTMLElement;
90+
const timer = screen.getByRole('alert').querySelector('.toast-timer') as HTMLElement;
9191
await vi.advanceTimersByTimeAsync(1000);
9292

9393
await waitFor(() => { expect(timer.style.transform).toContain('scaleX'); });
@@ -194,8 +194,8 @@ describe('ToastContainer', () => {
194194

195195
// Both toasts should have progress bars
196196
const alerts = screen.getAllByRole('alert');
197-
expect(alerts[0].querySelector('.timer')).toBeInTheDocument();
198-
expect(alerts[1].querySelector('.timer')).toBeInTheDocument();
197+
expect(alerts[0].querySelector('.toast-timer')).toBeInTheDocument();
198+
expect(alerts[1].querySelector('.toast-timer')).toBeInTheDocument();
199199
});
200200

201201
it('cleans up timers on unmount', async () => {

0 commit comments

Comments
 (0)