Files
felhom.eu/documentation/design/design-system.md
T
admin 43189e8972 D3 Part 4: docs — .tag/.badge addendum, website CHANGELOG, REPORT
- design-system.md: the .tag (outline+dot, state) vs .badge (filled
  chip, static informational label) distinction the D1 unified view
  left implicit, + a Website (TASK-D3) section (shared site.css,
  self-hosted fonts, external sprite, mask-based CSS marks, ?v=
  cache-busting, site_gates.py enforcement).
- website/CHANGELOG.md created (newest-on-top) with the D3 entry.
- REPORT.md overwritten per §15 (baselines, commits, gate outputs +
  all three red-proofs, 84-problem/182-emoji baseline → 0, contact-form
  freeze proof + live TESZT submission resend id, live checks, the
  narrow-viewport limitation, observations).
2026-07-02 22:19:58 +02:00

170 lines
9.5 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)`.
## `.tag` vs `.badge` (D3 addendum — resolves the D1 parallel-component drift)
Both exist in the controller; they are distinct components, not variants:
- **`.tag`** — outline + dot; expresses **state** (run/progress/warn/crit/neutral/off). Use it
whenever the thing shown can change at runtime: container state, drive activity, health.
The dot is part of the semantics (pulsing = in progress, hollow = inert).
- **`.badge`** — filled chip (dim background, no dot); expresses a **static informational label**:
tier names, protocol/type markers (USB, NFS), feature keywords on catalog cards. Nothing about
it changes at runtime.
- If the label has an icon-and-text metadata character ("~512M", "Pi kompatibilis"), prefer
**`.metarow`** (no container at all) over either chip.
- On the website, catalog feature keywords are `.badge`-class chips (filled, 2px, blue-dim) —
informational labels, not states.
## Website (TASK-D3)
The seven felhom.eu pages share ONE stylesheet (`website/assets/site.css?v=N`) built on this
document's tokens: self-hosted fonts (`/assets/fonts/`, no CDN — GDPR), a 70-symbol external
Lucide sprite (`/assets/icons.svg?v=N#i-name`; feature tiles = `.ico-tile` 48px bg-2 squares),
the two-tone heading as the brand signature (accent span = solid `--blue-bright`), and
mask-based ✓/✗ marks for CSS-generated content (sprites can't reach `::before`). Marketing-only
scale addition: hero H1 up to ~3.5rem/800. Assets are cache-busted with `?v=N` (nginx `expires
7d`) — bump every reference together. Enforcement: `scripts/site_gates.py` (BOM, emoji=0,
nav/footer consistency, analytics presence, no-CDN, banned tokens, zero `<style>` blocks,
cache-busting).
## 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.