From a336fe3123ddca5e274d4a56b073a1fea386f7ec Mon Sep 17 00:00:00 2001 From: kisfenyo Date: Thu, 2 Jul 2026 15:00:11 +0200 Subject: [PATCH] docs(design): design system v2 canonical reference (TASK-D0) Tokens, component semantics (meter/tag/metarow/panel/list/section-h/ stats/buttons), the state truth tables incl. the stopped->neutral exception-color change, vendored typography (latin-ext) + Lucide iconography, chart palette, and the grep-gate enforcement list. Shipped in controller v0.96.0; D1-D4 build from this document. --- documentation/design/design-system.md | 142 ++++++++++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 documentation/design/design-system.md diff --git a/documentation/design/design-system.md b/documentation/design/design-system.md new file mode 100644 index 0000000..56b17ac --- /dev/null +++ b/documentation/design/design-system.md @@ -0,0 +1,142 @@ +# Felhom design system v2 + +**Status:** canonical reference. Shipped in the controller UI with **TASK-D0** (controller v0.96.0, +2026-07-02); D1–D4 (settings IA split, Tárhely nav promotion, website, hub) build from this document. + +The system was approved via two mockup rounds. Four pillars: + +1. **Logo-derived navy palette** — a single blue accent on layered navy, replacing the GitHub-dark clone. +2. **Exception-based status color** — nominal state is blue/neutral; amber/red appear **only on deviation**. +3. **Vendored assets** — fonts and icons ship inside the binary; no CDN dependency (offline customer nodes). +4. **Flat metadata** — no pills, no shadows, no bubbles; one 2px radius everywhere. + +## The rule set + +- **One radius: 2px** (`--radius`). The only exceptions are true circles (state dots, spinners) at 50%. +- **No shadows.** No `box-shadow` anywhere. Depth comes from background layering (`--bg-0/1/2`) and + hairlines (`--line`, `--line-soft`). +- **Boxless sections.** Section headers are an h3 + muted hint over a 1px hairline (`.section-h`), + not a card. Panels (`.panel`) exist for genuinely grouped content, flat: bg-1 + 1px line. +- **Color = exception.** Anything operating normally is blue or neutral. Green does not exist in the + system. Amber (`--warn`) and red (`--crit`) are reserved for deviations that need attention. + Categorical **data series** (charts) are exempt — they may use distinct hues; *status* may not. + +## Tokens (canonical, verbatim) + +```css +:root { + --bg-0: #0A1220; --bg-1: #0F1B2E; --bg-2: #16263F; + --line: #22344F; --line-soft: #1A2A42; + --text-1: #EDF2F9; --text-2: #94A6BF; --text-3: #5E7392; + --blue: #0083D8; --blue-bright: #2EA8F5; --blue-dim: rgba(0,131,216,.13); + --warn: #E0A93E; --warn-dim: rgba(224,169,62,.12); + --crit: #E5534B; --crit-dim: rgba(229,83,75,.12); + --radius: 2px; + --font-ui: 'Plus Jakarta Sans', -apple-system, sans-serif; + --font-data: 'JetBrains Mono', monospace; +} +``` + +Semantics: `--bg-0` page, `--bg-1` panel, `--bg-2` elevated/nested surface (buttons, inputs, table +stripes). `--text-1` primary, `--text-2` secondary, `--text-3` muted/labels. `--line` visible +hairline, `--line-soft` row separators and meter tracks. `--blue` fills and primary actions, +`--blue-bright` text-on-dark accent and hover, `--blue-dim` selection/hover washes. + +## Typography + +- **UI face:** Plus Jakarta Sans (weights 400–800). **Data face:** JetBrains Mono (400–500) for + every numeric/technical value: sizes, times, percentages, paths, IDs, versions. +- Both are **vendored variable woff2 files** embedded in the binary and served from + `/static/fonts/` (immutable cache). **latin-ext is mandatory** — Hungarian ő/ű break without it. + `unicode-range` is preserved from the upstream subsets so latin-ext only loads when needed. +- No CDN font loading anywhere. `fonts.googleapis.com` is a banned string in the codebase. + +## Iconography + +- **Vendored Lucide sprite** (`templates/icons.html`, 30 icons as ``), + included once at the top of ``. Stroke = `currentColor`, stroke-width 2, round caps. +- Usage convention everywhere: `` + (`.ico` 16px, `.ico-sm` 14px, `.ico-spin` for progress). +- **Emoji are banned** in templates and JS-built strings (✅❌⚠🔒⏳… — see the D0 grep gate list). +- Standalone pages that don't include the layout (catchall, setup wizard) get inline SVGs or plain + text, not the sprite. + +## Components + +### Meter (`.meter` + `.meter-track` / `.meter-fill`) +The capacity readout — replaces every zone-colored bar. A 3px hairline track (`--line-soft`) with a +fill; **the fill is blue in nominal state**. Two 1px *neutral* ticks are baked into the track at 70% +and 85% (`.meter-track::before/::after`; `.no-ticks` disables). The wrapper carries the state class +from `usageColor`: `nominal` (blue), `warn` (amber fill), `crit` (red fill). Under warn/crit a +`.meter-flag` renders (dim background chip + triangle-alert icon) with the exact copy: +warn → **„Fogyóban a hely"**, crit → **„Kritikusan kevés hely"** (storage meters). +Disconnected/unknown: `.meter-empty` — empty track, value `—`, no color, no flag. +Deploy-page variant `.meter-track-stacked`: two segments — committed = solid `--blue`, planned/new = +`--blue` at 35% opacity. + +### Tag (`.tag`) +The state chip — replaces pills. Inline-flex, **2px radius**, 1px border, 6px round dot. Modifiers: +`tag-run` (blue border/text/dot), `tag-progress` (blue + pulsing dot; `prefers-reduced-motion` +respected), `tag-warn` (amber + dim bg), `tag-crit` (red + dim bg), `tag-neutral`/`tag-off` +(gray, hollow dot). Protected/locked content uses a neutral tag with the `i-lock` icon. + +### Metadata row (`.metarow` / `.metarows`) +Informational chips ("~512M", "Pi kompatibilis", "HDD szükséges") become icon + text with **no +container**: 14px muted-stroke icon, `--text-2` text, mono for values. + +### Panel / List (`.panel`, `.list .row`, `.stack-list`) +Panel: `--bg-1`, 1px `--line`, 2px radius, no shadow. List: one panel whose rows are separated by +`border-top: 1px solid var(--line-soft)`; each row has a 2px left state edge via `::before` — +blue for run/progress, amber for warn, transparent for neutral/off. + +### Section header (`.section-h`) +h3 + right-aligned muted hint over `border-bottom: 1px solid var(--line)`. No box. + +### Stats (`.stats-grid` / `.stat-card`) +Boxless figures with 1px right hairline dividers; numbers in `--font-data` at 1.9rem. A zero +"Leállítva" renders `--text-3` (`.stat-zero`); a nonzero stopped count renders `--warn` +(`.stat-warn`) — the exception rule applied to numbers. + +### Buttons +2px radius. Default: `--bg-2` + `--line` border, hover border `--blue`. `.btn-primary`: solid blue. +`.btn-danger`: **transparent background, `--crit` border + text** — danger is outline until the +confirm step (filled red only inside confirmation modals). `:focus-visible` = 2px `--blue-bright` +outline on all interactive elements. + +## State truth tables (funcmap, controller `internal/web/funcmap.go`) + +`stateColor(ContainerState)` → class suffix (consumed as `tag-*`, `stack-state-*`, `state-text-*`): + +| ContainerState | token | rendering | +|---|---|---| +| running | `run` | blue tag + blue row edge — nominal | +| starting, deploying | `progress` | blue tag, pulsing dot, blue edge | +| unhealthy | `warn` | amber tag + amber edge | +| restarting | `warn` | amber (a restart loop is a problem, not progress) | +| stopped, exited | `neutral` | gray tag, hollow dot, **no** colored edge | +| paused | `neutral` | gray | +| not_deployed, unknown | `off` | gray tag, no row edge | + +> Deliberate semantic change (operator-approved): **stopped/exited is neutral, not red.** A +> customer-stopped app is not an emergency; genuine failures surface via unhealthy state + alerts. + +`usageColor(percent)`: `<70 → nominal`, `70–85 → warn`, `≥85 → crit`. +`tempColor(celsius)`: `>75 → crit`, `≥60 → warn`, else `nominal` (thresholds unchanged from v1). +Hungarian `stateLabel` copy is frozen (guarded byte-identical by unit test). + +Text color classes: `.state-text-{run,progress,warn,crit,neutral,off,nominal}` — +`nominal` = `--text-1` (no color), `run`/`progress` = `--blue-bright`, `neutral`/`off` = `--text-3`. + +## Chart palette (Chart.js, monitoring) + +Categorical series (data, not status): cpu `#2EA8F5`, memory `#8E7CE8`, temp `#E0A93E`, +load `#5EC4B6`. Chrome: tooltip bg `#0F1B2E`, title `#EDF2F9`, body `#94A6BF`, border `#22344F`, +ticks `#94A6BF`, axis titles `#5E7392`, grid `rgba(34,52,79,.5)`. + +## Enforcement + +The D0 grep gate (all counts must be 0 in `controller/internal/{web,setup}`): +`fonts.googleapis.com`, `border-radius: 999px`, `box-shadow`, the GitHub-dark hexes +(`#0d1117 #161b22 #1c2128 #30363d #238636 #da3633 #d29922 #db6d28`), +`system-bar-{green,yellow,red}`, `state-text-{green,red,yellow,orange}`, and the emoji list +(⚙ 🔧 ✅ ❌ 🔒 ✏ 📦 🔗 ✕ ⏳ 🔴 🟡 ℹ️ ⚠ 💾). Run it after any UI change.