From 571c67526fe17e001ae3fd49959a40952414003d Mon Sep 17 00:00:00 2001 From: Xin Wang Date: Fri, 5 Jun 2026 13:48:42 +0800 Subject: [PATCH] Revise design and UI components for AI video admin interface Updated DESIGN.md to reflect a new editorial design approach with a navy palette and refined typography. Enhanced global styles in globals.css, including new color tokens and layout adjustments. Refactored components in AppShell, Sidebar, and Topbar for improved consistency and theming. Introduced a ThemeToggle component for user theme preferences and updated various pages to utilize new styles and components, ensuring a cohesive user experience across the application. --- DESIGN.md | 538 ++++++++---------- src/app/globals.css | 234 +++++--- src/app/layout.tsx | 34 +- src/components/layout/AppShell.tsx | 4 +- src/components/layout/Sidebar.tsx | 110 ++-- src/components/layout/ThemeToggle.tsx | 34 ++ src/components/layout/Topbar.tsx | 79 +-- src/components/pages/AssistantPage.tsx | 57 +- .../pages/AssistantWorkflowPage.tsx | 94 +-- src/components/pages/ComponentsPage.tsx | 12 +- src/components/pages/HistoryPage.tsx | 12 +- src/components/pages/HomePage.tsx | 93 +-- src/components/pages/PlaceholderPage.tsx | 53 ++ src/components/pages/ProfilePage.tsx | 12 +- src/components/pages/TestPage.tsx | 12 +- src/components/pages/WorkflowPage.tsx | 12 +- src/components/ui/button.tsx | 2 +- 17 files changed, 809 insertions(+), 583 deletions(-) create mode 100644 src/components/layout/ThemeToggle.tsx create mode 100644 src/components/pages/PlaceholderPage.tsx diff --git a/DESIGN.md b/DESIGN.md index 3fd6039..8087923 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -1,99 +1,129 @@ --- version: alpha name: ai-video-admin-design -description: A dark developer-console admin surface for managing AI video assistants. The base canvas is deep navy (`#080b13`) with cool off-white ink (`#e9eef7`); brand voltage comes from a cyan-to-blue gradient accent and soft radial glow blooms — not pastel orbs. Inter carries all UI type at 400–700 weights. CTAs use the shadcn primary blue pill; secondary actions are dark outlined panels on layered navy surfaces. The system reads as a focused ops dashboard, not a marketing site. +description: A quietly editorial admin surface for managing AI video assistants, rendered in a navy palette with both a dark and a light theme. The dark canvas is deep navy (`#070b16`) holding off-white ink (`#f1f5ff`); the light canvas is a cool off-white (`#f3f5fb`) holding deep-navy ink (`#0c1426`). Brand voltage is photographic, not chromatic — soft pastel atmospheric gradient orbs (mint → peach → lavender → sky → rose) drift behind hero copy as the only "color" moments. Display runs Cormorant Garamond Light at weight 300 — the editorial signature; Inter carries body, navigation, captions. CTAs are subtle pills: a deep-navy ink pill in light, inverting to an off-white pill in dark. There is no neon accent and no saturated CTA color. colors: - primary: "#2563eb" - primary-active: "#1d4ed8" - accent-cyan: "#22d3ee" - accent-blue: "#60a5fa" - accent-blue-strong: "#3b82f6" - ink: "#e9eef7" - body: "#9aa6bd" - body-sub: "#7f8aa3" - muted: "#5d6880" - hairline: "#161d2c" - hairline-soft: "#1b2233" - hairline-strong: "#2a3550" - hairline-node: "#273249" - canvas: "#080b13" - canvas-sidebar: "#0a0e17" - canvas-panel: "#0d121d" - surface-card: "#0f1521" - surface-hover: "#151e30" - surface-node: "#111827" + primary: "#1b2741" + primary-active: "#0c1426" + ink: "#0c1426" + body: "#44516c" + body-strong: "#1b2741" + muted: "#5d6b86" + muted-soft: "#94a0bd" + hairline: "#e3e7f1" + hairline-soft: "#eef1f8" + hairline-strong: "#cbd3e4" + canvas: "#f3f5fb" + canvas-soft: "#f9fafd" + surface-card: "#ffffff" + surface-strong: "#e9edf7" on-primary: "#ffffff" - on-accent-dark: "#04121a" - on-dark: "#ffffff" - gradient-cyan-start: "#22d3ee" - gradient-blue-end: "#3b82f6" - gradient-text-start: "#67e8f9" - gradient-text-end: "#60a5fa" - glow-blue: "rgba(46, 161, 255, 0.18)" - glow-cyan: "rgba(34, 211, 238, 0.16)" - stat-cyan: "#22d3ee" - stat-blue: "#60a5fa" - stat-violet: "#a78bfa" - stat-emerald: "#34d399" + gradient-mint: "#a7e5d3" + gradient-peach: "#f4c5a8" + gradient-lavender: "#c8b8e0" + gradient-sky: "#a8c8e8" + gradient-rose: "#e8b8c4" + semantic-error: "#dc2626" + semantic-success: "#16a34a" + +colors-dark: + primary: "#e8edf9" + primary-foreground: "#0c1426" + ink: "#f1f5ff" + foreground: "#e8edf9" + body: "#a6b2cb" + muted: "#93a0bb" + muted-soft: "#6c7a96" + hairline: "#1b2740" + hairline-soft: "#141d30" + hairline-strong: "#283450" + canvas: "#070b16" + canvas-soft: "#0b1322" + surface-card: "#0e1626" + surface-strong: "#18233a" + gradient-mint: "#5fae9b" + gradient-peach: "#c08a6b" + gradient-lavender: "#8a78ad" + gradient-sky: "#5f86b8" + gradient-rose: "#b07d8c" semantic-error: "#f87171" - semantic-success: "#34d399" + semantic-success: "#4ade80" typography: + display-mega: + fontFamily: "'Cormorant Garamond', 'Times New Roman', serif" + fontSize: clamp(2.5rem, 5vw, 4rem) + fontWeight: 300 + lineHeight: 1.05 + letterSpacing: -0.03em display-xl: - fontFamily: "'Inter', sans-serif" - fontSize: 36px - fontWeight: 700 - lineHeight: 1.1 - letterSpacing: -0.72px + fontFamily: "'Cormorant Garamond', serif" + fontSize: clamp(2rem, 4vw, 3rem) + fontWeight: 300 + lineHeight: 1.08 + letterSpacing: -0.02em display-lg: - fontFamily: "'Inter', sans-serif" + fontFamily: "'Cormorant Garamond', serif" + fontSize: 36px + fontWeight: 300 + lineHeight: 1.17 + letterSpacing: -0.01em + display-md: + fontFamily: "'Cormorant Garamond', serif" fontSize: 32px - fontWeight: 700 - lineHeight: 1.15 - letterSpacing: -0.64px + fontWeight: 300 + lineHeight: 1.13 + letterSpacing: -0.01em + display-sm: + fontFamily: "'Cormorant Garamond', serif" + fontSize: 24px + fontWeight: 300 + lineHeight: 1.2 + letterSpacing: 0 title-md: fontFamily: "'Inter', sans-serif" fontSize: 20px - fontWeight: 600 + fontWeight: 500 lineHeight: 1.35 letterSpacing: 0 title-sm: fontFamily: "'Inter', sans-serif" fontSize: 18px - fontWeight: 600 - lineHeight: 1.4 + fontWeight: 500 + lineHeight: 1.44 letterSpacing: 0 body-md: fontFamily: "'Inter', sans-serif" - fontSize: 15px + fontSize: 16px fontWeight: 400 - lineHeight: 1.73 - letterSpacing: 0 + lineHeight: 1.5 + letterSpacing: 0.01em body-strong: fontFamily: "'Inter', sans-serif" - fontSize: 15px - fontWeight: 600 - lineHeight: 1.73 - letterSpacing: 0 + fontSize: 16px + fontWeight: 500 + lineHeight: 1.5 + letterSpacing: 0.01em body-sm: + fontFamily: "'Inter', sans-serif" + fontSize: 15px + fontWeight: 400 + lineHeight: 1.47 + letterSpacing: 0.01em + caption: fontFamily: "'Inter', sans-serif" fontSize: 14px fontWeight: 400 lineHeight: 1.5 letterSpacing: 0 - caption: - fontFamily: "'Inter', sans-serif" - fontSize: 12px - fontWeight: 400 - lineHeight: 1.4 - letterSpacing: 0 - caption-semibold: + caption-label: fontFamily: "'Inter', sans-serif" fontSize: 12px fontWeight: 600 lineHeight: 1.4 - letterSpacing: 0 + letterSpacing: 0.08em + textTransform: uppercase button: fontFamily: "'Inter', sans-serif" fontSize: 14px @@ -106,21 +136,15 @@ typography: fontWeight: 400 lineHeight: 1.4 letterSpacing: 0 - brand-label: - fontFamily: "'Inter', sans-serif" - fontSize: 14px - fontWeight: 700 - lineHeight: 1.4 - letterSpacing: 0 rounded: none: 0px - sm: 6px - md: 8px - lg: 10px - xl: 12px - xxl: 16px - xxxl: 24px + sm: "calc(0.75rem * 0.6)" # ~7px + md: "calc(0.75rem * 0.8)" # ~10px + lg: 0.75rem # 12px (--radius) + xl: "calc(0.75rem * 1.4)" # ~17px + 2xl: "calc(0.75rem * 1.8)" # ~22px + 3xl: "calc(0.75rem * 2.2)" # ~26px pill: 9999px full: 9999px @@ -133,7 +157,7 @@ spacing: lg: 24px xl: 32px xxl: 48px - section: 64px + section: 96px components: app-shell: @@ -141,7 +165,7 @@ components: textColor: "{colors.ink}" typography: "{typography.body-md}" sidebar: - backgroundColor: "{colors.canvas-sidebar}" + backgroundColor: "{colors.surface-card}" textColor: "{colors.body}" borderColor: "{colors.hairline}" width: 252px @@ -155,323 +179,265 @@ components: backgroundColor: "{colors.primary}" textColor: "{colors.on-primary}" typography: "{typography.button}" - rounded: "{rounded.xl}" - padding: 10px 16px + rounded: "{rounded.pill}" height: 40px button-outline: - backgroundColor: "{colors.surface-card}" - textColor: "{colors.body}" + backgroundColor: transparent + textColor: "{colors.ink}" typography: "{typography.button}" - rounded: "{rounded.xl}" - borderColor: "{colors.hairline-soft}" - padding: 10px 16px - height: 40px - button-accent-cyan: - backgroundColor: "{colors.accent-cyan}" - textColor: "{colors.on-accent-dark}" - typography: "{typography.button}" - rounded: "{rounded.xl}" - padding: 10px 16px + rounded: "{rounded.pill}" + borderColor: "{colors.hairline-strong}" height: 40px nav-item: backgroundColor: transparent - textColor: "{colors.body}" + textColor: "{colors.muted}" typography: "{typography.nav-link}" - rounded: "{rounded.xl}" + rounded: "{rounded.pill}" height: 44px nav-item-active: - backgroundColor: "rgba(59, 130, 246, 0.15)" - textColor: "{colors.accent-blue}" + backgroundColor: "{colors.surface-strong}" + textColor: "{colors.ink}" hero-band: - backgroundColor: "{colors.canvas-panel}" + backgroundColor: "{colors.canvas-soft}" textColor: "{colors.ink}" typography: "{typography.display-xl}" - rounded: "{rounded.xxxl}" - padding: 32px - stat-card: - backgroundColor: "{colors.surface-card}" - textColor: "{colors.ink}" - typography: "{typography.body-md}" - rounded: "{rounded.xxl}" - padding: 20px + rounded: "{rounded.3xl}" + borderColor: "{colors.hairline}" + padding: 64px feature-card: backgroundColor: "{colors.surface-card}" textColor: "{colors.ink}" typography: "{typography.title-sm}" - rounded: "{rounded.xxl}" + rounded: "{rounded.2xl}" + borderColor: "{colors.hairline}" padding: 24px + section-card: + backgroundColor: "{colors.surface-card}" + textColor: "{colors.ink}" + rounded: "{rounded.2xl}" + borderColor: "{colors.hairline}" + padding: 24px + icon-plate: + backgroundColor: "{colors.surface-strong}" + textColor: "{colors.ink}" + rounded: "{rounded.full}" + size: 40px text-input: - backgroundColor: "{colors.canvas-panel}" - textColor: "{colors.on-dark}" + backgroundColor: "{colors.canvas}" + textColor: "{colors.ink}" typography: "{typography.body-md}" - rounded: "{rounded.lg}" - padding: 12px 16px - borderColor: "{colors.hairline-soft}" + rounded: "{rounded.md}" + borderColor: "{colors.hairline-strong}" badge-pill: - backgroundColor: "rgba(34, 211, 238, 0.10)" - textColor: "#67e8f9" - typography: "{typography.caption-semibold}" + backgroundColor: "{colors.surface-strong}" + textColor: "{colors.muted}" + typography: "{typography.caption-label}" rounded: "{rounded.pill}" padding: 4px 12px brand-icon: - backgroundColor: "linear-gradient(135deg, {colors.gradient-cyan-start}, {colors.gradient-blue-end})" + backgroundColor: "{colors.primary} + sky/lavender gradient orb overlay" textColor: "{colors.on-primary}" - rounded: "{rounded.xxl}" + rounded: "{rounded.xl}" size: 44px avatar: - backgroundColor: "linear-gradient(135deg, {colors.gradient-cyan-start}, {colors.gradient-blue-end})" + backgroundColor: "{colors.primary} + sky gradient orb overlay" textColor: "{colors.on-primary}" rounded: "{rounded.full}" size: 32px + gradient-orb: + background: "radial-gradient with one of {colors.gradient-*}" + blur: 48-64px + opacity: 0.45-0.6 --- ## Overview -AI 视频助手管理台 reads as a focused dark ops dashboard for configuring and monitoring AI video assistants. The base canvas is deep navy `{colors.canvas}` (#080b13) holding cool off-white ink `{colors.ink}` (#e9eef7). Brand voltage is **chromatic and directional**: a cyan-to-blue gradient accent (`{colors.gradient-cyan-start}` → `{colors.gradient-blue-end}`) marks logos, avatars, and primary CTAs; soft radial blue/cyan glow blooms add depth behind hero panels — not pastel atmospheric orbs. +AI 视频助手管理台 reads like a quietly editorial print magazine that happens to be an admin console. It ships in **two themes built from one navy palette**: -Type runs **Inter** at 400–700 across all surfaces. Headings are bold (700), labels semibold (600), body at 400. There is no display serif — the voice is utilitarian and developer-console. +- **Dark navy** (default): deep-navy canvas `{colors-dark.canvas}` (#070b16) holding off-white ink `{colors-dark.ink}` (#f1f5ff). +- **Light navy**: cool off-white canvas `{colors.canvas}` (#f3f5fb) holding deep-navy ink `{colors.ink}` (#0c1426). -CTAs split into three tiers: shadcn primary blue pill (`{component.button-primary}`), dark outlined panel (`{component.button-outline}`), and cyan solid for workflow-mode emphasis (`{component.button-accent-cyan}`). Layered navy surfaces (`#0a0e17` → `#0f1521` → `#0d121d`) create depth without heavy shadows. +The brand voltage is **photographic, not chromatic**: soft pastel atmospheric gradient orbs (mint, peach, lavender, sky, rose) drift behind hero/feature panels as the only "color" moments. There is no neon accent, no saturated CTA color, and no cyan/blue dev-console gradient anymore. + +Type pairs **Cormorant Garamond Light** (display serif at weight 300 — the open-source substitute for Waldenburg) with **Inter** for body, navigation, captions, and buttons. The display weight at 300 is the editorial signature — never bold. Latin display runs in the serif; CJK display falls back to the system serif/sans at weight 300 (still light/editorial). + +CTAs are subtle pills: in light mode the primary is a deep-navy ink pill (`{component.button-primary}`); in dark mode it inverts to an off-white pill (`{colors-dark.primary}` → off-white, text `{colors-dark.primary-foreground}`). The secondary is a transparent hairline-outline pill. **Key Characteristics:** -- Deep navy canvas, cool off-white ink. Blue/cyan gradient as brand accent. -- Layered surface stack: shell → sidebar → card → panel/input. -- Inter at 400–700 — no display serif, no weight-300 editorial voice. -- Radial glow blooms (blue/cyan) behind hero and workflow panels only. -- Rounded-xl (12px) for buttons and nav; rounded-2xl/3xl for cards and heroes. -- 81px header/sidebar-brand height; 32px page padding. +- One navy palette, two themes; dark is the default, persisted to `localStorage` with a no-flash bootstrap script in `layout.tsx`. +- Off-white / deep-navy ink. No saturated CTA color — ink pill only. +- Display runs Cormorant Garamond Light at weight 300 — editorial voice. +- Body runs Inter at 400/500 with subtle +0.01em tracking. +- Pastel gradient orbs (5 tokens) used as atmospheric decoration only. +- Pill geometry (`{rounded.pill}`) for every CTA, nav item, and badge; `{rounded.2xl}` for cards, `{rounded.3xl}` for hero bands. +- Hairline borders + a single soft shadow tier — no layered-surface dev-console stack. +- 96px section rhythm; ~40px page padding; 1180px max content width. ## Colors -### Brand & Accent -- **Primary** (`{colors.primary}` — #2563eb): shadcn primary blue — default CTA pill fill. Source: `oklch(0.55 0.22 255)`. -- **Primary Active** (`{colors.primary-active}` — #1d4ed8): Press/hover darkening on primary. -- **Accent Cyan** (`{colors.accent-cyan}` — #22d3ee): Workflow-mode CTA, status dots, glow accents. Tailwind `cyan-400`. -- **Accent Blue** (`{colors.accent-blue}` — #60a5fa): Active nav text, labels, stat icons. Tailwind `blue-400`. -- **Accent Blue Strong** (`{colors.accent-blue-strong}` — #3b82f6): Gradient end-stop, active states. Tailwind `blue-500`. +The two `colors` / `colors-dark` blocks above are the source of truth and are wired into `globals.css` as the shadcn variables (`--background`, `--foreground`, `--card`, `--primary`, `--border`, `--muted-foreground`, …) plus editorial extras (`--ink`, `--body-text`, `--surface-strong`, `--hairline*`, `--canvas-soft`, `--gradient-*`). Tailwind utilities like `bg-background`, `text-ink`, `border-hairline`, `bg-surface-strong`, and `text-muted-foreground` resolve to the active theme automatically. -### Surface Stack (darkest → lightest) -- **Canvas** (`{colors.canvas}` — #080b13): App shell floor, topbar background. -- **Canvas Sidebar** (`{colors.canvas-sidebar}` — #0a0e17): Sidebar panel — one step lighter than shell. -- **Canvas Panel** (`{colors.canvas-panel}` — #0d121d): Recessed inputs, toggle rows, hero base under glow. -- **Surface Card** (`{colors.surface-card}` — #0f1521): Cards, outline buttons, dropdowns. -- **Surface Hover** (`{colors.surface-hover}` — #151e30): Hover state for outline buttons and interactive panels. -- **Surface Node** (`{colors.surface-node}` — #111827): Workflow step nodes. +### Primary (CTA) +- **Light** `{colors.primary}` (#1b2741): deep-navy ink pill, white text. Press → `{colors.primary-active}` (#0c1426). +- **Dark** `{colors-dark.primary}` (#e8edf9): off-white pill, deep-navy text (`#0c1426`). The editorial inversion — used scarcely. + +### Surfaces +- **Canvas** — page floor. Light #f3f5fb / Dark #070b16. +- **Canvas Soft** — hero/placeholder bands. Light #f9fafd / Dark #0b1322. +- **Surface Card** — content cards, popovers. Light #ffffff / Dark #0e1626. +- **Surface Strong** — icon plates, badges, active nav. Light #e9edf7 / Dark #18233a. ### Hairlines -- **Hairline** (`{colors.hairline}` — #161d2c): Sidebar/topbar dividers, structural borders. -- **Hairline Soft** (`{colors.hairline-soft}` — #1b2233): Default card, input, and button borders. -- **Hairline Strong** (`{colors.hairline-strong}` — #2a3550): Hover border accent on cards and buttons. -- **Hairline Node** (`{colors.hairline-node}` — #273249): Workflow node outlines. +- **Hairline** — default 1px divider/card outline. Light #e3e7f1 / Dark #1b2740. +- **Hairline Soft** — lighter divider. Light #eef1f8 / Dark #141d30. +- **Hairline Strong** — input/outline-button border. Light #cbd3e4 / Dark #283450. ### Text -- **Ink** (`{colors.ink}` — #e9eef7): Headings, primary labels, input text. -- **Body** (`{colors.body}` — #9aa6bd): Nav links, descriptions, secondary copy. -- **Body Sub** (`{colors.body-sub}` — #7f8aa3): Sub-navigation items. -- **Muted** (`{colors.muted}` — #5d6880): Captions, placeholders, metadata. -- **On Primary** (`{colors.on-primary}` — #ffffff): Text on blue/cyan buttons and gradient icons. -- **On Accent Dark** (`{colors.on-accent-dark}` — #04121a): Text on solid cyan CTA buttons. +- **Ink** — display & primary text. Light #0c1426 / Dark #f1f5ff. +- **Body** — running copy. Light #44516c / Dark #a6b2cb. +- **Muted** — descriptions, sub-labels. Light #5d6b86 / Dark #93a0bb. +- **Muted Soft** — captions, placeholders. Light #94a0bd / Dark #6c7a96. -### Gradient & Glow (signature) -- **Gradient Cyan Start** (`{colors.gradient-cyan-start}` — #22d3ee): Brand icon/avatar gradient start. -- **Gradient Blue End** (`{colors.gradient-blue-end}` — #3b82f6): Brand icon/avatar gradient end. -- **Gradient Text Start** (`{colors.gradient-text-start}` — #67e8f9): Brand wordmark gradient start (`cyan-300`). -- **Gradient Text End** (`{colors.gradient-text-end}` — #60a5fa): Brand wordmark gradient end (`blue-400`). -- **Glow Blue** (`{colors.glow-blue}` — rgba(46,161,255,0.18)): Hero panel radial bloom. -- **Glow Cyan** (`{colors.glow-cyan}` — rgba(34,211,238,0.16)): Workflow panel radial bloom. - -These appear as radial-gradient backgrounds behind hero/workflow sections and as box-shadow halos on status dots. Never as flat card fills. - -### Stat Accents (dashboard metrics only) -- **Stat Cyan** (`{colors.stat-cyan}` — #22d3ee): Assistant count icon plate. -- **Stat Blue** (`{colors.stat-blue}` — #60a5fa): Session count icon plate. -- **Stat Violet** (`{colors.stat-violet}` — #a78bfa): Response time icon plate. -- **Stat Emerald** (`{colors.stat-emerald}` — #34d399): Resolution rate icon plate. - -Each stat uses `{color}/10` opacity background with matching text color. +### Atmospheric Gradient Orbs (signature) +Five pastel stops — `gradient-mint`, `gradient-peach`, `gradient-lavender`, `gradient-sky`, `gradient-rose`. In dark mode they shift to lower-chroma navy-compatible variants (e.g. sky #a8c8e8 → #5f86b8). They appear ONLY as soft, blurred radial blooms behind hero/feature/placeholder copy and as the orb overlay on the brand icon/avatar. Never as button fills, text colors, or flat card surfaces. ### Semantic -- **Success** (`{colors.semantic-success}` — #34d399): Trend badges, confirmation. Tailwind `emerald-400`. -- **Error** (`{colors.semantic-error}` — #f87171): Validation errors. shadcn destructive light mode. - -### shadcn CSS Variables (globals.css) - -The project also defines semantic tokens in `:root` / `.dark` via oklch. Key mappings: - -| Token | Light | Dark | -|---|---|---| -| `--background` | white | near-black | -| `--foreground` | near-black | near-white | -| `--primary` | `oklch(0.55 0.22 255)` | `oklch(0.6 0.22 255)` | -| `--card` | white | `oklch(0.205 0 0)` | -| `--border` | light gray | white 10% | -| `--destructive` | red | lighter red | - -Page components currently use hardcoded hex values above; shadcn tokens apply to base UI primitives (`Button`, `Select`, `Card` defaults). +- **Success** — Light #16a34a / Dark #4ade80. +- **Error** — Light #dc2626 / Dark #f87171. ## Typography ### Font Family -**Inter** is the primary UI font (`--font-sans`). **Geist Sans** and **Geist Mono** are loaded as fallbacks/utilities. No display serif. +**Cormorant Garamond** is the display serif (`--font-display`, weight 300) — the open-source substitute for the licensed Waldenburg Light. **Inter** carries body, navigation, captions, and buttons (`--font-sans`). **Geist Mono** is loaded for monospace. Helper classes in `globals.css`: `.font-display`, `.display-mega/-xl/-lg/-md/-sm`, `.caption-label`. ### Hierarchy -| Token | Size | Weight | Line Height | Use | +| Token | Size | Weight | Tracking | Use | |---|---|---|---|---| -| `{typography.display-xl}` | 36px | 700 | 1.1 | Page hero h1 | -| `{typography.display-lg}` | 32px | 700 | 1.15 | Section heads | -| `{typography.title-md}` | 20px | 600 | 1.35 | Card titles | -| `{typography.title-sm}` | 18px | 600 | 1.4 | Sub-section heads | -| `{typography.body-md}` | 15px | 400 | 1.73 | Default body | -| `{typography.body-strong}` | 15px | 600 | 1.73 | Emphasized body | -| `{typography.body-sm}` | 14px | 400 | 1.5 | Compact body | -| `{typography.caption}` | 12px | 400 | 1.4 | Metadata | -| `{typography.caption-semibold}` | 12px | 600 | 1.4 | Badges, trend pills | -| `{typography.button}` | 14px | 500 | 1.0 | Button labels | -| `{typography.nav-link}` | 14px | 400 | 1.4 | Sidebar nav | -| `{typography.brand-label}` | 14px | 700 | 1.4 | Sidebar brand name | +| `{typography.display-mega}` | clamp 40–64px | 300 | -0.03em | Marketing-scale hero | +| `{typography.display-xl}` | clamp 32–48px | 300 | -0.02em | Homepage hero h1 | +| `{typography.display-lg}` | 36px | 300 | -0.01em | Page titles | +| `{typography.display-md}` | 32px | 300 | -0.01em | Section heads | +| `{typography.display-sm}` | 24px | 300 | 0 | Card group titles | +| `{typography.title-md}` | 20px | 500 | 0 | Component titles (Inter) | +| `{typography.title-sm}` | 18px | 500 | 0 | List labels | +| `{typography.body-md}` | 16px | 400 | +0.01em | Default body | +| `{typography.body-strong}` | 16px | 500 | +0.01em | Emphasized body | +| `{typography.body-sm}` | 15px | 400 | +0.01em | Compact body | +| `{typography.caption-label}` | 12px | 600 | +0.08em, UPPER | Section labels, badges | +| `{typography.button}` | 14px | 500 | 0 | CTA pill | +| `{typography.nav-link}` | 14px | 400 | 0 | Sidebar nav | ### Principles -- **Bold headings.** Page titles at 700 — this is an admin console, not editorial. -- **Semibold for labels.** Form labels and card titles at 600. -- **Tight tracking on display.** `-0.72px` on hero h1; body stays at 0 tracking. +- **Display weight stays at 300.** Cormorant Garamond Light is the editorial signature. Never bold display copy. +- **Subtle tracking on body.** Inter at +0.01em (applied globally on `body`). +- **Negative tracking on display.** Tighter as size grows (-0.01em → -0.03em). +- **Caption labels** are the editorial section marker — uppercase, 600, +0.08em. ## Layout -### Spacing System -- **Base unit:** 4px. -- **Tokens:** `{spacing.xxs}` 4px · `{spacing.xs}` 8px · `{spacing.sm}` 12px · `{spacing.base}` 16px · `{spacing.md}` 20px · `{spacing.lg}` 24px · `{spacing.xl}` 32px · `{spacing.xxl}` 48px · `{spacing.section}` 64px. -- **Page padding:** 32px horizontal (`px-8`), 28px vertical (`py-7`). - -### Grid & Container -- Max content width: 1180px. -- Stat cards: 4-up grid at desktop. +### Spacing & Container +- Base unit 4px; section rhythm 96px (`{spacing.section}`). +- Page padding: `px-8 py-10`. Max content width 1180px. - Sidebar: 252px expanded, 76px collapsed. ### Whitespace Philosophy -Compact ops-dashboard pacing. Cards sit 16–24px apart inside the main scroll area. Hero band gets generous internal padding (32px) with a radial glow occupying the top-left quadrant. +Generous editorial pacing. Hero bands get large internal padding (64px) with a gradient orb in a corner; content cards sit 16–24px apart. ## Elevation & Depth -The system uses **layered navy surfaces + hairline borders**. Depth comes from surface color steps, not drop shadows. +The system uses **hairline + soft drop**. Cards float above the canvas via a 1px hairline and a single subtle shadow tier (`shadow-sm`, deepening to `shadow-md` on hover). Atmospheric depth comes from the gradient orbs, not from layered surface steps. | Level | Treatment | Use | |---|---|---| -| Shell | `{colors.canvas}` (#080b13) | App background | -| Sidebar | `{colors.canvas-sidebar}` (#0a0e17) | Navigation panel | -| Card | `{colors.surface-card}` (#0f1521) | Content cards, buttons | -| Recessed | `{colors.canvas-panel}` (#0d121d) | Inputs, toggle rows | -| Hairline border | 1px `{colors.hairline-soft}` | Card/input outlines | -| Hover border | 1px `{colors.hairline-strong}` | Interactive hover accent | -| Radial glow | `{colors.glow-blue}` or `{colors.glow-cyan}` | Hero/workflow atmospheric depth | -| Gradient shadow | `shadow-cyan-500/30` | Brand icon elevation | - -### Decorative Depth -- **Radial glow blooms** sit behind hero and workflow panels — blue for prompt mode, cyan for workflow mode. -- **Status dots** use colored glow halos: `box-shadow: 0 0 0 4px rgba(...,.16), 0 0 14px rgba(...,.35)`. +| Canvas | `{colors.canvas}` | App background, topbar | +| Card | `{colors.surface-card}` | Content cards, popovers | +| Hairline border | 1px `{colors.hairline}` | Card/input outlines | +| Soft drop | `shadow-sm` → `shadow-md` on hover | Card elevation | +| Gradient orb | blurred radial bloom, opacity 0.45–0.6 | Atmospheric depth only | ## Shapes -### Border Radius Scale +### Border Radius Scale (`--radius: 0.75rem` = 12px) -| Token | Value | Use | +| Token | Approx | Use | |---|---|---| -| `{rounded.sm}` | 6px | Compact elements | -| `{rounded.lg}` | 10px | shadcn base (`--radius: 0.625rem`) | -| `{rounded.xl}` | 12px | Buttons, nav items, inputs | -| `{rounded.xxl}` | 16px | Stat cards, brand icon | -| `{rounded.xxxl}` | 24px | Hero bands, workflow panels | -| `{rounded.pill}` | 9999px | Trend badges, status pills | -| `{rounded.full}` | 9999px | Avatars | +| `{rounded.md}` | ~10px | Form inputs | +| `{rounded.lg}` | 12px | Compact elements | +| `{rounded.xl}` | ~17px | Brand icon | +| `{rounded.2xl}` | ~22px | Feature/section cards | +| `{rounded.3xl}` | ~26px | Hero/placeholder bands | +| `{rounded.pill}` | 9999px | All buttons, nav items, badges | +| `{rounded.full}` | 9999px | Avatars, icon plates | ## Components ### App Shell - -**`app-shell`** — Background `{colors.canvas}`, text `{colors.ink}`. Flex row: sidebar left, main column right (topbar + scrollable content). +**`app-shell`** — `bg-background text-foreground`. Flex row: sidebar left, main column right (topbar + scrollable content at `px-8 py-10`). ### Sidebar - -**`sidebar`** — Background `{colors.canvas-sidebar}`, border-right `{colors.hairline}`, width 252px (76px collapsed). Brand block at top with `{component.brand-icon}` + gradient text label. Nav items at `{component.nav-item}` height 44px; active state uses `{component.nav-item-active}`. +**`sidebar`** — `bg-sidebar`, border-right `{colors.hairline}`, width 252/76px. Brand block: gradient-orb brand icon + serif wordmark + uppercase `管理台` caption-label. Nav items are pill-shaped (44px); active uses `bg-sidebar-accent` with foreground text. ### Top Bar +**`topbar`** — `bg-background`, border-bottom hairline, height 81px. Right-aligned: Help outline pill, **theme toggle** (sun/moon), notification bell, user avatar pill — all hairline-outline style. -**`topbar`** — Background `{colors.canvas}`, border-bottom `{colors.hairline}`, height 81px. Right-aligned: Help button, notification bell, user avatar dropdown — all `{component.button-outline}` style. +### Buttons (pills) +**`button-primary`** — ink pill (light) / off-white pill (dark), `{rounded.pill}`, height 40px (`size="lg"`). +**`button-outline`** — transparent pill, 1px `{colors.hairline-strong}` border, text ink; hover `bg-surface-strong`. -### Buttons +### Hero & Atmospheric +**`hero-band`** — `bg-canvas-soft`, `{rounded.3xl}`, hairline border, 64px padding, with 1–2 blurred gradient orbs (sky + lavender) behind editorial display copy, a caption-label eyebrow, and two CTAs. +**`gradient-orb`** — blurred radial bloom in one of the five pastel tokens; pure atmosphere, holds no content. -**`button-primary`** — Blue pill. Background `{colors.primary}`, text `{colors.on-primary}`, rounded `{rounded.xl}`, height 40px. +### Cards +**`feature-card` / `section-card`** — `bg-card`, `{rounded.2xl}`, hairline border, `shadow-sm`. Icon plate is a 40px `{colors.surface-strong}` circle with neutral foreground icon (no colored accent). -**`button-outline`** — Dark panel with border. Background `{colors.surface-card}`, text `{colors.body}`, border `{colors.hairline-soft}`. Hover: background `{colors.surface-hover}`, text `{colors.ink}`, border `{colors.hairline-strong}`. - -**`button-accent-cyan`** — Workflow-mode CTA. Background `{colors.accent-cyan}`, text `{colors.on-accent-dark}`, rounded `{rounded.xl}`. - -### Hero & Panels - -**`hero-band`** — Background `{colors.canvas-panel}` with `{colors.glow-blue}` radial gradient overlay. Rounded `{rounded.xxxl}`, border `{colors.hairline-soft}`, padding 32px. Contains brand icon, blue accent label, bold h1, body copy, and two CTAs. - -**`stat-card`** — 4-up metric grid. Background `{colors.surface-card}`, rounded `{rounded.xxl}`, padding 20px. Icon plate uses stat accent colors at 10% opacity background. - -### Forms - -**`text-input`** — Background `{colors.canvas-panel}`, text white, placeholder `{colors.muted}`, border `{colors.hairline-soft}`, rounded `{rounded.xl}`. - -**`badge-pill`** — Cyan-tinted pill for workflow status. Background cyan 10%, text cyan-300, rounded `{rounded.pill}`. +### Forms & Tags +**`text-input`** — `bg-background`, text ink, 1px `{colors.hairline-strong}` border, `{rounded.md}`; focus thickens border to ring. +**`badge-pill`** — `bg-surface-strong`, caption-label text, `{rounded.pill}`. ### Brand Elements +**`brand-icon`** / **`avatar`** — `{colors.primary}` base with a sky/lavender gradient-orb overlay, `{rounded.xl}` (icon) / `{rounded.full}` (avatar). The pastel orb replaces the old cyan→blue gradient. -**`brand-icon`** — 44×44px rounded `{rounded.xxl}` with `linear-gradient(135deg, cyan-400, blue-500)`, white icon, `shadow-cyan-500/30`, inset `ring-white/20`. +## Theming -**`avatar`** — 32×32px circle with same gradient, bold initial letter. +- Two themes from one palette: `.dark` toggles the variable set in `globals.css`. Dark is the default ``. +- A `ThemeToggle` (sun/moon) in the topbar flips `documentElement.classList` and writes `localStorage.theme`. +- A no-flash inline script in `layout.tsx` applies the stored theme before paint (defaults to dark). ## Do's and Don'ts ### Do -- Use the layered navy surface stack for depth — don't flatten everything to one background. -- Use cyan-to-blue gradient for brand marks (logo, avatars) only. -- Use `{colors.accent-blue}` at 15% opacity for active nav backgrounds. -- Use `{rounded.xl}` for interactive controls; `{rounded.xxl}` / `{rounded.xxxl}` for containers. -- Use Inter at 700 for page titles, 600 for labels, 400 for body. +- Reserve `{colors.primary}` (ink/off-white pill) for primary CTAs. +- Use Cormorant Garamond Light at weight 300 for every display headline. Never bold. +- Use Inter at +0.01em tracking for body. +- Use atmospheric gradient orbs (mint/peach/lavender/sky/rose) as decoration only. +- Use the pill shape for every CTA, nav item, and badge. +- Drive color from semantic tokens (`bg-card`, `text-ink`, `border-hairline`) so both themes work — never inline hex. ### Don't -- Don't introduce warm/off-white editorial palettes — this is a dark admin console. -- Don't use pastel gradient orbs — glows are blue/cyan radial blooms only. -- Don't use a display serif — Inter only. -- Don't use pill-shaped CTAs for primary actions — rounded-xl (12px) is the button geometry. -- Don't use saturated stat colors as primary CTAs — reserve `{colors.primary}` and `{colors.accent-cyan}` for actions. +- Don't reintroduce the cyan/blue dev-console accent or layered-navy surface stack. +- Don't bold display copy — it sits at weight 300. +- Don't use gradient orbs as button fills, text colors, or flat card backgrounds. +- Don't use sharp 0px or `{rounded.xl}` corners on CTAs — pill geometry is the brand button. +- Don't drop body Inter to weight 300 to match the serif — body stays at 400/500. ## Responsive Behavior -### Breakpoints - | Name | Width | Key Changes | |---|---|---| -| Mobile | < 640px | Stat cards 1-up; sidebar collapsed; user name hidden | -| Tablet | 640–1024px | Stat cards 2-up | -| Desktop | 1024–1280px | Stat cards 4-up; full sidebar | +| Mobile | < 640px | Feature grid 1-up; sidebar collapsed; user name hidden; display sizes shrink via clamp | +| Tablet | 640–1024px | Feature grid 2-up | +| Desktop | 1024–1280px | Feature grid 3-up; full sidebar | | Wide | > 1280px | Content caps at 1180px | ### Touch Targets -- Nav items at 44px height. -- Primary buttons at 40px height (`size="lg"`). - -### Collapsing Strategy -- Sidebar collapses to 76px icon-only via toggle button. -- Stat grid: 4-up → 2-up → 1-up. - -## Iteration Guide - -1. Focus on a single component at a time. -2. CTAs default to `{rounded.xl}`. Cards use `{rounded.xxl}` or `{rounded.xxxl}`. -3. Variants live as separate entries. -4. Use `{token.refs}` everywhere — never inline hex in new code (migrate existing hardcoded values over time). -5. Hover states: surface step up + border accent to `{colors.hairline-strong}`. -6. Inter 700 for display, 600 for labels, 400/500 for body and buttons. -7. Glow blooms scoped to hero/workflow panels only. +- Nav items 44px; primary buttons 40px (`size="lg"`). +- Sidebar collapses to 76px icon-only via toggle. ## Known Gaps -- Page components use hardcoded hex values; shadcn CSS variables exist but aren't fully wired to page-level surfaces yet. -- Light mode tokens exist in `:root` but the app renders dark-only (no `.dark` class toggle). -- Animation timings (glow pulse, sidebar collapse) out of scope. -- Form validation states beyond defaults not fully documented. +- Cormorant Garamond covers Latin only; CJK display headings fall back to the system serif/sans at weight 300. A bundled light CJK serif (e.g. a Noto Serif SC weight) is not yet wired in. +- Several inner pages (Components, History, Test, Workflow, Profile) currently use the shared `PlaceholderPage` editorial header and await real content. +- Animation timings (orb drift, hero entrance, theme cross-fade) out of scope. +- Form validation states beyond focus not yet documented. diff --git a/src/app/globals.css b/src/app/globals.css index bfd4bcb..bdec1f5 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -9,7 +9,8 @@ --color-foreground: var(--foreground); --font-sans: var(--font-sans); --font-mono: var(--font-geist-mono); - --font-heading: var(--font-sans); + --font-display: var(--font-display); + --font-heading: var(--font-display); --color-sidebar-ring: var(--sidebar-ring); --color-sidebar-border: var(--sidebar-border); --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); @@ -39,6 +40,24 @@ --color-popover: var(--popover); --color-card-foreground: var(--card-foreground); --color-card: var(--card); + + /* Editorial (ElevenLabs-derived) tokens — navy palette */ + --color-canvas-soft: var(--canvas-soft); + --color-surface-strong: var(--surface-strong); + --color-ink: var(--ink); + --color-body: var(--body-text); + --color-muted-soft: var(--muted-soft); + --color-hairline: var(--hairline); + --color-hairline-soft: var(--hairline-soft); + --color-hairline-strong: var(--hairline-strong); + --color-on-primary: var(--on-primary); + --color-success: var(--success); + --color-gradient-mint: var(--gradient-mint); + --color-gradient-peach: var(--gradient-peach); + --color-gradient-lavender: var(--gradient-lavender); + --color-gradient-sky: var(--gradient-sky); + --color-gradient-rose: var(--gradient-rose); + --radius-sm: calc(var(--radius) * 0.6); --radius-md: calc(var(--radius) * 0.8); --radius-lg: var(--radius); @@ -48,73 +67,114 @@ --radius-4xl: calc(var(--radius) * 2.6); } +/* ---------- Light navy (editorial off-white, navy ink) ---------- */ :root { - --background: oklch(1 0 0); - --foreground: oklch(0.145 0 0); - --card: oklch(1 0 0); - --card-foreground: oklch(0.145 0 0); - --popover: oklch(1 0 0); - --popover-foreground: oklch(0.145 0 0); - --primary: oklch(0.55 0.22 255); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.97 0 0); - --secondary-foreground: oklch(0.205 0 0); - --muted: oklch(0.97 0 0); - --muted-foreground: oklch(0.556 0 0); - --accent: oklch(0.97 0 0); - --accent-foreground: oklch(0.205 0 0); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.922 0 0); - --input: oklch(0.922 0 0); - --ring: oklch(0.708 0 0); - --chart-1: oklch(0.87 0 0); - --chart-2: oklch(0.556 0 0); - --chart-3: oklch(0.439 0 0); - --chart-4: oklch(0.371 0 0); - --chart-5: oklch(0.269 0 0); - --radius: 0.625rem; - --sidebar: oklch(0.985 0 0); - --sidebar-foreground: oklch(0.145 0 0); - --sidebar-primary: oklch(0.205 0 0); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.97 0 0); - --sidebar-accent-foreground: oklch(0.205 0 0); - --sidebar-border: oklch(0.922 0 0); - --sidebar-ring: oklch(0.708 0 0); + --background: #f3f5fb; + --foreground: #0f1b33; + --card: #ffffff; + --card-foreground: #0f1b33; + --popover: #ffffff; + --popover-foreground: #0f1b33; + --primary: #1b2741; + --primary-foreground: #ffffff; + --secondary: #e9edf7; + --secondary-foreground: #1b2741; + --muted: #eef1f8; + --muted-foreground: #5d6b86; + --accent: #e9edf7; + --accent-foreground: #1b2741; + --destructive: #dc2626; + --border: #dfe4f0; + --input: #cbd3e4; + --ring: #94a0bd; + --chart-1: #1b2741; + --chart-2: #3a4a6b; + --chart-3: #5d6b86; + --chart-4: #94a0bd; + --chart-5: #c8d0e2; + --radius: 0.75rem; + + /* Editorial tokens */ + --canvas-soft: #f9fafd; + --surface-strong: #e9edf7; + --ink: #0c1426; + --body-text: #44516c; + --muted-soft: #94a0bd; + --hairline: #e3e7f1; + --hairline-soft: #eef1f8; + --hairline-strong: #cbd3e4; + --on-primary: #ffffff; + --success: #16a34a; + + /* Atmospheric pastel orbs (cool-leaning for navy) */ + --gradient-mint: #a7e5d3; + --gradient-peach: #f4c5a8; + --gradient-lavender: #c8b8e0; + --gradient-sky: #a8c8e8; + --gradient-rose: #e8b8c4; + + --sidebar: #ffffff; + --sidebar-foreground: #0f1b33; + --sidebar-primary: #1b2741; + --sidebar-primary-foreground: #ffffff; + --sidebar-accent: #eef1f8; + --sidebar-accent-foreground: #1b2741; + --sidebar-border: #e3e7f1; + --sidebar-ring: #94a0bd; } +/* ---------- Dark navy (deep navy canvas, off-white ink) ---------- */ .dark { - --background: oklch(0.145 0 0); - --foreground: oklch(0.985 0 0); - --card: oklch(0.205 0 0); - --card-foreground: oklch(0.985 0 0); - --popover: oklch(0.205 0 0); - --popover-foreground: oklch(0.985 0 0); - --primary: oklch(0.6 0.22 255); - --primary-foreground: oklch(0.985 0 0); - --secondary: oklch(0.269 0 0); - --secondary-foreground: oklch(0.985 0 0); - --muted: oklch(0.269 0 0); - --muted-foreground: oklch(0.708 0 0); - --accent: oklch(0.269 0 0); - --accent-foreground: oklch(0.985 0 0); - --destructive: oklch(0.704 0.191 22.216); - --border: oklch(1 0 0 / 10%); - --input: oklch(1 0 0 / 15%); - --ring: oklch(0.556 0 0); - --chart-1: oklch(0.87 0 0); - --chart-2: oklch(0.556 0 0); - --chart-3: oklch(0.439 0 0); - --chart-4: oklch(0.371 0 0); - --chart-5: oklch(0.269 0 0); - --sidebar: oklch(0.205 0 0); - --sidebar-foreground: oklch(0.985 0 0); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.985 0 0); - --sidebar-accent: oklch(0.269 0 0); - --sidebar-accent-foreground: oklch(0.985 0 0); - --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.556 0 0); + --background: #070b16; + --foreground: #e8edf9; + --card: #0e1626; + --card-foreground: #e8edf9; + --popover: #0e1626; + --popover-foreground: #e8edf9; + --primary: #e8edf9; + --primary-foreground: #0c1426; + --secondary: #18233a; + --secondary-foreground: #e8edf9; + --muted: #141d30; + --muted-foreground: #93a0bb; + --accent: #18233a; + --accent-foreground: #e8edf9; + --destructive: #f87171; + --border: #1b2740; + --input: #283450; + --ring: #4a5876; + --chart-1: #e8edf9; + --chart-2: #c8d0e2; + --chart-3: #93a0bb; + --chart-4: #5d6b86; + --chart-5: #2a3654; + + /* Editorial tokens */ + --canvas-soft: #0b1322; + --surface-strong: #18233a; + --ink: #f1f5ff; + --body-text: #a6b2cb; + --muted-soft: #6c7a96; + --hairline: #1b2740; + --hairline-soft: #141d30; + --hairline-strong: #283450; + --on-primary: #0c1426; + --success: #4ade80; + + --gradient-mint: #5fae9b; + --gradient-peach: #c08a6b; + --gradient-lavender: #8a78ad; + --gradient-sky: #5f86b8; + --gradient-rose: #b07d8c; + + --sidebar: #0a111e; + --sidebar-foreground: #e8edf9; + --sidebar-primary: #e8edf9; + --sidebar-primary-foreground: #0c1426; + --sidebar-accent: #18233a; + --sidebar-accent-foreground: #e8edf9; + --sidebar-border: #1b2740; + --sidebar-ring: #4a5876; } @layer base { @@ -123,8 +183,52 @@ } body { @apply bg-background text-foreground; + letter-spacing: 0.01em; } html { @apply font-sans; } -} \ No newline at end of file +} + +@layer components { + /* Waldenburg Light substitute — EB Garamond at weight 300. The editorial + display signature: serif, light, tightly tracked. Never bold. */ + .font-display { + font-family: var(--font-display), "Times New Roman", serif; + font-weight: 300; + letter-spacing: -0.01em; + } + .display-mega { + font-size: clamp(2.5rem, 5vw, 4rem); + line-height: 1.05; + letter-spacing: -0.03em; + } + .display-xl { + font-size: clamp(2rem, 4vw, 3rem); + line-height: 1.08; + letter-spacing: -0.02em; + } + .display-lg { + font-size: 2.25rem; + line-height: 1.17; + letter-spacing: -0.01em; + } + .display-md { + font-size: 2rem; + line-height: 1.13; + letter-spacing: -0.01em; + } + .display-sm { + font-size: 1.5rem; + line-height: 1.2; + } + + /* Caption label — uppercase, tracked, the editorial section marker */ + .caption-label { + font-size: 0.75rem; + font-weight: 600; + line-height: 1.4; + letter-spacing: 0.08em; + text-transform: uppercase; + } +} diff --git a/src/app/layout.tsx b/src/app/layout.tsx index e159121..4211032 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,13 +1,16 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono, Inter } from "next/font/google"; +import { Geist_Mono, Inter, Cormorant_Garamond } from "next/font/google"; import "./globals.css"; import { cn } from "@/lib/utils"; -const inter = Inter({subsets:['latin'],variable:'--font-sans'}); +const inter = Inter({ subsets: ["latin"], variable: "--font-sans" }); -const geistSans = Geist({ - variable: "--font-geist-sans", +// Waldenburg Light is licensed; Cormorant Garamond at weight 300 is a close +// open-source substitute for the light editorial display voice. +const display = Cormorant_Garamond({ subsets: ["latin"], + weight: ["300", "400", "500"], + variable: "--font-display", }); const geistMono = Geist_Mono({ @@ -16,10 +19,13 @@ const geistMono = Geist_Mono({ }); export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: "AI 视频助手 · 管理台", + description: "创建、配置、测试并发布 AI 视频助手", }; +// Apply the persisted theme before paint to avoid a flash. Defaults to dark navy. +const themeScript = `(function(){try{var t=localStorage.getItem('theme');if(t==='light'){document.documentElement.classList.remove('dark')}else{document.documentElement.classList.add('dark')}}catch(e){document.documentElement.classList.add('dark')}})();`; + export default function RootLayout({ children, }: Readonly<{ @@ -27,9 +33,21 @@ export default function RootLayout({ }>) { return ( + +