191 lines
12 KiB
Markdown
191 lines
12 KiB
Markdown
# 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 `16–24px`;
|
||
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:* 40–50% 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:** 15–16px inside rows/buttons, 13–14px in dense tables/badges, 17–18px 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.
|