Redesign UI with new token system, primitives, and motion

Restraint-first design language: neutral gray surfaces + one blue accent
(cyan/amber/red reserved for state), 8pt spacing, softer radii, one
ambient + one raised shadow, standardized motion tokens. Alfa Slab One
demoted to BrandMark only; Inter carries all page copy.

New primitives (Button, Card, Badge, Stat, SectionHeader, Skeleton,
EmptyState, Tabs, Breadcrumbs, StatusPip, Field, Input, Select, Textarea)
plus motion utilities (tweenNumber, viewTransition) and a global toast
system. All eight route surfaces rewritten on top: Fleet gains an
overview hero, Server detail restructures into tabs, Deployments gets a
collapsible drift summary + per-server pipeline visual, and the
scripts/configs/plugins/betas flows adopt the new form primitives.

Sticky translucent nav with backdrop blur, view-transition cross-fades
between routes, staggered card entrance on Fleet, breathing status pip
halo, tabular-numeric player counts. Gmail-style g-key nav shortcuts.
Mobile stacks below 720px; prefers-reduced-motion neutralizes durations.
Legacy tokens preserved as aliases so any un-migrated code still renders.
This commit is contained in:
2026-09-14 19:48:48 -04:00
parent efd4a4fa66
commit a2d4a38925
34 changed files with 5093 additions and 3144 deletions
+41
View File
@@ -0,0 +1,41 @@
import { writable } from 'svelte/store';
// Small global toast queue.
//
// Any part of the UI can drop a message and it appears in the corner for a
// couple of seconds, then quietly dismisses. Replaces the ad-hoc "flash"
// strings scattered across pages when the message doesn't need to live inline
// with a form.
export type ToastVariant = 'info' | 'success' | 'warning' | 'danger';
export interface Toast {
id: number;
message: string;
variant: ToastVariant;
ttl: number;
}
export const toasts = writable<Toast[]>([]);
let nextId = 1;
export function toast(
message: string,
opts: { variant?: ToastVariant; ttl?: number } = {}
): void {
const t: Toast = {
id: nextId++,
message,
variant: opts.variant ?? 'info',
ttl: opts.ttl ?? 3500
};
toasts.update((arr) => [...arr, t]);
if (t.ttl > 0) {
setTimeout(() => dismiss(t.id), t.ttl);
}
}
export function dismiss(id: number): void {
toasts.update((arr) => arr.filter((t) => t.id !== id));
}