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:
@@ -0,0 +1,102 @@
|
||||
<script lang="ts">
|
||||
interface Props {
|
||||
value?: string;
|
||||
type?: 'text' | 'search' | 'email' | 'number' | 'url' | 'password' | 'file';
|
||||
placeholder?: string;
|
||||
id?: string;
|
||||
name?: string;
|
||||
required?: boolean;
|
||||
disabled?: boolean;
|
||||
readonly?: boolean;
|
||||
autocomplete?: import('svelte/elements').FullAutoFill;
|
||||
mono?: boolean;
|
||||
onchange?: (e: Event) => void;
|
||||
oninput?: (e: Event) => void;
|
||||
accept?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
value = $bindable(''),
|
||||
type = 'text',
|
||||
placeholder,
|
||||
id,
|
||||
name,
|
||||
required = false,
|
||||
disabled = false,
|
||||
readonly = false,
|
||||
autocomplete,
|
||||
mono = false,
|
||||
onchange,
|
||||
oninput,
|
||||
accept
|
||||
}: Props = $props();
|
||||
</script>
|
||||
|
||||
<input
|
||||
class="input"
|
||||
class:mono
|
||||
{type}
|
||||
{id}
|
||||
{name}
|
||||
{placeholder}
|
||||
{required}
|
||||
{disabled}
|
||||
{readonly}
|
||||
{autocomplete}
|
||||
{accept}
|
||||
bind:value
|
||||
{onchange}
|
||||
{oninput}
|
||||
/>
|
||||
|
||||
<style>
|
||||
.input {
|
||||
width: 100%;
|
||||
padding: 9px 12px;
|
||||
background: var(--surface-sunken);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-md);
|
||||
color: var(--text-primary);
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-sm);
|
||||
line-height: 1.4;
|
||||
transition:
|
||||
border-color var(--dur-fast) var(--ease-standard),
|
||||
background-color var(--dur-fast) var(--ease-standard),
|
||||
box-shadow var(--dur-fast) var(--ease-standard);
|
||||
}
|
||||
.input.mono { font-family: var(--font-mono); }
|
||||
.input::placeholder { color: var(--text-quaternary); }
|
||||
|
||||
.input:hover { border-color: var(--border-strong); }
|
||||
.input:focus {
|
||||
outline: none;
|
||||
border-color: var(--accent-primary);
|
||||
background: var(--surface);
|
||||
box-shadow: 0 0 0 3px rgba(46, 134, 209, 0.15);
|
||||
}
|
||||
.input:disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.input[type='file'] {
|
||||
padding: 6px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.input[type='file']::file-selector-button {
|
||||
margin-right: var(--space-3);
|
||||
padding: 6px 12px;
|
||||
background: var(--surface-raised);
|
||||
color: var(--text-primary);
|
||||
border: 1px solid var(--border-subtle);
|
||||
border-radius: var(--radius-sm);
|
||||
cursor: pointer;
|
||||
font-family: var(--font-body);
|
||||
font-size: var(--text-xs);
|
||||
font-weight: var(--weight-medium);
|
||||
transition: background-color var(--dur-fast) var(--ease-standard);
|
||||
}
|
||||
.input[type='file']::file-selector-button:hover {
|
||||
background: var(--surface-overlay);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user