Files
felhom.eu/documentation/design/design-system.md
T
admin a336fe3123 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.
2026-07-02 15:00:11 +02:00

143 lines
7.9 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Felhom design system v2
**Status:** canonical reference. Shipped in the controller UI with **TASK-D0** (controller v0.96.0,
2026-07-02); D1D4 (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 400800). **Data face:** JetBrains Mono (400500) 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 `<symbol id="i-<name>">`),
included once at the top of `<body>`. Stroke = `currentColor`, stroke-width 2, round caps.
- Usage convention everywhere: `<svg class="ico"><use href="#i-hard-drive"/></svg>`
(`.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`, `7085 → 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.