Files
ersatztv/design-system/readme.md
T
2026-07-02 07:37:00 +02:00

191 lines
12 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.
# ChicoryTV Design System
A dark, slick, app-like admin design system for **ChicoryTV** — a self-hosted IPTV /
custom-TV channel server. ChicoryTV is a modern reimagining of the **ErsatzTV** admin
UI (a .NET / Blazor Server + MudBlazor application): same product, same information
architecture, redrawn as a dense, technical, no-full-reload media-server console in the
spirit of tools like Komodo.
The product lets you turn a media library into personalized live TV channels — with an
EPG, channel scheduling, and streaming (IPTV/HLS/MPEG-TS) to any device.
## Sources
- **Codebase (ground truth):** `ersatztv/` — ErsatzTV fork, C#/.NET 10, Blazor Server, MudBlazor.
Key screens read for this system:
- `ErsatzTV/Shared/MainLayout.razor` — app bar, drawer nav, theme palette.
- `ErsatzTV/Pages/Channels.razor` — channel table + inline actions (the core view).
- `ErsatzTV/Pages/Playouts.razor` — playout list + playout-detail timeline items.
- `ErsatzTV/Pages/Schedules.razor`, `Libraries.razor`, `Index.razor` (health dashboard).
- **Upstream project:** ErsatzTV — https://ersatztv.org , https://github.com/ErsatzTV/ErsatzTV
- ChicoryTV is a **rebrand + redesign**, not a fork of the visual layer. The MudBlazor
green/Roboto theme is intentionally replaced; product vocabulary and IA are preserved.
> **Font substitution — please confirm.** ErsatzTV ships no brand font (MudBlazor defaults to
> Roboto). ChicoryTV adopts **Geist** (UI) + **Geist Mono** (numerics), loaded from Google
> Fonts. If you have a preferred/licensed brand typeface, share the files and it will be swapped in.
---
## Content fundamentals — how ChicoryTV writes
Voice is that of a **precise, technical operator tool** — terse, functional, confident. It
labels and instructs; it does not market or chit-chat.
- **Casing:** Title Case for primary actions and nav ("Add Channel", "Reset All Playouts",
"Edit Channel Numbers"); sentence case for helper text and descriptions.
- **Person:** mostly **impersonal / imperative** — verbs, not "you". Buttons are bare verbs
("Add Schedule", "Scan", "Delete", "Troubleshoot"). Occasional second person in help text.
- **Nouns are the product's domain language — use them exactly:** Channel, Schedule, Block,
Template, Deco, Playout (Classic / Block / Sequential / Scripted / JSON), Collection (Manual /
Smart / Multi / Rerun), Playlist, Library, Media Source (Local / Plex / Jellyfin / Emby),
FFmpeg Profile, Watermark, Filler, Streaming Mode (HLS Segmenter / HLS Direct / MPEG-TS),
EPG / XMLTV / M3U.
- **Numbers & time are first-class** and always concrete: channel numbers (`1.1`, `4.2`),
durations (`00:24:00`), clock times (`20:00:00`), counts (`1,204 items`). Rendered in mono.
- **Status is plain and specific:** "On air", "Healthy", "Not normalizing", "Playout build
failed — no items match the schedule window." State the thing and the consequence.
- **No emoji. No exclamation marks.** Tone stays flat and reliable. Icons carry affect, not punctuation.
- **Empty/blank:** say what is missing and the next action ("No playouts yet — Add Playout").
Examples pulled from the product surface: *"Show disabled"*, *"Show filler"*, *"Reset All
Playouts"*, *"Edit Channel Numbers"*, *"HLS Segmenter"*, *"Schedule Playout Reset"*,
*"This FFmpeg profile is not normalizing audio."*
---
## Visual foundations
**Overall vibe.** Dark, dense, engineered. Flat surfaces separated by hairline borders, one
cool accent, monospaced numerics. No decoration for its own sake — every pixel is data,
control, or the whitespace that organizes them.
**Color.** Near-black layered surfaces: app background `#0F1115`, sunken wells `#0B0D11`,
cards/rows `#171A21`, raised menus/hover `#1C2029`, selected `#222732`. Hairline borders
`#262B36` (dividers) and `#333A48` (controls). Text in three weights only: primary `#E6E9EF`,
muted `#9AA4B2`, faint `#626B7A`. **Exactly one accent** — in the primary *warm chicory* scheme this is roasted-amber `#E08A3C` (the cool *periwinkle* alternate uses `#5B7CFA`)
— reserved for primary actions, active nav, focus rings, and live/on-air state. Status colors
are used sparingly and only semantically: ok `#3FB984`, warn `#E0A83D`, error `#E5484D`. Each
status also has a ~14% soft tint for badges/backgrounds. Never introduce a second decorative hue.
**Type.** Geist for UI, base size **13px** (dense admin). Scale: 11 · 12 · 13 · 14 · 16 · 20 ·
26 · 34. Weights 400/500/600 (700 rare). Section eyebrows are 11px 600 uppercase, `0.06em`
tracking, faint. **Geist Mono with tabular figures** for everything numeric — channel numbers,
clock times, durations, counts, bitrates, percentages — so columns align. Headings use slight
negative tracking (`-0.01em`); everything else is normal.
**Spacing & layout.** 4px base grid (2 → 56). Fixed chrome: sidebar `232px`, top bar `52px`,
table/list rows `44px`, controls `34px` (sm `28px`). Layout is a fixed left sidebar + a
compact top bar + a scrolling content region. Page gutter `32px`; card interior `1624px`;
table cell padding `16px × 10px`. Dense but never cramped — rows breathe at 44px.
**Corners.** Tight and consistent: chips/badges `3px`, buttons/inputs `5px`, cards/rows/menus
`7px`, dialogs `10px`, pills fully round. Nothing is softer than 10px.
**Elevation.** Dark surfaces lean on **border + a subtle drop shadow**, never glow. `sm`
`0 1px 2px/.40`, `md` `0 4px 12px/.45`, popovers `0 8px 28px/.55`, dialogs `0 16px 40px/.55`.
Depth reads mostly through the surface-lightness ladder (bg → surface → raised → selected),
with shadow as a secondary cue. The one exception: live/on-air elements get a small accent
glow (playhead, pulsing status dot).
**Backgrounds.** No images, gradients, textures, or patterns — flat fills only. The single
"illustration" is the chicory-flower mark. Imagery, when present, is user content (channel
logos), shown on a sunken well and contained, never full-bleed.
**Borders & dividers.** 1px hairlines do the structural work: card edges, table row
separators, section splits, input outlines. Selected rows shift background rather than adding a
border; the active nav item adds a 2.5px accent rail on its left edge plus a soft accent tint.
**Motion.** Quick and functional. Durations 90 / 140 / 220ms; standard easing
`cubic-bezier(0.2,0,0,1)`, entrances `cubic-bezier(0.16,1,0.3,1)`. Hover = background/opacity
shift (`~90ms`); press = 0.5px nudge down, no scale bounce. Switches slide (`140ms`). The only
looping animations are meaningful: the live status-dot ping and indeterminate progress/scan
sweeps. Respect `prefers-reduced-motion`.
**States.**
- *Hover:* raise the surface one step (row → `#1C2029`) or lighten ghost controls; text goes
from muted to primary.
- *Active/selected:* accent-soft tint + (nav) accent rail; never a heavy fill.
- *Focus:* 3px accent ring (`--focus-ring`) + accent border on inputs.
- *Disabled:* 4050% opacity, no pointer.
- *Live/on-air:* accent color + pulsing dot + faint glow.
**Cards.** Flat `#171A21` surface, 1px hairline border, `7px` radius, no outer shadow by
default (shadow only when floating, e.g. menus/dialogs). Optional header row (title + subtitle
left, actions right) separated by a hairline; body padding `16px`, or `0` for flush tables.
**Transparency / blur.** Used sparingly: soft status/accent tints are alpha over the dark
surface; overlay scrims behind dialogs. No frosted-glass blur in the core admin.
### Color schemes (themes)
The system ships **three** interchangeable schemes as CSS theme scopes (`tokens/themes.css`).
Set `data-theme` on `<html>` to switch; each scope overrides only the base `--ctv-*` values, so
every component and semantic alias follows automatically.
- **Warm chicory** (primary, default `:root`) — roasted-amber accent `#E08A3C` + chicory-leaf
green secondary `#6B8F71`; live rides the amber. The signature ChicoryTV look.
- **Cool periwinkle** (`[data-theme="cool"]`) — the cooler, focused alternate; accent `#5B7CFA`.
- **Dual accent** (`[data-theme="dual"]`) — cool periwinkle app, with a warm focal hue `#F0663F`
driving the attention layer: live/on-air state **plus** primary actions, the active nav item, and
selection (wired through the `--action-primary` / `--ctv-live` aliases), so the two accents read
distinctly while warm and cool stay coherent.
The UI kit has a live swatch switcher (bottom-left) to preview all three; the choice persists.
---
## Iconography
- **System:** [**Lucide**](https://lucide.dev) — thin, consistent stroke icons — is the
ChicoryTV icon set. This is a deliberate substitution for MudBlazor's Material Symbols in the
source; Lucide's lighter 2px stroke suits the dark, technical aesthetic far better. Loaded
from CDN (`unpkg.com/lucide`) in cards and the UI kit.
- **Sizes:** 1516px inside rows/buttons, 1314px in dense tables/badges, 1718px in the top
bar. Stroke width stays at Lucide's default (~2). Icons inherit `currentColor` and sit at
`--text-secondary` until hover/active (then `--text-primary` or accent).
- **Common glyphs** (mapped from ErsatzTV actions): `Tv`, `CalendarClock`, `ListVideo`,
`Library`, `FolderTree`, `Settings`, `LayoutDashboard`, `Play`, `Pencil`, `Trash2`,
`RefreshCw`, `Stethoscope`/`Info` (troubleshoot), `Plus`, `Hash`, `Search`, `Radio` (on air),
`Film`, `Sparkles` (filler), `HardDrive`/`Server` (media source).
- **No emoji, no unicode-glyph icons** in the UI. The only unicode marks used are small
affordance triangles on the timeline playhead label.
- **Brand mark:** the chicory flower is an SVG (`assets/chicory-mark.svg`) — 16 radiating
periwinkle petals around a dark center. Do not redraw it; reference the file.
---
## File index
**Foundations**
- `styles.css` — root entry point (only `@import`s). Consumers link this.
- `tokens/fonts.css` — Geist + Geist Mono (Google Fonts).
- `tokens/colors.css` — surfaces, accent, status, text (base + semantic aliases).
- `tokens/typography.css` — families, scale, weights, tracking, tabular-num helper.
- `tokens/spacing.css` — 4px scale + fixed chrome sizes.
- `tokens/radius-elevation-motion.css` — radii, shadows, easings, durations.
- `guidelines/*.card.html` — foundation specimen cards (Colors, Type, Spacing, Brand).
**Assets** (`assets/`)
- `chicory-mark.svg` — flower mark. `chicorytv-wordmark.svg` — mark + wordmark. `chicorytv-icon.svg` — app icon.
**Components** (`components/`) — React primitives, `window.ChicoryTVDesignSystem_eb3b61`
- `forms/` — Button, IconButton, Input, Select, Switch, Checkbox.
- `data-display/` — Badge, StatusDot, Tag, ChannelLogo, Card, Stat, ProgressBar.
- `feedback/` — Toast, Tooltip, Spinner.
- `navigation/` — NavItem + NavSection, Tabs.
- Each folder has a `*.card.html` specimen and each component a `.d.ts` + `.prompt.md`.
**Admin template** (`templates/chicorytv-admin/`) — the reusable admin shell; the single
source of truth for the full app (consuming projects start from it, and it doubles as the
interactive specimen). Open `ChicoryTvAdmin.dc.html` (or `app.html` directly).
- Screens: **Dashboard** (on-air cards, stat tiles, health panel, activity, release notes),
**Channels** (bulk-select, `(D)`/`(H)` markers, inline actions), **New Channel** — the
ground-up *library-to-lineup* Channel Builder (searchable library browser + drag/double-click
to a reorderable lineup + friendly options, Channel Template picker, and an Advanced override
disclosure — replacing the old Channel→Schedule→Items→Playout editor chain), **Schedule
editor** (two-pane, drag-to-reorder), **Guide / EPG grid** (channels × time, now-marker),
plus **Playouts** and **Libraries**, and a live 3-theme switcher.
- `Shell.jsx` (nav + top bar), `*.jsx` screens, `data.js` (mock data), `ds-base.js` (loads the
compiled bundle + stylesheets). See its `README.md` for structure.
**Skill**
- `SKILL.md` — Agent-Skills-compatible entry point for reuse in Claude Code.