Files
Auto_Bangumi/DESIGN.md
Estrella Pan 6e8f2f0f35 refactor(webui): 图标换成 @lucide/vue,headlessui 换成 naive-ui,并改用 Vue 3.5 写法
- 30 个文件的 @icon-park/vue-next 图标换成 @lucide/vue;侧栏与移动导航的 RSS 图标
  改用 Lucide Rss,删除 vue-inline-svg 与 public/images/RSS.svg
- ab-modal 用 NModal、ab-bottom-sheet 用 NDrawer、ab-menu 与 ab-status-bar 系统菜单
  用 NDropdown、通知中心用 NPopover、ab-fold-panel 用 NCollapse;公开 props/emits/slots
  不变,dialog 语义、aria-expanded、menuitem 角色与 Esc 关闭由组件补齐;删除 @headlessui/vue
- naive-ui 主题覆盖移到 src/style/naive-theme.ts(abNaiveTheme),新增 Dropdown/Collapse/
  Popover 的 ab-* token;遮罩颜色统一为 --color-overlay
- withDefaults 改为响应式 props 解构;模板引用改用 useTemplateRef;ab-bottom-sheet、
  llm-auth-dialog、ab-list 的 v-model 改用 defineModel;visualViewport 监听用
  onWatcherCleanup 撤销;复制提示改用 useClipboard({ copiedDuring })
2026-10-20 11:45:48 +02:00

