From b786a825d4aeee4057512b76590ceb815f2346c2 Mon Sep 17 00:00:00 2001 From: pptx704 Date: Tue, 24 Mar 2026 12:33:18 +0600 Subject: [PATCH] Polish dashboard frontend: spacing, copy, resilience MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Increase content padding (p-7→p-8) and table cell padding (px-4→px-5, py-3→py-4 for data rows) across capsules, keys, and snapshots pages - Improve animation performance: wrenn-glow uses opacity instead of box-shadow (compositor-only, no paint cost) - Add prefers-reduced-motion media query covering inline style animations - Fix OAuth error display on login page (read ?error= param on mount) - Harden clipboard copy with try-catch and toast fallback - Improve empty state copy, dialog microcopy, and error messages - Add retry button to error banners on keys page - Replace "All systems operational" footer bar with a clean 1px divider - Fix text truncation on long capsule/snapshot names (min-w-0 + truncate) --- frontend/src/app.css | 43 ++++- frontend/src/lib/components/AuthModal.svelte | 20 +-- frontend/src/lib/components/Sidebar.svelte | 32 ++-- frontend/src/lib/components/Toaster.svelte | 2 +- .../routes/auth/github/callback/+page.svelte | 2 +- .../routes/dashboard/capsules/+page.svelte | 164 +++++++++--------- .../src/routes/dashboard/keys/+page.svelte | 129 +++++++------- .../routes/dashboard/snapshots/+page.svelte | 126 +++++++------- frontend/src/routes/login/+page.svelte | 42 +++-- 9 files changed, 304 insertions(+), 256 deletions(-) diff --git a/frontend/src/app.css b/frontend/src/app.css index 72fd49b..765d14a 100644 --- a/frontend/src/app.css +++ b/frontend/src/app.css @@ -48,6 +48,20 @@ --font-mono: 'JetBrains Mono Variable', monospace; --font-brand: 'Alice', serif; + /* Type scale — rem-based (root = 87.5%, giving 14px at browser default) + Heading tokens carry a default line-height; body/UI tokens inherit the global 1.6. */ + --text-display: 2.571rem; /* ~36px — auth/login section headings */ + --text-display--line-height: 1.1; + --text-page: 2rem; /* ~28px — page h1 titles */ + --text-page--line-height: 1.15; + --text-heading: 1.429rem; /* ~20px — dialog headings, empty-state */ + --text-heading--line-height: 1.25; + --text-body: 1rem; /* 14px — primary body, buttons, inputs */ + --text-ui: 0.929rem; /* ~13px — nav labels, table cells, secondary */ + --text-meta: 0.857rem; /* ~12px — key prefixes, minor info */ + --text-label: 0.786rem; /* ~11px — uppercase section labels */ + --text-badge: 0.714rem; /* ~10px — live badges, tiny indicators */ + /* Radii */ --radius-card: 8px; --radius-input: 5px; @@ -62,9 +76,13 @@ /* Base styles */ html { font-family: var(--font-sans); - font-size: 14px; + font-size: 87.5%; /* 14px at browser default; scales with user text-size preferences */ + line-height: 1.6; color: var(--color-text-primary); background-color: var(--color-bg-0); + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + text-rendering: optimizeLegibility; } body { @@ -72,6 +90,11 @@ body { min-height: 100vh; } +/* Tabular figures on all mono text — numbers align in tables and metric displays */ +.font-mono { + font-variant-numeric: tabular-nums; +} + /* Selection */ ::selection { background: rgba(94, 140, 88, 0.25); @@ -97,14 +120,14 @@ body { background: var(--color-bg-5); } -/* Live status dot glow animation */ +/* Live status dot pulse animation — opacity-only for GPU compositor, zero paint cost */ @keyframes wrenn-glow { 0%, 100% { - box-shadow: 0 0 6px rgba(94, 140, 88, 0.5); + opacity: 1; } 50% { - box-shadow: 0 0 14px rgba(94, 140, 88, 0.2); + opacity: 0.3; } } @@ -119,3 +142,15 @@ body { transform: translateY(0); } } + +/* Respect user motion preferences — covers both CSS class animations and inline style animations */ +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + animation-duration: 0.01ms !important; + animation-iteration-count: 1 !important; + transition-duration: 0.01ms !important; + scroll-behavior: auto !important; + } +} diff --git a/frontend/src/lib/components/AuthModal.svelte b/frontend/src/lib/components/AuthModal.svelte index 3ab217f..2f23a5c 100644 --- a/frontend/src/lib/components/AuthModal.svelte +++ b/frontend/src/lib/components/AuthModal.svelte @@ -61,12 +61,12 @@
{title} {subtitle} @@ -75,7 +75,7 @@
@@ -136,7 +136,7 @@ bind:value={password} placeholder="Password" autocomplete={mode === 'signin' ? 'current-password' : 'new-password'} - class="w-full rounded-[var(--radius-input)] border border-[var(--color-border)] bg-[var(--color-bg-2)] py-2.5 pl-9 pr-10 text-[13px] text-[var(--color-text-bright)] outline-none transition-all duration-150 placeholder:text-[var(--color-text-muted)] focus:border-[var(--color-accent)]" + class="w-full rounded-[var(--radius-input)] border border-[var(--color-border)] bg-[var(--color-bg-2)] py-2.5 pl-9 pr-10 text-ui text-[var(--color-text-bright)] outline-none transition-all duration-150 placeholder:text-[var(--color-text-muted)] focus:border-[var(--color-accent)]" /> @@ -165,14 +165,14 @@ -

