Files
voiceon-character-admin/DESIGN.md

12 KiB
Raw Blame History

AI Character Admin Design System

0. Research Log

  • PRD baseline: adopted 10.1~10.9 as the visual contract because Task 1.1 is PRD-driven setup, not a new visual exploration.
  • ui-ux-pro-max: ran .opencode/skills/ui-ux-pro-max/scripts/search.py with the PRD 10.9 design-system query. Adopted dense operational dashboard, subtle motion, status colors, focus visibility, reduced-motion, and no emoji icons. Excluded generated green palette, white on primary, dark mode support, Fira remote fonts, oversized landing typography, glass/continuous animation, and GSAP page transitions because they conflict with PRD 10.1~10.3.
  • ui-ux-pro-max UX: ran .opencode/skills/ui-ux-pro-max/scripts/search.py "animation accessibility z-index loading" --domain ux -n 12. Adopted loading feedback for waits over 300ms, semantic z-index scale, reduced motion, 150-300ms micro-interactions, and no decorative infinite animation. Deferred lazy-loaded media, loading buttons, skeletons, and route-level loading because Task 1.1 has no async page, media, form submit, or router surface yet.
  • Existing UI: only the Phase 0 root shell exists, so this document defines the minimal system before new UI consumes it.

1. Atmosphere & Identity

A bright Korean operations console: dense, calm, and explicit. The signature is cyan as an operational signal, used for primary action, links, focus, and information states while surfaces stay quiet and readable.

2. Color

Primitive Tokens

Role Token Light Usage
Brand 50 --color-brand-50 #F0FBFF Faint emphasis background
Brand 100 --color-brand-100 #D9F6FF Selected row, accent surface
Brand 200 --color-brand-200 #B5EEFF Emphasis border
Brand 300 --color-brand-300 #7CE2FF Decorative low emphasis
Brand 400 --color-brand-400 #36D1FF Secondary accent
Brand 500 --color-brand-500 #00BDF7 Fixed main color and primary background
Brand 600 --color-brand-600 #00A9DE Primary hover
Brand 700 --color-brand-700 #009DCE Primary active
Brand 800 --color-brand-800 #007EA8 Link, focus ring, info
Brand 900 --color-brand-900 #086789 Link hover
Brand 950 --color-brand-950 #063747 Deep brand accent

Semantic Tokens

Role Token Light Usage
Background --background #F6FBFD Page background
Foreground --foreground #102A33 Main text
Card / Popover --card, --popover #FFFFFF Surfaces and overlays
Muted --muted #E9F4F7 Muted surface
Muted foreground --muted-foreground #425F69 Secondary text
Secondary --secondary #E1F5FA Secondary controls
Secondary foreground --secondary-foreground #123E4B Text on secondary
Accent --accent #D9F6FF Hover and selected surface
Accent foreground --accent-foreground #0C566F Text on accent
Border --border #D5E8EE Decorative separators
Input --input #577581 Required control boundary
Primary --primary #00BDF7 Main CTA
Primary foreground --primary-foreground #062B36 Text/icons on primary
Ring / Link --ring, --link #007EA8 / #086789 Focus and link
Info --info #086789 Small informational labels
Success --success #167347 Open/success state
Success surface --success-surface #EAF8F0 Success Badge surface
Warning --warning #9A5B00 Scheduled/warning state
Warning surface --warning-surface #FFF7E6 Warning Badge surface
Destructive --destructive #B42318 Error/destructive state
Inactive --inactive #52636A Inactive state
Inactive surface --inactive-surface #EEF3F5 Inactive Badge surface

Rules

  • Feature components use semantic or component tokens, not raw hex.
  • #00BDF7 never uses white foreground; --primary-foreground is #062B36.
  • Light theme only in this release. No .dark, theme provider, theme toggle, or system dark integration.

3. Typography

Level Size Weight Line Height Usage
Page title 24px 700 1.3 Main page title
Section title 20px 650 1.4 Section headers
Body 14px 400 1.5 Dense admin text and tables
Small 13px 400 1.45 Secondary metadata
Caption 12px 600 1.4 Labels and badges
Mobile input 16px 400 1.5 iOS-safe input text

Primary font stack: Pretendard, Noto Sans KR, Apple SD Gothic Neo, system-ui, sans-serif.

4. Spacing & Layout

  • Base spacing is an 8px grid, with 4px available only for tight icon-label gaps.
  • Control target minimum is 44px.
  • Initial shell remains simple: the document scrolls until Task 1.4 introduces the admin shell.
  • Future desktop shell dimensions follow PRD: 240px sidebar and 56px header.

5. Components

StatusBadge

  • Structure: inline status container with a decorative dot and visible Korean text label.
  • Variants: OPEN, SCHEDULED, INACTIVE.
  • Spacing: --space-1, --space-2.
  • States: static display only in Task 1.1.
  • Accessibility: aria-label="상태: {label}"; color never carries status alone.
  • Motion: none.

IconOnlyAction

  • Structure: button with icon slot, accessible name, and tooltip description.
  • Variants: single icon-only control primitive.
  • Spacing: 44px minimum target, centered icon.
  • States: hover, active, focus-visible, disabled.
  • Accessibility: aria-label, tooltip element, 3:1 control boundary and focus ring tokens. Tooltip text equal to the label is not wired as aria-describedby to avoid duplicate name/description.
  • Motion: 150ms transform/color transition, disabled under reduced motion.

PageState

  • Structure: tokenized card surface for loading, empty, error, and content pass-through states.
  • Variants: loading and empty use role="status"; error uses role="alert" and optional retry button.
  • Accessibility: state meaning is visible Korean copy plus semantic role; retry is a native button.
  • Motion: none.

