--- name: webui-design description: Design system for the AI Edit Video web dashboard (noti.vn) - the three-step type scale, light/dark color tokens, the component primitives every page must use, Inter font, SVG icons, Shopify Admin style layout. MUST read before writing or modifying any UI in apps/web. --- # Web UI Design System - AI Edit Video by noti.vn The web UI is a **monitoring and management dashboard**, not a video editor. Aesthetic standard: Shopify Admin - minimal, high information density but still airy, every element has a reason to exist. **Enforcement:** `cd apps/web && node scripts/check-design-system.mjs`. It flags off-scale type sizes, off-rhythm spacing, raw colors, and hand-rolled copies of existing primitives. Run it before you claim a UI change is done. ## 1. The type scale - three steps, no fourth This is the single rule that decides whether the dashboard reads clean or noisy, so it comes first. | Class | Size | Role | |---|---|---| | `text-sm` | 14px | **Content.** The default. Descriptions, values, field labels, item names in a list, messages. When in doubt, this one. | | `text-meta` | 13px | **Metadata.** Timestamps, ids, counts, the hint under a field, file paths. | | `text-xs` | 12px | **Chrome detail.** Only inside badges/chips, table ``, and `.t-eyebrow`. Never for a sentence. | Plus exactly two headings - `text-xl font-semibold` (20px) for the page title, `text-sm font-semibold` (14px) for a card title - and `text-[28px] font-bold` for the big figure inside ``. Never more than 2 heading levels on one page. **Banned:** `text-[10px]`, `text-[11px]`, `text-[13px]`, `text-[15px]`, `text-base`, `text-lg`, and every other arbitrary size. Need 13px? That is what `text-meta` is for - a hand-typed arbitrary size is exactly how a fourth step sneaks back in. Below 12px there is no step at all: either the text matters enough to be readable, or it should not be on screen. `text-meta` is defined in `globals.css` via `@theme { --text-meta }`, so it is a real Tailwind utility, not a convention. **Why this is written down so forcefully:** before the August 2026 overhaul the codebase had **416 uses of `text-xs` against 136 of `text-sm`**. 12px had silently become the body size, with a long tail of 10/11/13/15px underneath. Everything looked the same, so nothing read as more important than anything else. The spec said 14px body the whole time - what was missing was a name for 13px and something that counted. ## 2. Spacing rhythm Gaps and padding come from one scale: `gap-1` (4px), `gap-2` (8px), `gap-3` (12px), `gap-4` (16px), `gap-6` (24px); `p-2`, `p-3`, `p-4`. **Banned:** `gap-0.5`, `gap-2.5`, `gap-5`, `p-0.5`, `p-1.5`, `p-2.5`. Icon buttons do not need their own padding - use ``. Page shell: content gutter 20px (`Shell.tsx`), cards 16px inside, 16px between cards. `` deliberately carries **no bottom margin** - the page's own `gap-4` provides it. Adding `mb-4` back doubles it to 32px and detaches the title from its page. ## 3. Primitives - use them, never re-derive them Every one of these exists because the same thing had been hand-written many times over. Reaching past a primitive to raw Tailwind is the failure mode this system is built to prevent. | Need | Use | Replaced | |---|---|---| | Button | `} /> {error && } {loading ? : rows.length ? …
: }
``` **Detail page** (projects/[id], text-to-video/[id], translate-video/[id], auto-cut/[id], images/[id], styles/[id]): the 3-column `` from `components/Workspace.tsx`, in work-rhythm order **source → setup → output**, with `` first in column 3. Column widths come from a **container query**, not a media query, because the real width depends on whether the rail and panel are collapsed. Hand-rolling `xl:grid-cols-5` here is a bug, not a style choice - it ignores the rail/panel state. Destructive and save actions live in `` on every detail page. Not at the bottom of the page, not inside a card, not as a bare text button. ## 9. Standard components (CSS contracts in `globals.css`) - **Button**: 36px tall, 16px horizontal padding, radius `--radius`. `.btn-sm` is 30px/13px. There is no third size - do not override `