78 lines
6.9 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Design
Visual system of the AutoBangumi web UI (Vue 3 + naive-ui + UnoCSS). Source of truth for tokens: `webui/src/style/var.scss` (`:root` light, `.dark` overrides). naive-ui components are themed via the `abNaiveTheme` `GlobalThemeOverrides` in `webui/src/style/naive-theme.ts` (provided by the root `NConfigProvider` in `webui/src/App.vue`), which reference these same CSS custom properties. Icons come from `@lucide/vue`.
## Color
Strategy: **Soft Ink** — near-monochrome ink on soft neutral fills. Color never carries text; it appears only as small marks and on a short list of interactive states.
Where purple is allowed: primary buttons, focus rings, switch/checkbox-on, the active-tab underline, selected states. Where semantic color is allowed: small square status/tag marks (~7–10px, 2–2.5px radius), error borders/text on invalid fields, destructive buttons. Everything else is ink (`--color-text*`) on neutral surfaces.
| Token | Light | Role |
|---|---|---|
| `--color-primary` | `#6C4AB6` | Primary actions, focus, on-states, selection |
| `--color-primary-hover` | `#563A92` | Hover on primary |
| `--color-primary-light` | `#E8DEF8` | Selected/tinted backgrounds |
| `--color-primary-alpha` | `rgba(108,74,182,.2)` | Focus rings |
| `--color-accent` | `#F97316` | Sparse highlight (calendar today) |
| `--color-success` | `#22C55E` | Status marks: healthy/running |
| `--color-danger` | `#EF4444` | Status marks, errors, destructive actions |
| `--color-warning` | `#F59E0B` | Status marks: needs-review/degraded |
| `--color-bg` | `#FAFAFA` | App background |
| `--color-surface` | `#FFFFFF` | Cards, panels, bars |
| `--color-surface-2` | `#F1F4F8` | Filled controls and list rows (borderless) |
| `--color-text` | `#1E293B` | Body text |
| `--color-text-secondary` | `#64748B` | Secondary text |
| `--color-text-muted` | `#94A3B8` | Muted text — decorative only, not for body copy (fails 4.5:1) |
| `--color-border` | `#E2E8F0` | Hairlines (chips, alerts, dividers) |
Dark theme overrides the same custom properties under `.dark` on `<html>`; never hard-code theme-specific colors in components.
Anti-patterns (rejected as AI-generic, do not reintroduce): pastel pill badges with colored text on tinted fills, tinted alert boxes with icons, icon-in-a-rounded-square empty states, floating-thumb segmented controls, glowing status dots.
## Typography
One family: `Inter, -apple-system, 'Noto Sans SC', 'Microsoft YaHei', system-ui, sans-serif` (`--font-family`). Fixed rem scale, tight ratio; no display face. CJK fallback quality is a requirement, not an afterthought.
Data text — feed sources, timestamps, episode markers (E08), speeds, hashes — sets in `--font-mono` with `font-variant-numeric: tabular-nums`. Content titles (CJK) may carry weight (600–650); UI labels stay at 500 and below.
## Spacing & Layout
Mobile-first tokens: `--layout-padding` 12→14→16px, `--layout-gap` 10→12px across breakpoints (`forTablet`/`forDesktop` SCSS mixins). Bars: `--topbar-height` 48px, `--nav-height` 56px, `--touch-target` 44px. Desktop: sidebar + content; mobile: bottom nav.
## Radius, Shadow, Motion
- Radius scale (Soft Ink): `--radius-sm` 4px (all controls) · `--radius-md` 8px (cards, menus, modals) · `--radius-lg` 12px (sheets) · `--radius-xl` 16px · `--radius-full` (switch rail only).
- Shadows: `--shadow-sm/md/lg`, all soft black at ≤10% alpha; no colored glows.
- Motion: `--transition-fast` 150ms / `--transition-normal` 200ms / `--transition-slow` 300ms, all `ease-out`. Motion conveys state only.
## Z-index
Semantic scale only: `--z-dropdown` 10 · `--z-sticky` 20 · `--z-fixed` 30 · `--z-modal-backdrop` 40 · `--z-modal` 50 · `--z-popover` 60 · `--z-tooltip` 70. Never arbitrary values.
## Components
- **Universal `ab-*` layer** (`src/components/basic/`): pages and composites use only `ab-*` components — never raw `NButton`/`NInput`/bare styled `<button>`. Where naive-ui fits, the `ab-*` component wraps it (`ab-select`, `ab-switch`, `ab-tooltip`, `ab-modal`, `ab-bottom-sheet`, `ab-menu`, `ab-fold-panel`); where it doesn't, the component is custom on tokens (`ab-button`, `ab-tag`).
- **Composites**: app-specific `ab-*` components (`ab-add-rss`, `ab-edit-rule`, calendar pieces, layout shells) compose the universal layer + tokens.
- **Utility CSS**: UnoCSS (`presetWind3`), utilities go in `class` (no attributify); prefer token-backed values over arbitrary literals.
- Every interactive component ships all states: default, hover, focus-visible, active, disabled, loading, error — and ≥44px touch targets on mobile (`--touch-target`).
- Soft Ink component grammar: filled controls on `--color-surface-2` (border only on focus/error); tags are hairline chips with a square semantic mark and ink text; status is a square mark + ink label; tabs underline (no thumb); alerts are hairline outlines led by a bold colored word (no icons, no tinted fills); toasts are inverted ink chips; empty states are left-aligned prose with one action; loading is skeletons, not spinners-in-content.
- Every universal component ships a Storybook story and (where it has behavior) a vitest test in `__tests__/`.
The universal set (`src/components/basic/` unless noted):
| Group | Components |
|---|---|
| Actions | `ab-button` (primary/secondary/ghost/danger·sm/md·loading), `ab-icon-button` (required label, 44px touch, ghost/solid/round), `ab-menu` (naive-ui NDropdown), `ab-split-button` |
| Forms | `ab-field` (label/description/error/required + aria wiring via injection), `ab-input` (affixes, clearable, password reveal, error), `ab-select`, `ab-switch`, `ab-segmented` (underline tabs), `ab-setting` (root — field+control settings row) |
| Overlays | `ab-modal` (adaptive: dialog ≥ tablet, bottom sheet on mobile; footer slot; sizes sm/md/lg), `useConfirm()` + `ab-confirm-host` (promise confirm, danger variant), `ab-tooltip`, `ab-bottom-sheet` (internal to ab-modal) |
| Display | `ab-tag` (semantic marks: success/warning/danger/info/neutral, closable), `ab-status` (running/stopped/paused/degraded + label/detail), `ab-badge` (count/dot, 99+), `ab-container` (root — title/footer/flush card), `ab-fold-panel` (root — accessible, animated), `ab-progress` |
| Data & feedback | `ab-list` (selection, loading skeleton, density comfortable/compact, empty slot), `ab-toolbar` (search/filters/actions), `ab-alert` (hairline, bold lead word), toast via `useMessage()` (inverted ink), `ab-empty`, `ab-skeleton` (lines/row/card), `ab-pull-refresh` |
| Page chrome | `ab-page-title`, `ab-search`, `ab-status-bar` (root), layout shells |
Retired (do not reintroduce): `ab-popup`, `ab-adaptive-modal`, `ab-label`, `ab-setting`'s type-switch-only API, `ab-add`, `ab-button-multi`, `ab-data-list`, `ab-image`, `ab-rule`, `ab-swipe-container`, and the `ab-input` UnoCSS attribute shortcut.
## Voice
UI copy through i18n only (`src/i18n/en.json` + `zh-CN.json`, keys always added in pairs). Sentence case in English. Labels are nouns, buttons are verbs, errors say what to do next.