SearchToolbar

  • Structure: controlled search input with optional filter slot inside a bordered card surface.
  • Variants: generic query only; domain endpoint query names stay outside the primitive.
  • Accessibility: search input has a visible label and mobile-safe input sizing.
  • Motion: none.

ResourcePagination

  • Structure: PageData summary, page-size select, and native previous/next buttons.
  • Variants: disabled previous/next derive from page bounds and hasNext.
  • Accessibility: native controls expose Korean names and preserve keyboard behavior.
  • Motion: none beyond control state color.

ResponsiveResourceList

  • Structure: desktop and mobile rendering slots inside one region; the primitive owns breakpoint visibility only.
  • Variants: none; domain columns, DTOs, and action unions stay in consuming screens.
  • Accessibility: region is named by the caller.
  • Motion: none.

ConfirmDeactivateDialog

  • Structure: modal confirmation dialog with target name, impact copy, cancel, and destructive confirm action.
  • Variants: confirmation only; never a switch replacement.
  • Accessibility: alertdialog, visible title, native buttons.
  • Motion: none.

UnsavedChangesGuard

  • Structure: render-prop guard for route-leave triggers that opens a confirmation dialog only while dirty.
  • Variants: dirty blocks, safe/saved state lets the route callback run immediately.
  • Accessibility: cancel keeps the user in context and returns focus to the trigger.
  • Motion: none.

FileField

  • Structure: controlled File | null field with visible label, native file input, accept guidance, selected filename, and clear button.
  • Variants: domain-neutral only; allowed extensions, MIME, and max bytes are injected by caller policy.
  • Accessibility: label targets the input; description, accept guidance, and error are connected with aria-describedby; clear is a native button.
  • Motion: none.

ImageCropDialog

  • Structure: modal crop surface with preview, output size, directional move buttons, zoom range, reset, cancel, and apply.
  • Variants: caller injects aspect, maxWidth, and noUpscale; domain profile names and GIF exceptions stay outside the primitive.
  • Accessibility: dialog has visible title, keyboard preview controls, range input, and button alternatives. No pointer-only requirement in Phase 1.6.
  • Motion: transform-only preview adjustment. No crop dependency is added; Canvas is used only when generating the final File.

UploadProgress

  • Structure: status label, optional filename, progressbar, and optional cancel/retry buttons.
  • Variants: display-only upload state; no upload client, request adapter, or domain form ownership.
  • Accessibility: progressbar exposes aria-valuenow; cancel/retry are native buttons.
  • Motion: none.

SuccessNotification

  • Structure: one shell-level role="status" message after successful create, update, or deactivate navigation.
  • Variants: success only.
  • Tokens: --success-surface, --success, --border, --radius-lg, --space-3, --space-4.
  • Accessibility: aria-label="작업 성공"; no timer or dismiss control in P3-T3.
  • Motion: none.

AdminAudioPlayer

  • Reference: Plyr audio is the primary surface reference; Media Chrome audio supplies the explicit current/duration and playback-rate anatomy. Runtime extraction on 2026-08-03 found 4452px single-row bars, icon-only media controls, a flexible progress range, compact time/rate values, and no persistent descriptive labels.
  • Structure: native audio element wrapped by one compact control bar ordered play/pause → flexible seek → current/duration → playback rate → volume icon/range, followed by the existing generic error and manual retry block. No image, poster, video viewport, waveform, or settings menu is rendered.
  • Surface: card, border, input, primary, primary-foreground, and existing radius/spacing tokens only. The standard bar is approximately one 44px control high with 412px token spacing; remaining inline space goes to seek before volume.
  • Variants: shared signed-URL player only; it never downloads, autoplays, auto-refetches, or infers signed URL expiry.
  • Responsive: 375px, 768px, and 1280px standard viewports keep a single visual bar. Only constrained containers at 200% zoom may wrap secondary controls; clipping and horizontal overflow are forbidden.
  • Accessibility: player region is named by title, keyboard Space/Enter toggles play, seek/volume use range inputs, speed uses native select, and icon-only controls retain accessible names. Visible 볼륨 and 재생 속도 labels are omitted to match the compact reference while labels remain available to assistive technology.
  • Motion: none.

AudioPlaybackProvider

  • Structure: shared context coordinates active audio by player id so only one player continues at a time.
  • Variants: stores only player ids in React state; signed URLs are not logged, persisted, or passed into the provider.
  • Accessibility: no direct rendered surface.
  • Motion: none.

CommentThread

  • Structure: two-level comment management surface with root comment form, root rows, direct reply expansion, reply form, and edit/delete row actions.
  • Variants: audio target includes languageCode; community target omits it. Replies never expose nested reply controls.
  • Accessibility: forms use visible labels, rows are named articles, reply lists are named regions, and mutation actions are native buttons.
  • Motion: none beyond existing control state color.

6. Motion & Interaction

  • Motion is limited to 150ms micro-interactions for real control state changes.
  • Only transform, opacity, and color changes are used for Task 1.1 primitives.
  • prefers-reduced-motion: reduce disables non-essential transitions and animations.

7. Depth & Surface

Strategy: mixed but restrained. Dense admin surfaces primarily use borders and tonal shifts; shadows are reserved for overlays in later phases.

8. Accessibility Constraints & Accepted Debt

  • Target WCAG 2.2 AA: body text 4.5:1, control boundary/focus indicator 3:1, visible focus on every interactive element.
  • Korean text must keep system fallbacks and inputs must stay at least 16px on mobile.
  • Accepted debt: no component showcase route in Task 1.1 because the requested scope is token/base/component setup only; RTL tests exercise the primitive states.