Server Deploy / deploy (push) Successful in 1m6s
web/ was the last app on its own palette — a neutral #0f1117 ground with an indigo accent, unrelated to the logo navy that site/ and adminsite/ are built on. It now draws from the same tokens, so all three apps are one system. It stays locked to dark, taking site/'s dark values. That is what keeps adminsite/'s light default meaningful: an operator with both open tells them apart by the ground before clicking anything destructive, and now that both are the same palette, the ground is the only thing left doing that work. The colour names stay this app's own — text-primary, border, surface rather than ink, rule, panel — because every screen already reads that way, and adminsite/ already establishes that each app names the shared tokens after its own subject. Tokens are stored as RGB channels with the hex in a trailing comment. The console leans on Tailwind opacity modifiers far more than the other two (bg-danger/10, border-accent/50, ring-accent/30), and <alpha-value> only compiles against channels; the comments keep the three token blocks diffable by eye. Beyond colour: - radii collapse to site/'s 4px in tailwind.config.ts rather than rewriting ~140 rounded-lg classes; rounded-full is untouched for dots and pills - badges become site/'s chip — mono, uppercase, tracked, currentColor rule, no fill — keeping their dot so state is never colour alone - table column heads take the keyed-label idiom, at text-secondary rather than tertiary, which lands under 4.5:1 at that size - sidebar marks the active item with an accent bar, the device site/ uses for the chosen plan, instead of a filled pill that reads as pressable - filled accent and danger buttons take accent-ink; the dark accent is a light blue and danger a coral, and white on either was unreadable - login's packet pulses shift from green to the accent, so the sign-in screen is the same two blues as the marketing hero The last hex literals and stock-palette classes are gone; the only ones left are NetworkBackground's canvas fills, which cannot read a CSS variable and are commented with the token each came from. Only the web image rebuilds from this.
178 lines
4.9 KiB
CSS
178 lines
4.9 KiB
CSS
@tailwind base;
|
|
@tailwind components;
|
|
@tailwind utilities;
|
|
|
|
/* ==========================================================================
|
|
Vantage control plane design tokens.
|
|
|
|
These are site/app/globals.css's **dark** token values — same names, same
|
|
colours — so the marketing site, the HQ console and this app are one visual
|
|
system. Change them in all three apps in the same commit; nothing enforces
|
|
the match automatically.
|
|
|
|
Two things differ from site/ and adminsite/, both deliberate:
|
|
|
|
1. There is no light block. web/ stays locked to dark. adminsite/ defaults
|
|
to light precisely so that a staff member holding both open can tell
|
|
them apart before clicking something destructive, and that argument
|
|
only holds while this app is the dark one.
|
|
|
|
2. Every colour is stored as RGB channels, with the hex it came from in a
|
|
trailing comment so it still diffs against site/ by eye. The plain
|
|
--token is derived from the channels rather than written twice. This app
|
|
leans hard on Tailwind's opacity modifiers (bg-danger/10, border-accent/50,
|
|
ring-accent/30 and so on), and those only compile when the colour reaches
|
|
Tailwind as channels it can drop an alpha into.
|
|
========================================================================== */
|
|
|
|
:root {
|
|
color-scheme: dark;
|
|
|
|
--ground-rgb: 7 22 40; /* #071628 */
|
|
--panel-rgb: 13 33 56; /* #0d2138 */
|
|
--panel-2-rgb: 16 40 66; /* #102842 */
|
|
--ink-rgb: 228 236 246; /* #e4ecf6 */
|
|
--ink-2-rgb: 159 179 202; /* #9fb3ca */
|
|
--ink-3-rgb: 113 135 159; /* #71879f */
|
|
--rule-rgb: 30 56 85; /* #1e3855 */
|
|
--rule-soft-rgb: 23 44 68; /* #172c44 */
|
|
--accent-rgb: 91 155 232; /* #5b9be8 */
|
|
--accent-ink-rgb: 4 16 31; /* #04101f */
|
|
--up-rgb: 79 180 132; /* #4fb484 */
|
|
--down-rgb: 226 112 90; /* #e2705a */
|
|
--pend-rgb: 214 166 63; /* #d6a63f */
|
|
--logo-rgb: 127 178 240; /* #7fb2f0 */
|
|
|
|
/* Not in site/: hover states and the well.
|
|
|
|
site/ brightens the accent with a filter, which a Tailwind colour token
|
|
cannot do, so the two hovers are named values. --well is the floor the
|
|
ground sits on — install one-liners, key blobs and run logs, the places
|
|
showing machine output rather than interface. */
|
|
--accent-hover-rgb: 127 178 240; /* #7fb2f0, the accent at site/'s hover brightness */
|
|
--down-hover-rgb: 200 90 69; /* #c85a45 */
|
|
--well-rgb: 4 16 31; /* #04101f */
|
|
|
|
--ground: rgb(var(--ground-rgb));
|
|
--panel: rgb(var(--panel-rgb));
|
|
--panel-2: rgb(var(--panel-2-rgb));
|
|
--ink: rgb(var(--ink-rgb));
|
|
--ink-2: rgb(var(--ink-2-rgb));
|
|
--ink-3: rgb(var(--ink-3-rgb));
|
|
--rule: rgb(var(--rule-rgb));
|
|
--rule-soft: rgb(var(--rule-soft-rgb));
|
|
--accent: rgb(var(--accent-rgb));
|
|
--accent-ink: rgb(var(--accent-ink-rgb));
|
|
--up: rgb(var(--up-rgb));
|
|
--down: rgb(var(--down-rgb));
|
|
--pend: rgb(var(--pend-rgb));
|
|
--logo: rgb(var(--logo-rgb));
|
|
--well: rgb(var(--well-rgb));
|
|
|
|
--shadow: 0 1px 0 rgba(0, 0, 0, 0.35), 0 20px 44px -26px rgba(0, 0, 0, 0.85);
|
|
|
|
--sans: ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
|
|
--mono: ui-monospace, "Cascadia Mono", "SF Mono", "JetBrains Mono", Menlo, Consolas, monospace;
|
|
}
|
|
|
|
* {
|
|
box-sizing: border-box;
|
|
margin: 0;
|
|
padding: 0;
|
|
}
|
|
|
|
body {
|
|
background: var(--ground);
|
|
color: var(--ink);
|
|
font-family: var(--sans);
|
|
-webkit-font-smoothing: antialiased;
|
|
}
|
|
|
|
/* site/'s heading treatment, which is what stands in for a display face.
|
|
Utilities still win where a component sets weight or tracking explicitly. */
|
|
h1,
|
|
h2,
|
|
h3 {
|
|
font-weight: 800;
|
|
letter-spacing: -0.03em;
|
|
text-wrap: balance;
|
|
}
|
|
|
|
code {
|
|
font-family: var(--mono);
|
|
}
|
|
|
|
:focus-visible {
|
|
outline: 2px solid var(--accent);
|
|
outline-offset: 2px;
|
|
border-radius: 2px;
|
|
}
|
|
|
|
::-webkit-scrollbar {
|
|
width: 6px;
|
|
height: 6px;
|
|
}
|
|
|
|
::-webkit-scrollbar-track {
|
|
background: var(--panel-2);
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb {
|
|
background: var(--rule);
|
|
border-radius: 3px;
|
|
}
|
|
|
|
::-webkit-scrollbar-thumb:hover {
|
|
background: var(--ink-3);
|
|
}
|
|
|
|
@keyframes led-pulse {
|
|
0%,
|
|
100% {
|
|
opacity: 1;
|
|
}
|
|
50% {
|
|
opacity: 0.5;
|
|
}
|
|
}
|
|
.led-pulse {
|
|
animation: led-pulse 1.4s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes cell-ring {
|
|
0%,
|
|
100% {
|
|
box-shadow: 0 0 0 0 rgb(var(--accent-rgb) / 0.5);
|
|
}
|
|
50% {
|
|
box-shadow: 0 0 0 4px rgb(var(--accent-rgb) / 0);
|
|
}
|
|
}
|
|
.cell-ring {
|
|
animation: cell-ring 1.4s ease-in-out infinite;
|
|
}
|
|
|
|
@keyframes caret-blink {
|
|
50% {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
.caret-blink {
|
|
animation: caret-blink 1s step-end infinite;
|
|
}
|
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
.led-pulse,
|
|
.cell-ring,
|
|
.caret-blink {
|
|
animation: none;
|
|
}
|
|
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
animation-duration: 0.001ms !important;
|
|
transition-duration: 0.001ms !important;
|
|
}
|
|
}
|