Skip to main content

videorc-design

Videorc's UI design language — a native macOS desktop app on real window glass (vibrancy), built exclusively from shadcn/ui components and Videorc's shared primitives: flush panes, toolbars, grouped lists, glass chips, and a Raycast-style ⌘K palette. Use whenever building, styling, reviewing, or planning ANY Videorc UI (new components, screens, dialogs, lists, toolbars, badges, confirmations, empty or locked states), when the user mentions "our design", "the design skill", "glass", "native look", or asks how something should look.

Source facts

Repository
TheOrcDev/videorc
Last source activity
October 13, 2026 at 10:18
Detected SKILL.md language
English
Stars
509
Forks
71

Install options

The review-first prompt is selected by default. You can switch to a direct command or download a local copy.

Review the source files

Read SKILL.md and any companion files shown by SkillsMP before deciding whether to install.

Showing SKILL.md

SKILL.md
Source instructions · Read-only preview
name
videorc-design
description
Videorc's UI design language — a native macOS desktop app on real window glass (vibrancy), built exclusively from shadcn/ui components and Videorc's shared primitives: flush panes, toolbars, grouped lists, glass chips, and a Raycast-style ⌘K palette. Use whenever building, styling, reviewing, or planning ANY Videorc UI (new components, screens, dialogs, lists, toolbars, badges, confirmations, empty or locked states), when the user mentions "our design", "the design skill", "glass", "native look", or asks how something should look.
# Videorc Design Language (v2: the window family) The single source of truth for how Videorc looks and feels. Every UI task follows this skill. Videorc is a desktop app, not a web page: every window sits on the real macOS window material, panes are flush and split by hairlines, controls are desktop-sized, and colour is information. The reference is the Videorc logo: a glossy black-glass orb with chrome detail and one LED-red accent. Light mode is the porcelain twin of the same structure. The words on these surfaces are not in this file. How to write them is [`docs/ui-copy.md`](../../../docs/ui-copy.md) (voice, casing, ellipsis, toasts, errors, disabled and locked copy), and the one name for each thing is the glossary in [`CONTEXT.md`](../../../CONTEXT.md). **Status: shipped in plan 050 (2026-09-23).** `styles.css` implements the tokens below. `src/main/window-glass.ts` gives every window (main, Stream Manager, Captions, Notes, Preview) its material and title bar. The Preview frame's data-URL document mirrors the coats through `src/main/window-palette.ts`, and a parity test fails when they drift. Fix tokens and primitives, never restyle a screen ad hoc. Plan 174 (the UI/UX pass, 2026-10) gave every job one shared primitive ([Primitives](#primitives)) and added the guards that hold them. ## Hard rules 1. **shadcn/ui only.** Every element is a shadcn/ui component or a composition of them, installed and customized per the `shadcn` skill. No other component libraries and no hand-rolled widgets when a primitive exists. audiocn (the owner's shadcn audio registry, `@audiocn`) counts as shadcn: its meters draw audio, fed by Videorc adapters, never by its Web Audio hooks or blocks. Base UI enters only inside its files (`docs/audiocn.md`). 2. **Real glass, never fake glass.** The OS draws the blur. No CSS `backdrop-filter` / `backdrop-blur` anywhere in the renderer: it wedged the compositor in June, and on the vibrancy windows it never reaches the screen (Chromium's own capture shows the blur, the display does not; plan 072). A guard test fails on it. No wallpaper underlays, no inline `color-scheme` on the root. 3. **Dark first, light supported.** Dark is the reference. Light is the same structure with the light token column, never a separately designed theme. 4. **Colour is information.** Chrome is monochrome. Saturated colour appears only in source/platform icons, status dots, and emphasis chips: red for record, live, and destructive; green for healthy; amber for attention. Never tint panels, rows, or text decoratively, and never colour a status word: the tone rides on an icon, a dot, a chip or an Alert. 5. **Keyboard-first, quietly.** Primary actions show their shortcut as a key chip. Global hints live in the status bar, not on every surface. 6. **One primitive per job.** Confirmations, locks, disabled reasons, shortcuts, device pickers, status words, numbers and times each have one shared primitive or helper ([Primitives](#primitives)). Use it. If it lacks something, extend it minimally; never hand-roll a competing one. ## Owner calls Binding. Follow them as written; a change that would undo one is a question for the owner, not a design choice. - **The toolbar holds only the page title** (2026-09-23). Record and Go Live… sit at the top of the Studio inspector, and a page's own actions sit in its body. - **Buddy stays second in the sidebar, directly under Studio, on ⌘9** (2026-10-10, plan 174 D6 and D8). It keeps Clean cut and every other feature, because the page sells Premium. Never move it or a feature out of it. - **A click on a live chat row puts the message on stream**, and a click on the live row takes it off (2026-10-10, D10; plans 055 and 151). There is no select-first step and no separate Show button. - **Toasts are never tinted** (2026-09-29). A toast's type colours its icon only, never the panel, its rim or its text. - **No success toast for a scene, layout or source change** (2026-07-16): the stage is the confirmation. - **The platform mark closes a chat or Activity row on the far right**, at 20 px, with YouTube's official icon (plan 165). - **The Stream Manager's live chip reads ON AIR** (2026-09-24), the broadcast convention. Everywhere else the state is "On air". - **On macOS the brand lives in About and the Dock**, never in the sidebar (2026-09-23). - **The Buddy mark is the real artwork**, never a redrawn glyph (2026-10-04; see [Icons](#icons)). ## The window family Every window uses one material and one title-bar recipe (`window-glass.ts`): - **Material.** `vibrancy: 'under-window'`, with the effect state pinned to `active` so glass stays glass when the window is inactive, stripped to **clear glass** by the native addon (plan 091): Ghostex's strip on Electron's own vibrancy view, no tint, no wallpaper tinting, no saturation boost, a 60 pt blur. What shows through is the desktop as soft colour, and the coats decide the whole tone. `VIDEORC_GLASS_STYLE=material` keeps AppKit's own grey material as the A/B control for one release; `VIDEORC_GLASS=0` paints the solid palette instead. - **Title bar.** `titleBarStyle: 'hiddenInset'`. Traffic lights are centred in the header row, which is 40 px (28 px for Preview). A header row shares its line with the lights: on macOS it pads `pl-[88px]` (`TRAFFIC_LIGHT_GUTTER_CLASS`, through `useTrafficLightGutter()` in `components/window-frame.tsx`), **in native fullscreen too**. macOS draws the lights over the header when the title bar reveals on hover, and a window filling the screen keeps them showing; dropping the gutter there put the lights on the Stream Manager title (2026-09-25). Off macOS the native frame owns the title bar and the row pads `pl-3`. Header rows are drag regions, and their controls opt out with `[-webkit-app-region:no-drag]`. - **Single-pane windows** (Stream Manager, Notes, the Captions reader) wrap their body in `WindowFrame` and lead with one `WindowHeader`: the title as the window's `h1` (14 px / 600), an optional quiet `meta` fact after it, and the window's few controls trailing. Stream Manager keeps its row to the title. - **One dark-always window.** The Preview frames video, so main pins its NSWindow appearance to `darkAqua` through the native addon, and a light main window never lightens it. If the pin is unavailable, it falls back to solid dark. Stream Manager, Captions and Notes follow the app theme like main (#412). - **Covers.** One cover per region, Ghostex's numbers (plan 091, D3): how much of the solid tone a region paints over the clear blur. The sidebar covers 88% (dark) · 93% (light), the work area 83% · 86%: the sidebar is the heavier, more covered region, the work area shows more desktop. - The coats are derived from the covers (D4), never set by hand: `body` paints the lighter cover (`--glass-window`), the sidebar `<aside>` adds its delta (`bg-glass-sidebar`), content panes add theirs (`bg-glass-content`, 0 on macOS), so each region composites to exactly its cover. - Single-pane windows wrap their body in `WindowFrame`. - Never stack a third coat. - Windows keeps plan 050's Mica composite through its own covers. - **Providers.** Each window is its own React root and wraps its tree in `<WindowProviders window="main|stream-manager|captions|notes">`: one TooltipProvider (600 ms, the desktop delay) and one Toaster, inset above that window's status bar. - **Accessibility.** `prefers-reduced-transparency` swaps the window coat for `--glass-solid` and drops the region coats (the addon leaves AppKit's solid material whole). `prefers-contrast: more` puts both covers at 95% and strengthens hairlines, chip rims, and the secondary and tertiary text tiers. - **Proof.** `pnpm probe:ui-glass --gate` measures every window over stand-in backdrops: transmission (the glass is real), sharpness (nothing behind is legible), contrast, pinned-dark luminance, the native effect view state, and since plan 091 neutrality (the stripped material passes the backdrop colour through), `nativeClear` (the strip holds) and `ghostexParity` (each region composites to its cover). It runs in `smoke:local-gates`. ## Tokens Implemented as shadcn CSS variables in `apps/desktop/src/renderer/src/styles.css` (oklch; that file is the live source). Values below are dark · light. Coats and surfaces - Base `--glass-base`: black `0.16 0.004 286` (`#0D0D0F`) · porcelain `0.985 0.001 286` (`#FAFAFB`), the OKLCH components every coat is cut from. Solid `--glass-solid` is that base, opaque. - Covers `--glass-cover-sidebar` / `--glass-cover-work`: 0.88 / 0.83 · 0.93 / 0.86 (Windows 0.34 / 0.5116 · 0.5 / 0.62). The coats `--glass-window`, `--glass-sidebar` and `--glass-content` are derived from them in `styles.css`. - Floating glass, three flat opaque tiers of the solid (plan 091, D6, Ghostex's web-modal recipe), each a 1 px rim (white 10% · black 10%) and a drop shadow, no sheen and no highlight: - popup `--glass-float` (menus, selects, popovers, hover cards, chart tooltips, toasts): black `#222224` (the solid plus 6% then 3% white, `color-mix` in sRGB) · porcelain `oklch(0.99 0 0)`; - dialog `--glass-float-dialog` (dialogs, the palette, the error panel): black `#1C1C1D` · porcelain; - tooltip `--glass-float-tooltip`: the black solid · porcelain. Opaque: without a frost, even 3% translucency lets white text under a menu read through. Deliberately darker than the glass over a bright desktop: `probe:ui-glass --surfaces` checks each tier reads its own token whatever is behind it. - Inset `bg-inset`: foreground 3%, for a well inside a section (a preview frame, a code line). Never a card. Text (three tiers, nothing else) - Primary `text-foreground`: titles and labels, weight 500–600. - Secondary `text-muted-foreground`: inline context, metadata, placeholders, descriptions, and every fact the streamer needs. - Tertiary `text-subtle`: section labels, quiet hints, disabled. **Essential information never uses `text-subtle`** (plan 174 D12): a reason, a limit, a status, a value or a rule a streamer needs is secondary or primary. `--subtle` passes AA on the solid (0.62 dark · 0.54 light) and rises under `prefers-contrast: more`, but over a bright desktop it is still the faintest tier. - Sizes are tokens: `text-label` 11 px / 600 (section labels, `SectionLabel`), `text-section` 13 px / 600 (section headers), `text-meta` 12 px (descriptions and metadata). **No text below 11 px** (a guard fails on `text-[9px]` and `text-[10px]`). Hairlines and selection - Hairlines `border-border`: white 10% · black 12%, always 1 px. - Hover/selected row `bg-accent`: white 8% · black 6%, a full-row block with no outline. Pressed `bg-accent-pressed`: 12% · 10%. - Focus-visible: a 2–3 px ring, keyboard only. Colour - `--live` / `--destructive`: the logo's LED red. Record, on air, failed, destructive. Never chrome. - `--success`: healthy or connected. `--warning`: needs attention. `--info`: rare. - `--member`: a muted violet for paid membership (YouTube member, Twitch, Kick or X subscriber). It paints only the member row tint in chat (plan 154); never a chip tone, never text. - `--reward`: a muted teal for Twitch channel point rewards. It paints only the reward row tint in Activity (`bg-reward/8`, plan 177); never a chip tone, never text. - Use them through the tone utilities (`tone-success`, `tone-warning`, `tone-destructive`, `tone-live`, `tone-neutral`) and the chip utilities. Never as text colour on status copy: the status colour guard fails on a coloured status word. ## Desktop scale Everything except ⌘K uses the desktop scale. - **Layout.** There is no page column: content fills its pane edge to edge with a 16 px gutter. Panes are flush and split by 1 px hairlines. Inside a window there are no floating panels, no shadows, and no big cards. - **Toolbar** (`Toolbar`, 40 px): the page title (14 px / 600, the window's `h1`) on the window's drag band, and nothing else (owner call, 2026-09-23). Actions live with the content they act on. The Studio's Record, Go Live… and the stop button ("Stop recording", or "End livestream" while on air) and its clock sit at the top of the inspector, right above Session; a page's own actions sit in its body (a filter bar, a section header, or the PageHeader row). The toolbar never scrolls; only `PaneBody` does. - **Sidebar** (plan 174 D8). - The top row holds the traffic lights and the ⌘K button. - Studio leads, with a live dot and the clock while a recording or livestream runs. Buddy sits directly under it on ⌘9 (owner call). - "Setup" labels the pages you configure between sessions: Sources ⌘2, Scene ⌘3, Assets ⌘4, Livestream ⌘5, Captions ⌘6, Output ⌘7. Library ⌘8 follows under no label: a group of one row carries none. - Settings (⌘,) sits in a bottom block above the account row. Health has no row: the status bar's state is its door (D9). - Section labels are `SectionLabel` (11 px / 600, tertiary). Rows are 28 px, with 14 px text and 16 px icons. - On Windows the native title bar carries the app's name, so the sidebar shows no mark (D18). - **Status bar** (`StatusBar`, 26 px in the main window): the session state on the left, as a button that opens Health ("● On air 12:04", `StatusBarSessionState`). On the right, quiet 11 px shortcut hints (`StatusBarHint`), each still clickable and pressed while its window is open: ⌘K Search, ⌘P Preview, ⇧⌘N Notes, ⇧⌘J Stream Manager. Every clickable thing in a status bar is a `StatusBarHint` or a `StatusBarAction` (20 px, 11 px tertiary text that brightens on hover). - **Sections** (`PanelSection`): flush. A 13 px / 600 header (an `h2`; a nested section passes `headingLevel={3}`), a 12 px secondary description, a hairline between sections, and 16 px padding. No border, background, radius, or shadow. - **Pages with tabs** (Settings, plan 064; Buddy, plan 150; Livestream, plan 174): `PageTabs`, one segmented `TabsList` in a `PaneStrip` pinned under the toolbar (`border-b px-gutter py-2`). Only the region under it scrolls: the page spreads `PANE_OWNS_SCROLL`, so `PaneBody` stops scrolling for it. The last tab used is remembered on this device, deep links open a named tab (`openWorkspaceTab(tab, subTab?)`), and ⌘K lists every tab. A filter bar pinned under the toolbar is a `PaneStrip` too. - Each tab answers one question. - A tab body is a `ConfigGrid` of two sections with `CONFIG_GRID_PAIR`, or a `PageStack` for one. - Settings rows are `SettingsRow` (`label`, `description`, `error`, the control as its child) in a `FieldGroup variant="grouped"`: label and 12 px description on the left, the control on the right. - **Locked means disabled, with one reason.** One `LockAlert` (or `SessionLockAlert` while recording or live, `OfflineAlert` while Videorc reconnects) with its one action leads the tab, and every control under it is disabled. Never put an enabled-looking switch or button beside "isn't available". A Premium feature stays on screen behind its lock, never hidden: the locked page is what sells it. - Rules a streamer needs (limits, Premium versus free) go in the section's description, never in `text-subtle` footnotes. - **Lists, not card stacks.** Sets of like things (destinations, sources, devices, settings) are one `GroupedList` of `ListRow`s: 32 px rows (28 px compact) with hairline separators. A row is a fact unless it says otherwise: `interactive` adds the hover, `as="button"` makes the whole row one button, and a row that holds its own controls stretches a `ListRowButton` over itself. A fact row that jumps to the page owning its value is a `NavigableRow` (icon, label, value, chevron). Cards remain only for objects with a picture (scene thumbnails, takeovers, Buddies, library items): `MediaCard`, 8 px radius, white 4% fill, a 1 px hairline, no shadow; `selected` is a brighter rim, never a colour. Sources (plan 173) is the reference for rows that carry controls: one `SourceItem` per source (`components/sources/source-item.tsx`), a header row (icon, title, one status chip, one quick control, More), a body indented to the title, and the rare settings folded into More. - **Overflow menus.** Low-frequency per-item actions go in one `KebabMenu` (⋯): items grouped with a hairline between groups, destructive items last, each item's keys right-aligned, a spinner while an item's work runs. `reveal="hover"` hides the trigger until its row (`group/kebab-host`) is hovered or focused. - **Radii.** Tiers only, never ad-hoc radius values per screen: - `rounded-panel`: 12 px. Containers and dialogs. - `rounded-row`: 8 px. Rows and cards. - `rounded-chip`: 6 px. Controls and key chips. - `rounded-tag`: 7 px. Tags. Status chips are round. - Floating surfaces add the popup tier (`rounded-lg`, 10 px; see Floating surfaces). Nothing else: no `rounded-sm`/`md`/`xl` or `rounded-[Npx]` in chrome (a guard checks). - Window corners belong to the OS. - **Controls.** The default height is 28 px for Button, Toggle, Select trigger, and Input. Retune it once in `components/ui`, never per screen: no `h-*` or `size-*` on a control at its call site (a guard checks). - **One compact size** (plan 174 S4.1, J1-11): `xs`, 24 px. Button `xs` and `icon-xs`, Toggle and ToggleGroup `xs`, SelectTrigger `xs` (SegmentedControl `sm` is the same 24 px). Use it only where the control sits inside something denser than a page: an Alert's action; a row's trailing or hover actions (`ListRow`, `SourceItem`, chat, activity,
View on GitHub
This SKILL.md is very large, so SkillsMP previews the first section here. View on GitHub