From 60eda8f39ebb80694cb9d4aacad484258fcd94d9 Mon Sep 17 00:00:00 2001 From: Rogee Date: Fri, 28 Aug 2026 17:51:21 +0800 Subject: [PATCH] HH-787: add CreatorHub design system specification (#13) --- design.md | 378 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 378 insertions(+) create mode 100644 design.md diff --git a/design.md b/design.md new file mode 100644 index 0000000..3d1cae5 --- /dev/null +++ b/design.md @@ -0,0 +1,378 @@ +--- +version: alpha +name: CreatorHub Operations Console +description: Enterprise UI and interaction source of truth for CreatorHub. +colors: + primary: "#0866EF" + primary-hover: "#0759D4" + on-primary: "#FFFFFF" + secondary: "#061A38" + secondary-hover: "#0B2A54" + on-secondary: "#FFFFFF" + on-secondary-muted: "#A9B7CC" + surface-page: "#FFFFFF" + surface-raised: "#FFFFFF" + surface-subtle: "#F8FAFC" + text-primary: "#111827" + text-secondary: "#475467" + text-tertiary: "#667085" + divider: "#D7DDE7" + success: "#067647" + success-surface: "#ECFDF3" + warning: "#B54708" + warning-surface: "#FFFAEB" + error: "#B42318" + error-surface: "#FEF3F2" +typography: + page-title: + fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" + fontSize: 2.25rem + fontWeight: 760 + lineHeight: 1.15 + letterSpacing: -0.035em + section-title: + fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" + fontSize: 1.25rem + fontWeight: 700 + lineHeight: 1.3 + letterSpacing: -0.01em + body-lg: + fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" + fontSize: 1.05rem + fontWeight: 400 + lineHeight: 1.55 + body-md: + fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" + fontSize: 1rem + fontWeight: 400 + lineHeight: 1.5 + label: + fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" + fontSize: 0.875rem + fontWeight: 650 + lineHeight: 1.4 + caption: + fontFamily: "Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Segoe UI, sans-serif" + fontSize: 0.75rem + fontWeight: 400 + lineHeight: 1.4 + mono: + fontFamily: "ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, Liberation Mono, monospace" + fontSize: 0.875rem + fontWeight: 400 + lineHeight: 1.5 +spacing: + xxs: 4px + xs: 8px + sm: 12px + md: 16px + lg: 24px + xl: 32px + page-desktop: 36px + section: 48px + control-height: 40px + primary-control-height: 56px + appbar-height: 70px + drawer-collapsed: 76px + drawer-expanded: 240px + content-max-width: 1280px +rounded: + none: 0px + subtle: 4px + control: 7px + panel: 8px + full: 9999px +components: + page: + backgroundColor: "{colors.surface-page}" + textColor: "{colors.text-primary}" + typography: "{typography.body-md}" + panel: + backgroundColor: "{colors.surface-raised}" + textColor: "{colors.text-primary}" + rounded: "{rounded.panel}" + padding: "{spacing.lg}" + panel-compact: + backgroundColor: "{colors.surface-raised}" + textColor: "{colors.text-primary}" + rounded: "{rounded.panel}" + padding: "{spacing.md}" + table-header: + backgroundColor: "{colors.surface-subtle}" + textColor: "{colors.text-secondary}" + typography: "{typography.label}" + page-title: + textColor: "{colors.text-primary}" + typography: "{typography.page-title}" + section-heading: + textColor: "{colors.text-primary}" + typography: "{typography.section-title}" + intro-copy: + textColor: "{colors.text-secondary}" + typography: "{typography.body-lg}" + metadata: + textColor: "{colors.text-tertiary}" + typography: "{typography.caption}" + code-value: + textColor: "{colors.text-primary}" + typography: "{typography.mono}" + button-primary: + backgroundColor: "{colors.primary}" + textColor: "{colors.on-primary}" + typography: "{typography.label}" + rounded: "{rounded.control}" + height: "{spacing.control-height}" + padding: 0 16px + button-primary-hover: + backgroundColor: "{colors.primary-hover}" + textColor: "{colors.on-primary}" + button-secondary: + backgroundColor: "{colors.surface-raised}" + textColor: "{colors.primary}" + typography: "{typography.label}" + rounded: "{rounded.control}" + height: "{spacing.control-height}" + padding: 0 16px + button-danger: + backgroundColor: "{colors.error}" + textColor: "{colors.on-primary}" + typography: "{typography.label}" + rounded: "{rounded.control}" + height: "{spacing.control-height}" + padding: 0 16px + icon-button: + backgroundColor: "{colors.surface-raised}" + textColor: "{colors.text-primary}" + rounded: "{rounded.control}" + size: "{spacing.control-height}" + input-field: + backgroundColor: "{colors.surface-raised}" + textColor: "{colors.text-primary}" + typography: "{typography.body-md}" + rounded: "{rounded.control}" + height: "{spacing.primary-control-height}" + padding: 0 14px + navigation-item: + backgroundColor: "{colors.secondary}" + textColor: "{colors.on-secondary-muted}" + typography: "{typography.body-md}" + rounded: "{rounded.panel}" + height: 52px + padding: 0 16px + navigation-item-hover: + backgroundColor: "{colors.secondary-hover}" + textColor: "{colors.on-secondary}" + navigation-item-active: + backgroundColor: "{colors.primary}" + textColor: "{colors.on-primary}" + status-success: + backgroundColor: "{colors.success-surface}" + textColor: "{colors.success}" + typography: "{typography.label}" + rounded: "{rounded.full}" + padding: 4px 8px + status-warning: + backgroundColor: "{colors.warning-surface}" + textColor: "{colors.warning}" + typography: "{typography.label}" + rounded: "{rounded.full}" + padding: 4px 8px + status-error: + backgroundColor: "{colors.error-surface}" + textColor: "{colors.error}" + typography: "{typography.label}" + rounded: "{rounded.full}" + padding: 4px 8px + separator: + backgroundColor: "{colors.divider}" + size: 1px +--- + +## Overview + +CreatorHub should feel like an **audit-ready operations console**, not a consumer social dashboard: the calm, stable shift board in a control room where operators can see current state, take one deliberate action, and verify its outcome without visual noise. + +The audience is an internal operator managing authorized accounts, isolated browser environments, network exits, confirmations, tasks, and audit evidence. The interface must communicate control, traceability, and safe stopping before speed or novelty. + +Three principles decide ambiguous designs: + +1. **State before decoration.** Current state, ownership, last evidence, and safe next action receive the strongest hierarchy. +2. **Explicit domain actions.** Start, stop, recycle, confirm, pause, and cancel remain named actions; never disguise them as generic CRUD. +3. **Quiet density.** Use type, spacing, borders, and alignment for hierarchy. Color is reserved for navigation, primary action, status, focus, and errors. + +`design.md` is the normative design contract for future pages. Existing implementation differences are migration work for separate issues; do not silently fork this system inside page code. + +## Colors + +The palette keeps the existing CreatorHub blue and navy anchors while replacing ad hoc page colors with semantic roles. + +- **Primary** `{colors.primary}` is the single interaction accent. Use it for the main action, active navigation, links, and focus—not for decoration. +- **Secondary** `{colors.secondary}` is the stable navigation shell. `{colors.secondary-hover}` is its only hover layer. +- **Surfaces** remain white. `{colors.surface-subtle}` is limited to table headers, grouped metadata, and quiet empty-state regions; it must not become a field of nested gray cards. +- **Text** uses `{colors.text-primary}` for decisions and data, `{colors.text-secondary}` for explanation, and `{colors.text-tertiary}` for metadata. Do not lower opacity to invent more tiers. +- **Semantic colors** mean state only. Success, warning, and error always pair color with a label or icon and never stand alone as the sole signal. +- **Dividers** `{colors.divider}` create structure. Regular panels use a 1px divider rather than a shadow. + +Do not introduce gradients, glow, glass surfaces, decorative color blocks, or page-specific hex values. A new color requires a reusable semantic role and contrast verification before it enters this file and `web/src/theme.js`. + +## Typography + +Use the existing Inter-first system stack. Do not download a web font for a control console; system fallbacks preserve speed and Chinese coverage. + +- `page-title` is reserved for one page-level `h1`. +- `section-title` labels major work regions, not every card. +- `body-lg` is limited to the page summary or a high-value empty-state explanation. +- `body-md` is the default reading and data size. +- `label` is used for controls, field labels, table headers, and status labels. +- `caption` is supporting metadata; it must still pass normal-text contrast. +- `mono` is for identifiers, endpoints, profile names, hashes, and machine values only. + +Use sentence case in Chinese and English. Do not use all caps, decorative display type, or more than three weights in one view. Numeric identifiers must not be made smaller merely to fit; wrap them or expose a copy action. + +## Layout + +The shell reuses the current application geometry: + +- fixed app bar: `{spacing.appbar-height}`; +- permanent desktop drawer: `{spacing.drawer-expanded}` expanded and `{spacing.drawer-collapsed}` collapsed; +- content width: at most `{spacing.content-max-width}`, left-aligned in the main work area; +- page padding: 16px on `xs`, 24px from `sm`, and `{spacing.page-desktop}` from `lg`; +- section separation: 24–48px; panel padding: 16px compact or 24px standard. + +Every page follows the same anatomy: page title and one-sentence purpose, page-level primary action when present, filters or creation controls, results, then supporting evidence. Place the action near the object it changes; avoid a detached toolbar full of unrelated buttons. + +Use MUI's existing breakpoints: `xs` below 600px, `sm` from 600px, `md` from 900px, and `lg` from 1200px. The application must remain usable without horizontal page scrolling at 320px. + +## Elevation & Depth + +CreatorHub is border-first and nearly flat. Regular panels, tables, forms, and cards use a 1px divider and no shadow. Hierarchy comes from containment, whitespace, and subtle surface changes. + +Only transient overlays—menus, dialogs, popovers, and tooltips—may use MUI's default elevation. Do not stack raised cards inside raised cards, invent page-level z-index scales, or use shadows as focus indicators. + +## Shapes + +Controls use the 7px `control` radius; panels and navigation items use the 8px `panel` radius. The 4px `subtle` radius is reserved for small inline regions. Full pills are limited to compact statuses and tags. + +Do not use oversized rounded cards, capsule buttons for ordinary actions, mixed corner systems, or decorative circles. Icons come from the installed MUI icon set and use the same stroke character within an action group. + +## Components + +### Shared state model + +Interactive components must define, as applicable: `default → hover → focus-visible → active → disabled`, plus `busy`, `error`, `empty`, and `success` at the workflow level. Hover never substitutes for focus. Disabled controls explain the prerequisite in nearby text or a tooltip when the reason is not obvious. + +### Buttons and domain actions + +- Allow one contained primary button per work region. Secondary actions are outlined or text buttons. +- Use verb-first labels: “创建环境”, “启动”, “停止”, “回收”. Avoid “确定”, “处理”, and icon-only domain actions. +- Destructive or high-impact actions use the error color and a confirmation that names the target and consequence. Default focus stays on cancel. +- Busy labels describe progress (“创建中…”, “停止中…”), disable repeat submission, and preserve layout width. +- Disable only the affected row or object unless the operation invalidates the whole page. + +### Inputs and forms + +- Every input has a persistent visible label. Placeholder text is an example, never the label. +- Required fields use both native `required` semantics and a visible marker; validation runs on blur or submit, not on each keystroke unless the value can be checked without noise. +- Error text sits next to the field, states how to recover, and remains until corrected. Preserve valid input after request failure. +- Group no more than one primary submission action per form. Put advanced or risky fields behind explicit disclosure only when they actually exist. + +### Navigation + +- The active item uses primary blue, white text, and the router's current-location semantics. +- Collapsed navigation retains accessible names and tooltips; mobile navigation closes after a route selection and returns focus to the opener when dismissed. +- Disabled destinations must look unavailable and remain non-interactive. Do not present speculative routes as working navigation. + +### Tables and responsive cards + +- Desktop tables are for comparison across rows. Keep stable column order, left-align text, right-align numeric measures, and place actions last. +- At widths below `md`, replace wide tables with cards that preserve the same data, status, action availability, and reading order. +- Empty results show what is empty and the safe next action. Loading preserves the content region; errors use an alert with a retry action. +- Long identifiers wrap safely or truncate with an accessible full-value affordance. Never shrink below the type tokens to force fit. + +### Status and feedback + +- Status uses a localized label plus color and, when space permits, an icon or dot. Unknown backend states display a safe “未知状态” label and the raw value as secondary evidence. +- Success is shown by the changed object state; use a toast only when the result would otherwise be invisible. +- Page or action errors use a persistent alert near the affected work region. Do not expose secrets, raw stack traces, or unactionable transport errors. +- Polling updates data without stealing focus, resetting forms, moving the viewport, or repeatedly announcing unchanged content. + +### Dialogs, menus, and tooltips + +- Use a dialog only for a decision that cannot safely occur inline. The title names the action and target; the body states the consequence. +- Escape and the close control dismiss a dialog before submission. After completion or cancellation, return focus to the trigger. +- Tooltips supplement icon-only controls and unfamiliar terms; they never carry required instructions or validation errors. + +## Do's and Don'ts + +- **Do** make the current state and next safe action obvious before adding density. +- **Do** reuse MUI and react-admin interaction patterns, theme tokens, and installed icons. +- **Do** keep high-risk lifecycle actions explicit, confirmable, idempotent where supported, and backed by visible outcome evidence. +- **Do** design loading, empty, error, permission/policy hold, and stale-data states with the default state. +- **Do** keep filters, sort, and pagination stable when data refreshes. +- **Don't** add a hero, KPI vanity cards, illustrations, gradients, glassmorphism, or animation merely to make an internal tool feel “modern”. +- **Don't** mix Ant Design, Arco, Mantis code, or another component language into MUI. +- **Don't** encode status only with color, use disabled text as ordinary metadata, or hide critical actions behind hover. +- **Don't** auto-retry `needs_confirmation`, `policy_hold`, unknown results, platform warnings, authentication failures, or destructive actions. +- **Don't** show credentials, cookies, tokens, proxy secrets, or cross-account data in UI copy, logs, screenshots, errors, or exports. + +## Naming + +- Token names are lowercase kebab-case and semantic: `text-secondary`, not `gray-600`; `status-error`, not `red-pill`. +- Component variants use `--`, for example `button-primary-hover`. Do not name tokens after a single page. +- React components use PascalCase, props and handlers use camelCase, and domain actions use exact verbs (`start`, `stop`, `recycle`, `confirm`, `pause`, `cancel`). +- Backend state keys stay stable lowercase English; user-facing labels are localized Chinese. Never use the display label as the program state. +- A user-visible resource has one canonical noun across navigation, title, table, form, dialog, error, and audit text. For the current runtime resource, use “运行环境”; use “Profile” only for the isolated profile artifact. +- Accessibility names describe action plus target (“停止 account-a”), not icon shape (“方形按钮”) or position (“右侧按钮”). + +## Interaction + +Interaction is quick and mechanical, like operating a reliable control panel: + +| Token | Value | Use | +|---|---:|---| +| `motion.feedback` | 120ms | hover, press, focus, toggle | +| `motion.exit` | 160ms | dismissing menus and dialogs | +| `motion.layout` | 200ms | drawer and bounded layout transitions | +| `motion.easing` | `cubic-bezier(0.4, 0, 0.2, 1)` | all transitions | + +Nothing bounces, overshoots, pulses indefinitely, or animates longer than 200ms. Under `prefers-reduced-motion: reduce`, durations collapse to effectively zero while state changes remain perceivable. + +Actions update in this order: acknowledge the trigger, prevent duplicate submission, perform the request, expose the resulting state or actionable error, then restore focus. Never use optimistic UI for destructive actions or results whose server outcome may be unknown. + +## Responsive Behavior + +- **320–599px (`xs`):** temporary drawer; one-column forms; cards replace data tables; actions wrap without reordering; 16px page padding. +- **600–899px (`sm`):** 24px page padding; forms may use two columns only when labels and errors still fit. +- **900–1199px (`md`):** permanent drawer; comparison tables; two-column creation forms; no horizontal page overflow. +- **1200px+ (`lg`):** 36px page padding; up to three form columns; content stops growing at 1280px. + +Responsive variants must preserve content and action parity. A mobile card may rearrange a row, but it may not omit state, evidence, errors, or a permitted action. Pointer and keyboard behavior must both work at every breakpoint. + +## Accessibility + +WCAG 2.2 AA is the minimum release boundary. + +- Normal text requires 4.5:1 contrast; large text and non-text controls require 3:1. Disabled controls are exempt from text contrast but must remain distinguishable. +- All workflows operate by keyboard in a logical order with no trap. Use native controls first and preserve visible focus. +- Focus uses a 2px high-contrast ring with a 2px offset. On primary or navy surfaces, switch to a white ring when blue cannot reach 3:1. +- Pointer targets are at least 24×24px with sufficient separation; prefer 44×44px for primary, mobile, and destructive actions. A 40px dense control is allowed only when adjacent targets remain separated and keyboard access is equivalent. +- Page landmarks, one `h1`, ordered headings, table headers, field labels, descriptions, and error associations are semantic—not simulated with styling. +- Loading indicators and live results have concise accessible names. Announce completion and errors once; routine polling must not create repeated live-region output. +- Icons never replace text for domain actions. Decorative icons are hidden from assistive technology; icon-only shell controls require explicit accessible names and tooltips. +- Zoom to 200% and text spacing overrides must not clip content, hide actions, or force two-dimensional scrolling outside genuine data tables. +- Do not communicate state, required fields, or errors by color alone. + +## Implementation Boundary and Review Checklist + +CreatorHub owns its MUI theme, Layout/AppBar/Menu, and react-admin data-provider seam. Future UI work must update those existing seams instead of building a parallel component system. Mantis Free may inform visual judgment under its license, but its template code is not imported or forked. Do not add a token runtime, generator, Storybook, or new UI dependency until a concrete implementation issue proves the existing MUI theme insufficient. + +Before a page is accepted, verify: + +1. tokens and terminology come from this file or an explicitly reviewed extension; +2. default, hover, focus-visible, disabled, busy, loading, empty, error, and success states are covered where applicable; +3. the page works at 320px, 600px, 900px, and 1200px without losing content or actions; +4. keyboard order, focus return, accessible names, contrast, reduced motion, and 200% zoom pass; +5. domain actions retain their explicit semantics and failure/unknown outcomes never auto-retry; +6. focused interaction tests cover any changed theme, layout, navigation, resource action, or data-provider behavior. + +Format and philosophy reference: [google-labs-code/design.md](https://github.com/google-labs-code/design.md) at `9bf8eae67128b6cc55ad9bf86665767deb4c11cd` (Apache-2.0), accessed 2026-08-28. This file adopts the structured-token-plus-rationale approach; CreatorHub's values, rules, and prose are project-specific.