+

{switchText} -

+
-
+
{#if error} -
- {error} +
+ {error} +
{/if} {#if loading}
-
+
@@ -164,13 +175,13 @@
-

No API keys yet

-

Create a key to authenticate SDK and API requests.

+

No API keys yet

+

Create your first key to start making API requests.

@@ -232,20 +243,14 @@ {/each}
-

+

{keys.length} {keys.length === 1 ? 'key' : 'keys'} total

{/if}
- -
-
- - All systems operational -
-
+
@@ -260,17 +265,17 @@ >
-

New API Key

-

Give your key a name to identify it later.

+

New API Key

+

Give your key a name to identify it later.

{#if createError} -
+
{createError}
{/if}
-
@@ -287,14 +292,14 @@
-

{newKey.name || 'API Key'}

-

+

{newKey.name || 'API Key'}

+

Copy this key now — it won't be shown again.

- + {newKey.key ?? ''}
@@ -399,15 +404,15 @@ >
-

Revoke Key

-

+

Revoke Key

+

Revoke {revokeTarget.name || revokeTarget.id}? Any request using it will stop working immediately.

-

{revokeTarget.key_prefix}...

+

{revokeTarget.key_prefix}…

{#if revokeError} -
+
{revokeError}
{/if} @@ -416,14 +421,14 @@ @@ -220,16 +220,16 @@ {#if pageTab === 'snapshots'} -
+
{#if error} -
+
{error}
{/if} {#if loading}
-
+
@@ -243,7 +243,7 @@ {#each ([['all', 'All'], ['snapshot', 'Snapshots'], ['base', 'Images']] as const) as [val, label]} {/each}
- + {filteredSnapshots.length} {filteredSnapshots.length === 1 ? 'snapshot' : 'snapshots'} @@ -266,16 +266,16 @@
-

+

{emptyHeading(typeFilter)}

-

+

{emptyDescription(typeFilter)}

{#if typeFilter === 'all' || typeFilter === 'snapshot'} Go to Capsules @@ -290,13 +290,13 @@
-
Name
-
Type
-
vCPUs
-
Memory
-
Size
-
Created
-
Actions
+
Name
+
Type
+
vCPUs
+
Memory
+
Size
+
Created
+
Actions
@@ -306,19 +306,19 @@ style="grid-template-columns: 2fr 1fr 0.7fr 0.9fr 0.8fr 1.3fr 140px; animation: fadeUp 0.35s ease both; animation-delay: {i * 40}ms" > -
- {snapshot.name} +
+ {snapshot.name}
-
+
{#if snapshot.type === 'snapshot'} - + Snapshot {:else} - + Image @@ -326,31 +326,31 @@
-
+
{#if snapshot.type === 'snapshot' && snapshot.vcpus != null} - {snapshot.vcpus} + {snapshot.vcpus} {:else} - + {/if}
-
+
{#if snapshot.type === 'snapshot' && snapshot.memory_mb != null} - {snapshot.memory_mb} MB + {snapshot.memory_mb} MB {:else} - + {/if}
-
- {formatBytes(snapshot.size_bytes)} +
+ {formatBytes(snapshot.size_bytes)}
-
- {timeAgo(snapshot.created_at)} +
+ {timeAgo(snapshot.created_at)}
@@ -359,7 +359,7 @@ @@ -392,7 +392,7 @@ {/each}
-

+

{filteredSnapshots.length} {filteredSnapshots.length === 1 ? 'snapshot' : 'snapshots'} {typeFilter !== 'all' ? `· filtered` : '· total'}

@@ -406,7 +406,7 @@
- All systems operational + All systems operational
@@ -427,7 +427,7 @@ openDropdownName = null; if (target) { deleteTarget = target; deleteError = null; } }} - class="flex w-full items-center gap-2 px-3 py-2 text-[12px] text-[var(--color-red)] transition-colors duration-150 hover:bg-[var(--color-red)]/5" + class="flex w-full items-center gap-2 px-3 py-2 text-meta text-[var(--color-red)] transition-colors duration-150 hover:bg-[var(--color-red)]/5" > @@ -452,8 +452,8 @@ class="relative w-full max-w-[380px] rounded-[var(--radius-card)] border border-[var(--color-border-mid)] bg-[var(--color-bg-2)] p-6" style="animation: fadeUp 0.2s ease both" > -

Delete Snapshot

-

+

Delete Snapshot

+

Delete {deleteTarget.name}? This action cannot be undone.

@@ -464,14 +464,14 @@ -

+

This live capture includes saved memory state. Any capsule relying on it will be unable to resume.

{/if} {#if deleteError} -
+
{deleteError}
{/if} @@ -480,14 +480,14 @@
@@ -250,13 +258,13 @@ {/if} {#if error} -

{error}

+

{error}

{/if}