Files
testing-tool/docs/VISUAL_STYLE_GUIDE.md
LingandRX f9821553b3 chore: merge develop into main (#56)
* fix(ci): fix release workflow artifact upload issue

- Split artifact upload into 3 separate steps (Chrome, Firefox, Source)
- Update download steps to match new artifact names
- Add if-no-files-found: error for better error handling
- Fix Node.js 20 compatibility issue with upload-artifact@v4 glob pattern

* docs: 更新 AGENTS.md,添加测试工具和翻译文件结构说明

* docs: 更新 AGENTS.md,完善 CI 步骤和存储键名格式说明

* fix: remove unnecessary animations from StorageCleaner page

- Remove entrance animations (animate-in, fade-in, zoom-in) from all components
- Remove scale effect (active:scale-[0.99]) from clean button
- Remove transition effects (transition-all, transition-colors) from OptionItem, StorageOptionsGrid, AutoRefreshToggle
- Remove bouncing animation from error icon
- Remove pulsing animation from warning banner
- Keep animate-spin on button loading spinner as functional indicator

* fix: remove unnecessary animations from RightClickRestorer page

* fix: remove all unnecessary animations from all pages

- Remove dead code animations (animate-in, fade-in, slide-in, zoom-in, shake) from tailwindcss-animate plugin (not installed)
- Remove decorative transition effects (transition-all, transition-colors) from all page components
- Remove scale effects (active:scale-95) from buttons
- Remove bounce animations (animate-bounce) from icons
- Keep functional animate-spin on loading spinners as they provide essential loading feedback

Affected pages: Dashboard, Timestamp, Jwt, JsonTools, Base64Converter, HtmlToMarkdown, MarkdownToHtml, QrCode, TextStatistics

* docs: 添加项目文档(copilot-instructions、编码规范、各目录 README)

- 新增 .github/copilot-instructions.md:Copilot 指令文件,涵盖构建命令、CI 流水线、项目架构、关键规范
- 新增 .github/CODING_STANDARDS.md:完整的代码编写规范文档(TypeScript、React、样式、错误处理、命名、测试、Hook、i18n、存储等 12 个章节)
- 新增 11 个目录的 README.md:components、components/ui、config、entrypoints、i18n、lib、pages、providers、public、src、types、utils
- 更新 AGENTS.md:补充页面组件模式说明和 components/ui 目录

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(CopyButton): use shadcn buttonVariants instead of duplicated variant classes

Remove hand-written variantClasses/sizeClasses that duplicated shadcn's
buttonVariants. Now extends ButtonProps and imports buttonVariants from
components/ui/button for consistent styling.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ErrorBoundary): use shadcn destructive tokens instead of hardcoded red colors

Replace hardcoded red-200/red-50/red-600 with border-destructive,
bg-destructive/5, text-destructive. Use variant='destructive' on
Button. Align error log styling with PageErrorBoundary.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(TextInputArea): replace hardcoded Chinese strings with i18n t() calls

Use existing translation keys for clear success, copy success, and
copy error messages instead of hardcoded Chinese text.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(QrCodePreview): use shadcn Button instead of manual button styles

Replace hand-written button class strings with shadcn Button component
using outline and default variants. Removes ~15 lines of duplicated
Tailwind classes.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* docs(GlobalSnackbar): clarify relationship with sonner toast

Add note explaining when to use GlobalSnackbar (provider context,
severity levels, custom positioning) vs sonner toast (simple one-off
messages).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ImageUploader): wrap handleClearFile with useCallback

Prevents unnecessary re-renders by memoizing the callback, consistent
with handleFileChange which already uses useCallback.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(SwitchButtonGroup): remove redundant containerPadding conditional

Both branches of the ternary returned 'p-1'. Inline the constant
directly into the className string.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(TopBar): add missing openExtensionPage import

The function was called in handleOpenInTab but never imported from
utils/chromeTabs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(TopBar): add selectedIndex boundary protection in keyboard nav

Guard against out-of-bounds access when search results change during
keyboard navigation. Check selectedIndex < totalItems before accessing
arrays.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(RouterContainer): remove unnecessary empty-dep useMemo

getEntryPointType() is a simple config getter. Replace useMemo(() => ..., [])
with a direct call — the empty dep array made the memo pointless.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat(i18n): add missing translation keys for error boundaries and copy messages

Add keys for errorBoundary, pageErrorBoundary, router, and
messages.copyEmpty in both zh and en locales.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(CopyButton): replace hardcoded Chinese strings with i18n t() calls

Use useTranslation('common') for tooltip, copy empty/success/error
toast messages.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(ErrorBoundary): replace hardcoded Chinese with i18n via withTranslation HOC

Use react-i18next withTranslation HOC for class component to translate
title, description, and refresh button text.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(PageErrorBoundary): replace hardcoded Chinese with i18n via withTranslation HOC

Use react-i18next withTranslation HOC for class component to translate
title, description, and retry button text.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(RouterContainer): replace hardcoded Chinese with i18n t() calls

Use useTranslation('common') for 404 page title and description with
entryPointType interpolation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(pages): 修复 TypeScript 错误、硬编码字符串,移除死代码

- 修复 LiveClock.tsx CopyButton size 属性类型错误 (small → sm)
- 修复 ToolCard.tsx 紫色 RGB 值拼写错误 (147,51,2 purple → 147,51,232)
- 替换 4 处硬编码中文为 i18n 调用 (StorageCleaner, JsonTools)
- MarkdownToHtml 预览链接色改为 CSS 变量以支持暗黑模式
- 移除 Base64Converter 中已被 Base64ConverterSection 替代的死代码
  (FileMode.tsx, ImageMode.tsx 及其测试文件)
- 更新 README.md 移除对已删除文件的引用

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix(CopyButton, LiveClock): 修改按钮大小为 'icon',移除硬编码颜色配置

* refactor(TextInputArea): 复用 CopyButton 组件替换内联复制逻辑

* fix(test): 修复 CopyButton mock 未捕获 clipboard writeText 异常

* refactor: 迁移 i18n 系统从 react-i18next 到 chrome.i18n

- 移除 react-i18next、i18next 及相关依赖
- 删除旧的 i18n/ 目录和 useLazyTranslation 工具
- 新增 utils/chromeI18n.ts 类型安全 wrapper(useI18n Hook + getMessage)
- 生成 public/_locales/{zh,en}/messages.json(298 个翻译 key)
- 批量更新 39+ 组件文件的导入和翻译调用
- 转换翻译键格式:namespace:key → namespace_key
- 修复 ErrorBoundary/PageErrorBoundary 从 withTranslation HOC 改为直接调用 getMessage
- 更新 vitest.setup.ts mock 加载实际翻译文本
- 修复 11 个测试文件的断言以匹配中文翻译
- TypeScript、ESLint、547 项测试全部通过

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: 移除语言切换按钮

chrome.i18n 语言由浏览器设置决定,不支持运行时切换,移除 TopBar 中的语言切换按钮及相关逻辑。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: 移除英文翻译,仅保留中文

删除 public/_locales/en/ 目录,清理 chromeI18n.ts 中未使用的语言切换相关导出。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* chore: 清理未使用的 i18n key(322 → 245)

移除 77 个未被源码引用的翻译 key,包括:
- jsonFormat_* (17): 未接入的子模式标题和按钮
- buttons_* (15): 代码使用 common: 命名空间格式,直接 key 未引用
- qrCode_* (14): 未使用的页面标题和状态提示
- timestamp/common/storageCleaner/jsonDiff 等零散未用 key

保留了通过模板字面量动态引用的 key(storageCleaner_options_*、common_buttons_themeMode_*、jsonFormat_*InputPlaceholder 等)。

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: reorganize directory structure into src/

Move all source code directories into src/ for cleaner project structure:
- pages/, components/, utils/, config/, providers/, types/, lib/, assets/, entrypoints/ → src/
- Use WXT srcDir config to resolve @/ alias to src/
- Update tsconfig, vitest, eslint, tailwind configs
- Remove scattered README.md files from subdirectories
- Update documentation (AGENTS.md, CODING_STANDARDS.md, README.md)
- Fix pre-existing lint error in RouterProvider.tsx

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: remove meaningless comments

Remove 50+ noise comments across the codebase:
- AI-generated verbose prose (💡 emoji, '超进化', '大闸', etc.)
- Comments that restate what the code obviously does
- Comments about deleted code
- Import-level noise comments
- Overly verbose Chinese section markers

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* refactor: clean up unused code and simplify imports

- Remove unused imports and variables across 35 files
- Simplify component logic and remove dead code
- Clean up test files by removing unnecessary setup
- Streamline CI workflow configuration

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: remove HTML-Markdown conversion features (htmlToMarkdown & markdownToHtml)

Removed both conversion tools and all related code:
- Deleted page components: HtmlToMarkdown, MarkdownToHtml
- Deleted utility functions: htmlToMarkdown.ts, markdownToHtml.ts
- Deleted unit tests for both utilities
- Removed feature configs and PageType entries from storage.d.ts
- Removed preview mode types and StorageSchema keys
- Removed all i18n translation keys from messages.json
- Removed vendor-markdown chunk from wxt.config.ts
- Removed marked dependency from package.json
- Updated feature count in tests (11 -> 9, page order 10 -> 8)
- Updated README, AGENTS.md, and copilot-instructions.md

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: redesign Dashboard layout with search and recently used tools

- Add search input at top to filter tools by name/description
- Add 'recently used' section showing last 3 used tools as compact chips
- Add 'all tools' section header for the tool card grid
- Track tool usage via new 'app/recentlyUsedTools' storage key
- Update navigateTo to record recently used tools (max 3, LRU order)
- Add i18n keys: dashboard_searchPlaceholder, dashboard_recentlyUsed, dashboard_allTools

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: replace ToolCard grid with compact icon+name grid on Dashboard

- Replace large ToolCard layout with dense grid (3-6 columns responsive)
- Each item shows icon + tool name in a compact tile
- Hover highlights icon and text with smooth transitions
- Remove ToolCard import from Dashboard (component retained for reuse)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: remove redundant search input from Dashboard (header already has one)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: optimize TopBar search with Cmd+K shortcut and improved styling

- Add global Cmd/Ctrl+K keyboard shortcut to focus search input
- Show keyboard shortcut hint (⌘K) in search input when empty
- Improve input styling: rounded-lg, softer borders, focus transitions
- Improve dropdown: rounded-lg, larger shadow, better spacing
- Larger result icons (h-8 w-8) with rounded-lg containers
- Refined history section with uppercase label and muted icon
- Search icon fades in on focus via group-focus-within

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* feat: improve search history to show recently used tools with direct navigation

- Store feature keys (PageType) in history instead of translated labels
- Show icon + name + description in history items (same as search results)
- Click or Enter on history item navigates directly to the tool page
- Remove History icon import (no longer needed)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* fix: useI18n fallback for empty string translations from chrome.i18n

* refactor: 移除设置页面

- 删除 options 入口点目录及相关文件
- 移除 wxt.config.ts 中 options_ui 配置
- 移除 TopBar 组件中的设置按钮和 onOpenOptions prop
- 移除 popup/sidepanel App.tsx 中的 handleOpenOptions
- 移除相关测试中的设置页面引用
- 移除 messages.json 中设置相关翻译键
- 通过 lint、typecheck、test 验证

* docs: 更新文档,移除设置页面引用

- README.md: 移除 entrypoints/options/ 目录说明
- AGENTS.md: 更新项目结构描述,移除 options/ 入口点

* docs: add comprehensive page development standards (CODING_STANDARDS.md §11)

- 新增 §11 页面开发规范(10 个子节),覆盖目录结构分4级、入口组件命名统一为 Index、业务 Hook 内部结构顺序、常量/子组件/存储键/模式切换/布局约定等规范
- 新增 §11.9 页面开发检查清单(10 步逐项确认)
- 更新 AGENTS.md 和 copilot-instructions.md 同步引用新规范章节
- 修正 useLazyTranslation → useI18n 与实际代码保持一致

* refactor(pages): apply UI+Hook separation and unify component naming

- Dashboard, RightClickRestorer: rename page component to Index
- Jwt: extract useJwt.ts hook + JwtSection.tsx sub-component (127→76 lines)
- TextStatistics: extract useTextStatistics.ts hook (63→48 lines)
- JsonTools: extract useJsonTools.ts hook + constants.ts (199→109 lines)
- All pages now follow CODING_STANDARDS.md §11 pattern

* refactor(StorageCleaner): improve style consistency and checkbox alignment

- Group storage options, auto-refresh toggle, and clean button into unified card
- Remove card wrappers from StorageOptionsGrid and AutoRefreshToggle (now sections within parent card)
- Align OptionItem and Select All checkboxes with consistent px-3.5 padding
- Distinguish indeterminate vs checked checkbox states visually
- Standardize spacing (space-y-4), add select-none to page root
- Fix import path for StorageCleanerConfirm to relative ./

* fix(StorageCleaner, Base64Converter): fix layout padding and checkbox alignment

- StorageCleaner: add px-3.5 to grid container to prevent content sticking to border
- StorageCleaner: adjust select-all bar to pl-3.5 pr-7 for checkbox alignment with grid items
- Base64Converter: add px-2 to TextMode and Base64ConverterSection for consistent spacing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* chore: add initial CLAUDE.md file with reference to @AGENT.md

* docs: add comprehensive visual style guide

Add VISUAL_STYLE_GUIDE.md to standardize UI development:
- Design principles and keywords
- Complete color system (CSS variables, semantic usage)
- Typography hierarchy and patterns
- Spacing, layout, and container specs
- Border radius and shadow system
- Component specs (Button, Input, Card, Dialog, etc.)
- Interaction and animation guidelines
- Dark mode implementation rules
- Tool color palette and assignment
- Code organization standards
- Anti-patterns checklist
- Quick reference appendix

Update .gitignore to allow tracking the style guide.

* docs(AGENTS): update i18n section to reflect chrome.i18n migration

- Replace react-i18next docs with chrome.i18n API documentation
- Update translation file paths from i18n/locales/ to public/_locales/
- Update test mock description for chromeI18n
- Remove references to useLazyTranslation, vendor-i18n chunk, and snackbar

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(content): simplify content script structure

- Remove messageHandler.ts (6-line pass-through facade)
- Import contextMenuHandler directly from content.ts
- Replace local getI18nText() with shared getMessage() from utils/chromeI18n

Reduces content script files from 3 to 2 and eliminates i18n logic duplication.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(RightClickRestorer): simplify setDomain logic

Replace nested if/try/catch with a single ternary expression.
isUnsupportedPage() already handles invalid URLs, so new URL() is safe here.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(background): extract main world injection script

Move the 62-line inline main world injection function from background.ts
to a dedicated utils/rightClickInjection.ts file.

This separates the RightClickRestorer's DOM patching logic from the
service worker's message routing concerns.

- background.ts: -62 lines, now delegates to imported function
- utils/rightClickInjection.ts: new file with mainWorldInjectionScript()

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(background): simplify RELOAD_TAB with setTimeout

Replace the 30-line chrome.alarms API implementation with a simple
setTimeout for delayed tab reloads.

Rationale: The only usage is a 1-second delay after storage cleaning.
Service workers don't go idle within 1 second, so alarms' persistence
benefit is unnecessary here.

- Removed: alarm naming, create/clear, listener register/unregister
- Added: 4-line setTimeout conditional

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(storageCleaner): extract executeScript helpers

Extract two generic helpers to eliminate repetitive executeScript boilerplate:

- runScript<T>(): for size/query operations (5 functions)
- runCleanScript(): for cleanup operations (5 functions)

Also add CLEAN_OPTION_KEYS constant shared by formatCleaningResult
and isEmptyResult.

- 428 lines → 379 lines (-49 lines)
- Eliminates ~100 lines of duplicated try/catch/result parsing

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(base64Converter): simplify utility functions

- Mark formatFileSize() as @deprecated, delegate to formatBytes
- Replace manual for-loop in base64ToBytes() with Uint8Array.from()
- Replace nested loops in sniffMimeFromBytes() with Array.every()

5 insertions(+), 23 deletions(-)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: replace GlobalSnackbar with sonner toast

Remove the custom 373-line GlobalSnackbar component and its Provider/Hook
in favor of the already-installed sonner library.

Changes:
- Delete src/components/GlobalSnackbar.tsx (+ tests)
- Remove SnackbarProvider from popup/App.tsx and sidepanel/App.tsx
- Replace useSnackbar() calls with toast.success()/toast.error() in:
  - ImageUploader.tsx
  - useQrCode.ts
  - ParsePanel.tsx
  - LiveClock.tsx
- Update test mocks to use sonner instead of GlobalSnackbar

-554 lines, +43 lines (net -511 lines)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(RouterContainer): remove unnecessary useMemo and hoist constant

- Replace useMemo with direct expression for animationClass
- Move entryPointType to module scope (value is fixed after mount)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: remove unnecessary useMemo calls across components

Replace over-cached simple computations with direct expressions:

- popup/App.tsx: getEntryPointType() and routerConfig (values are stable)
- Dashboard/index.tsx: visibleSet, visibleFeatures, recentFeatures
  (Set creation + array filter/map are cheaper than useMemo overhead)
- useTextStatistics.ts: getTextStats(text) (simple string processing)
- ThemeModeProvider.tsx: contextValue object (setMode is stable via useCallback)

-39 lines, +28 lines (net -11 lines)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: unify formatBytes usage and simplify useContextMenuData

1. Remove formatSize/formatByteSize delegation functions:
   - storageCleaner.ts:formatSize (was just formatBytes wrapper)
   - textStatistics.ts:formatByteSize (was just formatBytes wrapper)
   - Update all callers to import formatBytes directly from @/utils/format

2. Simplify useContextMenuData hook:
   - Remove unnecessary useCallback wrapping
   - Inline checkAndConsumeData logic directly in useEffect
   - Eliminate callback->effect dependency cycle

-69 lines, +67 lines (net -2 lines, but removes 2 indirection layers)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(storageCleaner): simplify architecture and improve type semantics

- Remove RELOAD_TAB message chain, use chrome.tabs.reload() directly
- Rename IndexedDB label to '站点存储' for accuracy
- Introduce StorageSizeInfo type to distinguish bytes vs count
- Rename totalSize to totalBytes for clarity
- Merge runCleanScript into runScript to reduce duplication
- Rename CleaningResult.success to overallSuccess to avoid confusion
- Remove unused domain state and setDomain call

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor: replace require with import for JSON module in vitest.setup.ts

* refactor: simplify Timestamp and RightClickRestorer

Timestamp:
- Extract shared utilities (msToUnit, dayjsFromTimestamp, ModeType)
- Remove unnecessary useCallback/useMemo/React.memo
- Remove extra info section (relative time, ISO 8601, UTC)
- Fix missing translation key timestamp_unitS

RightClickRestorer:
- Remove website badge (右键已解锁 overlay)
- Remove mouse penetration logic for media elements
- Remove unnecessary useCallback
- Remove redundant setIsLoading call

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* refactor(StorageCleanerConfirm.test.tsx): update IndexedDB label to Chinese translation

---------

Co-authored-by: Ubuntu <ubuntu@localhost.localdomain>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-29 23:00:21 +08:00

872 lines
26 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.
# Testing Tools — 视觉规范文档
> **版本**: 1.0.0
> **日期**: 2026-05-29
> **适用范围**: 所有新页面、新组件、UI 修改
> **设计系统**: 基于 [shadcn/ui](https://ui.shadcn.com/) + Tailwind CSS
---
## 目录
1. [设计原则](#1-设计原则)
2. [色彩系统](#2-色彩系统)
3. [排版规范](#3-排版规范)
4. [间距与布局](#4-间距与布局)
5. [圆角与阴影](#5-圆角与阴影)
6. [组件规范](#6-组件规范)
7. [交互与动效](#7-交互与动效)
8. [暗色模式](#8-暗色模式)
9. [工具色彩标识](#9-工具色彩标识)
10. [代码规范](#10-代码规范)
11. [反模式清单](#11-反模式清单)
---
## 1. 设计原则
### 1.1 核心定位
Testing Tools 是一款**浏览器扩展开发者工具集**,视觉风格遵循:
- **专业克制** — 低饱和度色彩,避免视觉噪音
- **信息密度优先** — 紧凑布局,在 400×600px 的 popup 空间内高效展示
- **开发者友好** — 等宽字体用于代码/数据,清晰的信息层级
- **一致性至上** — 所有页面、组件遵循同一套视觉语言
### 1.2 设计关键词
```
简洁 · 现代 · 功能导向 · 低对比度 · 微圆角 · 微妙阴影
```
### 1.3 与 shadcn/ui 的关系
本项目以 shadcn/ui 为底座,所有基础组件(Button、Input、Select 等)均来自或对齐 shadcn/ui 的默认样式。业务组件在此基础上扩展,**不得破坏底层设计语言的统一性**。
---
## 2. 色彩系统
### 2.1 CSS 变量定义
所有色彩通过 CSS 自定义属性(HSL 格式)管理,定义于 `src/index.css`
#### 亮色模式 (`:root`)
| 变量名 | HSL 值 | 用途 | 近似色 |
| -------------------------- | ------------------- | ------------- | --------- |
| `--background` | `0 0% 100%` | 页面背景 | `#ffffff` |
| `--foreground` | `222.2 84% 4.9%` | 主文字 | `#020617` |
| `--card` | `0 0% 100%` | 卡片背景 | `#ffffff` |
| `--card-foreground` | `222.2 84% 4.9%` | 卡片文字 | `#020617` |
| `--popover` | `0 0% 100%` | 浮层背景 | `#ffffff` |
| `--popover-foreground` | `222.2 84% 4.9%` | 浮层文字 | `#020617` |
| `--primary` | `222.2 47.4% 11.2%` | 主按钮/强调 | `#0f172a` |
| `--primary-foreground` | `210 40% 98%` | 主按钮文字 | `#f8fafc` |
| `--secondary` | `210 40% 96.1%` | 次级背景 | `#f1f5f9` |
| `--secondary-foreground` | `222.2 47.4% 11.2%` | 次级文字 | `#0f172a` |
| `--muted` | `210 40% 96.1%` | 静音/禁用背景 | `#f1f5f9` |
| `--muted-foreground` | `215.4 16.3% 46.9%` | 次要文字 | `#64748b` |
| `--accent` | `210 40% 96.1%` | 悬停高亮 | `#f1f5f9` |
| `--accent-foreground` | `222.2 47.4% 11.2%` | 悬停文字 | `#0f172a` |
| `--destructive` | `0 84.2% 60.2%` | 错误/删除 | `#ef4444` |
| `--destructive-foreground` | `210 40% 98%` | 错误文字 | `#f8fafc` |
| `--border` | `214.3 31.8% 91.4%` | 边框 | `#e2e8f0` |
| `--input` | `214.3 31.8% 91.4%` | 输入框边框 | `#e2e8f0` |
| `--ring` | `222.2 84% 4.9%` | 焦点环 | `#020617` |
| `--radius` | `0.5rem` | 全局圆角 | `8px` |
#### 暗色模式 (`.dark`)
暗色模式下所有变量自动反转,保持对比度关系:
| 变量名 | HSL 值 | 近似色 |
| ---------------------- | ------------------- | --------- |
| `--background` | `222.2 84% 4.9%` | `#020617` |
| `--foreground` | `210 40% 98%` | `#f8fafc` |
| `--primary` | `210 40% 98%` | `#f8fafc` |
| `--primary-foreground` | `222.2 47.4% 11.2%` | `#0f172a` |
| `--secondary` | `217.2 32.6% 17.5%` | `#1e293b` |
| `--muted` | `217.2 32.6% 17.5%` | `#1e293b` |
| `--border` | `217.2 32.6% 17.5%` | `#1e293b` |
### 2.2 使用规范
```tsx
// ✅ 正确:使用 CSS 变量
<div className="bg-background text-foreground border-border">
// ✅ 正确:使用语义化色彩名
<Button className="bg-primary text-primary-foreground">
<span className="text-muted-foreground">
// ❌ 错误:硬编码颜色值
<div className="bg-white text-black">
<div className="bg-[#f1f5f9]">
```
### 2.3 语义化色彩使用场景
| 色彩 | 场景 |
| -------------------------- | -------------------------------- |
| `background` | 页面根背景 |
| `foreground` | 主标题、正文 |
| `muted-foreground` | 描述文字、占位符、次级标签 |
| `border` | 卡片边框、分割线、输入框边框 |
| `card` + `card-foreground` | 卡片容器及其内容 |
| `primary` | 主按钮、选中状态、关键操作 |
| `secondary` | 次级按钮、工具栏背景、标签页背景 |
| `destructive` | 错误提示、删除操作、验证失败 |
| `accent` | 悬停背景、下拉选中项 |
---
## 3. 排版规范
### 3.1 字体栈
项目使用系统默认字体栈(Tailwind 默认),**不引入自定义字体**。
```css
/* Tailwind 默认 sans-serif */
font-family:
ui-sans-serif,
system-ui,
-apple-system,
BlinkMacSystemFont,
'Segoe UI',
Roboto,
'Helvetica Neue',
Arial,
sans-serif;
/* 等宽字体用于代码/数据 */
font-family:
ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
```
### 3.2 字号层级
| 层级 | 类名 | 大小 | 字重 | 用途 |
| --------- | ------------------------------------------------ | ---- | ------- | ---------------------- |
| 页面标题 | `text-base font-bold` | 16px | 700 | 页面主标题(极少使用) |
| 卡片标题 | `text-sm font-bold tracking-tight` | 14px | 700 | 卡片/区块标题 |
| 正文 | `text-sm` | 14px | 400 | 普通正文 |
| 次级文字 | `text-xs` | 12px | 400/500 | 描述、标签 |
| 微标签 | `text-[10px] font-bold uppercase tracking-wider` | 10px | 700 | 区域标签、分类标题 |
| 数据/代码 | `font-mono text-sm` | 14px | 400 | 时间戳、JSON、代码 |
### 3.3 排版模式
```tsx
// 区域标签(Section Label)— 最常用
<span className="text-[10px] font-bold text-muted-foreground/90 uppercase tracking-wider">
输出结果
</span>
// 卡片标题
<h4 className="font-bold text-sm tracking-tight text-foreground leading-snug">
标题文字
</h4>
// 描述文字
<p className="text-[11px] font-medium text-muted-foreground/90 leading-normal">
描述内容
</p>
// 数据展示
<span className="font-mono font-bold text-foreground text-sm tracking-tight tabular-nums">
1716950400000
</span>
```
### 3.4 行高与字间距
| 属性 | 值 | 场景 |
| ----------------- | -------- | ------------------ |
| `leading-none` | 1 | 单行数据、紧凑布局 |
| `leading-snug` | 1.375 | 标题、短文本 |
| `leading-relaxed` | 1.625 | 长文本、代码块 |
| `tracking-tight` | -0.025em | 标题、数据 |
| `tracking-wider` | 0.05em | 大写标签 |
---
## 4. 间距与布局
### 4.1 容器尺寸
```tsx
// Popup 模式(默认)
<div className="w-[400px] max-w-[400px] min-w-[400px] h-[600px] min-h-[600px]">
// Tab 模式(全屏自适应)
<div className="sm:w-screen sm:max-w-none sm:min-w-0 sm:h-screen sm:min-h-0">
```
### 4.2 间距节奏
| Token | 值 | 使用场景 |
| --------------- | ----------- | ------------------ |
| `p-3` / `p-3.5` | 12px / 14px | 页面内边距(紧凑) |
| `p-4` | 16px | 标准页面内边距 |
| `p-5` | 20px | 卡片内部填充 |
| `gap-2` | 8px | 紧凑元素间距 |
| `gap-3` | 12px | 标准元素间距 |
| `gap-4` | 16px | 区块间距 |
| `gap-6` | 24px | 大区块间距 |
### 4.3 布局模式
#### 页面布局
```tsx
// 标准页面结构
<div className="p-4 w-full flex flex-col space-y-4 min-h-[500px] select-none">{/* 页面内容 */}</div>
```
#### 卡片布局
```tsx
// 标准卡片
<div className="p-5 rounded-xl border border-border bg-card text-card-foreground shadow-sm">
{/* 卡片内容 */}
</div>
// 可聚焦卡片(含焦点环)
<div className="border border-border rounded-xl bg-card ... focus-within:ring-1 focus-within:ring-ring focus-within:border-ring">
```
#### 双栏网格
```tsx
// 响应式双栏
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 items-stretch">
```
#### 工具卡片网格(Dashboard
```tsx
// Dashboard 紧凑网格
<div className="grid grid-cols-3 sm:grid-cols-4 md:grid-cols-5 lg:grid-cols-6 gap-2">
```
---
## 5. 圆角与阴影
### 5.1 圆角体系
| Token | 值 | 使用元素 |
| -------------- | ------ | ---------------------- |
| `rounded-sm` | 2px | Checkbox、小标签 |
| `rounded-md` | 6px | 按钮、输入框、Select |
| `rounded-lg` | 8px | 搜索框、小卡片 |
| `rounded-xl` | 12px | 大卡片、面板、图标容器 |
| `rounded-full` | 9999px | 标签、Avatar |
### 5.2 阴影体系
| 级别 | 类名 | 用途 |
| ---- | --------------------- | ---------------------- |
| 无 | — | 静态元素 |
| 低 | `shadow-sm` | 卡片、输入框、按钮 |
| 中 | `shadow-lg` | 下拉菜单、浮层、Dialog |
| 动态 | 自定义 `shadow-[...]` | 卡片悬停时的彩色阴影 |
### 5.3 彩色阴影规范(工具卡片专用)
```tsx
// 工具卡片悬停阴影 — 必须使用 rgba 格式配合 CSS 变量
className="hover:shadow-[0_8px_24px_-8px_rgba(var(--tool-color),0.14)]
dark:hover:shadow-[0_8px_30px_-10px_rgba(var(--tool-color),0.25)]"
```
---
## 6. 组件规范
### 6.1 Button
来源:`src/components/ui/button.tsx`
#### 变体
| 变体 | 类名 | 场景 |
| ------------- | -------------------------------------------- | ------------------ |
| `default` | `bg-primary text-primary-foreground` | 主操作 |
| `destructive` | `bg-destructive text-destructive-foreground` | 删除、危险操作 |
| `outline` | `border border-input bg-background` | 次级操作、取消 |
| `secondary` | `bg-secondary text-secondary-foreground` | 次要操作 |
| `ghost` | 仅悬停背景 | 图标按钮、低优先级 |
| `link` | 下划线文字 | 跳转链接 |
#### 尺寸
| 尺寸 | 高度 | 内边距 | 场景 |
| --------- | ------- | ----------- | -------- |
| `default` | 40px | `px-4 py-2` | 标准按钮 |
| `sm` | 36px | `px-3` | 紧凑按钮 |
| `lg` | 44px | `px-8` | 突出按钮 |
| `icon` | 40×40px | — | 图标按钮 |
#### 使用示例
```tsx
// 主操作
<Button>确认</Button>
// 图标按钮
<Button variant="ghost" size="icon">
<Copy className="h-4 w-4" />
</Button>
// 危险操作
<Button variant="destructive" size="sm">删除</Button>
```
### 6.2 Input
来源:`src/components/ui/input.tsx`
```tsx
// 标准输入框
<Input
className="font-mono font-semibold h-10 shadow-sm placeholder:text-muted-foreground/60 focus:bg-background"
/>
// 错误状态
<Input
className="border-destructive focus-visible:ring-destructive"
/>
```
**规范要点**
- 高度统一为 `h-10`40px
- 等宽字体用于数据输入
- 占位符使用 `text-muted-foreground/60`
- 错误时边框变红并调整焦点环
### 6.3 SwitchButtonGroup
来源:`src/components/ui/switch.tsx`
```tsx
// 分段控制器
<SwitchButtonGroup
value={mode}
options={[
{ value: 'ts2dt', label: '转日期' },
{ value: 'dt2ts', label: '转时间戳' },
]}
onChange={setMode}
size="small"
/>
```
**规范要点**
- 容器:`rounded-lg bg-muted p-1`
- 选中项:`bg-background text-foreground shadow-sm font-semibold`
- 未选中项:`hover:bg-background/50 hover:text-foreground/80`
- 尺寸:`small`32px)用于工具页,`medium`36px)标准
### 6.4 Card(工具卡片)
来源:`src/pages/Dashboard/ToolCard.tsx`
```tsx
// 标准工具卡片结构
<div className="group relative rounded-xl border border-border/70 bg-card p-4 h-auto flex flex-col gap-3 shadow-sm">
{/* 上半部分:图标 + 标题 + 箭头 */}
<div className="flex items-center justify-between">
<div className="flex gap-3 items-center">
{/* 图标容器 */}
<div className="flex items-center justify-center w-10 h-10 rounded-xl bg-[rgba(var(--tool-color),0.08)] text-[rgb(var(--tool-color))]">
<Icon className="h-5 w-5" />
</div>
{/* 文字 */}
<div>
<h4 className="font-bold text-sm">标题</h4>
<p className="text-[11px] text-muted-foreground/90">描述</p>
</div>
</div>
<ChevronRight className="h-4 w-4" />
</div>
{/* 下半部分:预览区(可选) */}
<div className="mt-1 pt-3 border-t border-dashed border-border/80">{snapshot}</div>
</div>
```
### 6.5 TextInputArea
来源:`src/components/TextInputArea.tsx`
```tsx
// 多行文本输入区
<TextInputArea
value={input}
onChange={setInput}
placeholder="输入内容..."
showCount={true}
showClear={true}
allowCopy={true}
minRows={6}
maxRows={12}
/>
```
**规范要点**
- 外容器:`rounded-md border border-input bg-background shadow-sm`
- 焦点状态:`focus-within:ring-1 focus-within:ring-ring`
- 错误状态:`border-destructive focus-within:ring-destructive`
- 底部工具栏:`h-10 bg-muted/30 border-t border-border/50`
- 字体:`font-mono text-sm`
### 6.6 Dialog
来源:`src/components/ui/dialog.tsx`
```tsx
// 对话框内容
<DialogContent className="sm:rounded-lg">
<DialogHeader>
<DialogTitle>标题</DialogTitle>
<DialogDescription>描述文字</DialogDescription>
</DialogHeader>
{/* 内容 */}
<DialogFooter>
<Button variant="outline">取消</Button>
<Button>确认</Button>
</DialogFooter>
</DialogContent>
```
### 6.7 Select
来源:`src/components/ui/select.tsx`
```tsx
<Select>
<SelectTrigger className="h-9 shadow-sm bg-background">
<SelectValue />
</SelectTrigger>
<SelectContent className="max-h-64">
<SelectItem className="text-xs font-semibold focus:bg-accent cursor-pointer">选项</SelectItem>
</SelectContent>
</Select>
```
### 6.8 Checkbox
来源:`src/components/ui/checkbox.tsx`
```tsx
// 标准复选框
<Checkbox className="h-4 w-4 rounded-sm border-primary data-[state=checked]:bg-primary" />
// 小型复选框(工具栏内)
<Checkbox className="h-3.5 w-3.5 rounded border-input data-[state=checked]:bg-primary shadow-sm" />
```
### 6.9 Badge
来源:`src/components/ui/badge.tsx`
| 变体 | 场景 |
| ------------- | ------------------ |
| `default` | 状态标签、分类 |
| `secondary` | 次要标签 |
| `destructive` | 错误标签 |
| `outline` | 可点击标签、筛选器 |
### 6.10 CopyButton
来源:`src/components/CopyButton.tsx`
```tsx
// 标准复制按钮
<CopyButton text={content} />
// 小型复制按钮
<CopyButton text={content} size="sm" className="h-7 w-7 rounded-md border" />
```
**规范要点**
- 默认 `variant="ghost" size="icon"`
- 复制成功后变为绿色背景 + 对勾图标
- 使用 `sonner` toast 提示复制结果
---
## 7. 交互与动效
### 7.1 过渡规范
| 属性 | 值 | 场景 |
| ------------------- | ---------- | ----------------------------- |
| `transition-colors` | 150ms ease | 色彩变化(悬停、焦点) |
| `transition-all` | 150ms ease | 综合变化(SwitchButtonGroup |
| `duration-200` | 200ms | 复制按钮状态切换 |
### 7.2 焦点状态
所有可交互元素必须有可见的焦点指示器:
```tsx
// 标准焦点环
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
// 紧凑焦点环(图标按钮)
focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring
// 输入框焦点
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
```
### 7.3 悬停状态
```tsx
// 按钮悬停
hover:bg-accent hover:text-accent-foreground
// 卡片悬停
hover:bg-muted/30 hover:border-[rgba(var(--tool-color),0.45)]
// 链接/文字悬停
hover:text-foreground hover:underline
```
### 7.4 动画规范
使用 `tailwindcss-animate` 提供的动画:
```tsx
// 淡入
animate-in fade-in duration-150
// 淡入 + 缩放(SwitchButtonGroup 选中项)
animate-in fade-in-50 zoom-in-95 duration-150
// 从顶部滑入(下拉菜单)
animate-in fade-in slide-in-from-top-2 duration-150
// 错误提示出现
animate-in fade-in slide-in-from-top-1 duration-150
// 骨架屏脉冲
animate-pulse
```
### 7.5 禁用状态
```tsx
// 统一禁用样式
disabled:pointer-events-none disabled:opacity-50
```
---
## 8. 暗色模式
### 8.1 实现方式
通过 `darkMode: 'class'`Tailwind 配置)+ `.dark` 类切换:
```tsx
// ThemeModeProvider 自动处理
document.documentElement.classList.toggle('dark', resolvedMode === 'dark');
```
### 8.2 暗色模式下的特殊处理
```tsx
// 彩色阴影增强(暗色模式下阴影需要更高透明度)
shadow-[0_8px_24px_-8px_rgba(var(--tool-color),0.14)]
dark:shadow-[0_8px_30px_-10px_rgba(var(--tool-color),0.25)]
// 图标容器背景增强
bg-[rgba(var(--tool-color),0.08)]
dark:bg-[rgba(var(--tool-color),0.12)]
// 成功状态文字调整
text-emerald-600 dark:text-emerald-400
```
### 8.3 暗色模式色彩映射原则
| 亮色 | 暗色 | 说明 |
| ---------------- | ---------------- | ------------------------------- |
| 纯白背景 | 深蓝黑背景 | 避免纯黑 `#000`,使用 `#020617` |
| 浅灰背景 | 深灰背景 | 保持层次关系 |
| 深文字 | 浅文字 | 反转对比度 |
| 彩色阴影低透明度 | 彩色阴影高透明度 | 暗色需要更强视觉反馈 |
---
## 9. 工具色彩标识
### 9.1 色板定义
每个工具分配一个主题色,定义于 `src/config/features.tsx`
```ts
const PALETTE_COLORS: Record<PaletteColorKey, string> = {
primary: '13, 148, 136', // teal (#0d9488)
success: '22, 163, 74', // green (#16a34a)
warning: '217, 119, 6', // amber (#d97706)
error: '220, 38, 38', // red (#dc2626)
secondary: '147, 51, 232', // purple (#9333e8)
info: '37, 99, 235', // blue (#2563eb)
};
```
### 9.2 工具色彩分配
| 工具 | 色彩键 | 色值 |
| ----------- | ----------- | ------ |
| 时间戳转换 | `primary` | Teal |
| 存储清理 | `warning` | Amber |
| 二维码工具 | `success` | Green |
| 文本统计 | `secondary` | Purple |
| JWT 解析 | `info` | Blue |
| JSON 对比 | `primary` | Teal |
| Base64 转换 | `info` | Blue |
| 右键还原 | `success` | Green |
### 9.3 工具色彩使用规范
```tsx
// 1. 通过 style 注入 CSS 变量
<div style={{ ['--tool-color' as string]: rgbValues }}>
// 2. 图标容器背景(低透明度)
bg-[rgba(var(--tool-color),0.08)]
dark:bg-[rgba(var(--tool-color),0.12)]
// 3. 图标颜色
text-[rgb(var(--tool-color))]
// 4. 悬停边框
hover:border-[rgba(var(--tool-color),0.45)]
// 5. 悬停阴影
hover:shadow-[0_8px_24px_-8px_rgba(var(--tool-color),0.14)]
// 6. 箭头悬停色
group-hover:text-[rgb(var(--tool-color))]
```
**注意**:工具色彩仅用于**标识和装饰**,不得用于功能性色彩(如成功/错误状态)。
---
## 10. 代码规范
### 10.1 Tailwind 类名组织顺序
使用 `cn()` 工具函数(`clsx` + `tailwind-merge`)组合类名,按以下顺序排列:
```tsx
className={cn(
// 1. 布局(display, position, flex, grid
'flex items-center justify-between',
// 2. 尺寸(width, height, padding, margin
'w-full h-10 px-4',
// 3. 外观(background, border, shadow, rounded
'rounded-md border border-input bg-background shadow-sm',
// 4. 文字(color, font, text-align
'text-sm font-medium text-foreground',
// 5. 交互(hover, focus, disabled, cursor
'hover:bg-accent focus-visible:ring-2 disabled:opacity-50',
// 6. 动画(transition, animate
'transition-colors duration-150',
// 7. 条件类
isActive && 'bg-primary text-primary-foreground',
// 8. 外部传入
className,
)}
```
### 10.2 颜色使用检查清单
- [ ] 所有颜色使用 CSS 变量(`bg-background` 而非 `bg-white`
- [ ] 边框使用 `border-border` 及其透明度变体
- [ ] 文字层级使用 `foreground``muted-foreground``muted-foreground/60`
- [ ] 错误状态使用 `destructive` 系列
- [ ] 工具色彩仅用于装饰性元素
### 10.3 组件文件组织
```
src/
├── components/ui/ # shadcn 基础组件(只读,不修改)
├── components/ # 业务组件
│ ├── CopyButton.tsx
│ ├── SwitchButtonGroup.tsx
│ ├── TextInputArea.tsx
│ └── ...
├── pages/ # 页面组件
│ ├── <ToolName>/
│ │ ├── index.tsx # 页面入口
│ │ ├── use<ToolName>.ts # 业务逻辑 Hook
│ │ └── components/ # 页面私有组件
│ └── ...
└── providers/ # Context Providers
```
### 10.4 新增页面模板
```tsx
// src/pages/NewTool/index.tsx
import SwitchButtonGroup from '@/components/SwitchButtonGroup';
import { useI18n } from '@/utils/chromeI18n';
import { cn } from '@/lib/utils';
export default function Index() {
const { t } = useI18n('newTool');
return (
<div className="p-4 w-full flex flex-col space-y-4 min-h-[500px] select-none">
{/* 页面内容 */}
<div className="p-5 rounded-xl border border-border bg-card text-card-foreground shadow-sm">
<h4 className="font-bold text-sm tracking-tight">{t('newTool:title')}</h4>
</div>
</div>
);
}
```
---
## 11. 反模式清单
以下模式**禁止**在项目中使用:
### 11.1 色彩反模式
```tsx
// ❌ 硬编码颜色
<div className="bg-white text-black">
<div className="bg-gray-100">
<div className="text-gray-500">
// ❌ 使用非语义化 Tailwind 颜色
<div className="bg-slate-50">
<div className="text-zinc-400">
// ✅ 使用 CSS 变量
<div className="bg-background text-foreground">
<div className="bg-muted text-muted-foreground">
```
### 11.2 布局反模式
```tsx
// ❌ 固定高度导致内容截断
<div className="h-[200px]">
// ✅ 使用 min-height 或自适应
<div className="min-h-[200px]">
<div className="h-auto">
// ❌ 使用 margin 做组件间距
<div className="mb-4">
// ✅ 使用 gap
<div className="flex flex-col gap-4">
```
### 11.3 组件反模式
```tsx
// ❌ 修改 shadcn/ui 基础组件样式
// 如需修改,通过 className 覆盖或创建包装组件
// ❌ 内联样式用于颜色(工具色彩除外)
<div style={{ backgroundColor: '#f1f5f9' }}>
// ❌ 混合使用不同圆角体系
<Button className="rounded-lg"> // Button 应为 rounded-md
// ❌ 忽略焦点状态
<button className="..."> // 缺少 focus-visible 样式
```
### 11.4 暗色模式反模式
```tsx
// ❌ 仅适配部分元素
<div className="bg-white text-black dark:bg-gray-900 dark:text-white">
// ✅ 使用 CSS 变量自动适配
<div className="bg-background text-foreground">
// ❌ 暗色模式下使用不合适的透明度
<div className="bg-black/5 dark:bg-white/5"> // 对比度不足
// ✅ 使用语义化变量
<div className="bg-muted">
```
---
## 12. 附录
### 12.1 常用类名速查
```
// 页面容器
p-4 w-full flex flex-col space-y-4 min-h-[500px] select-none
// 标准卡片
p-5 rounded-xl border border-border bg-card text-card-foreground shadow-sm
// 工具栏
flex h-10 items-center justify-between px-1.5 bg-secondary/40 rounded-xl border border-border/60
// 区域标签
text-[10px] font-bold text-muted-foreground/90 uppercase tracking-wider
// 数据展示
font-mono font-semibold text-foreground text-sm
// 错误提示
text-xs font-medium text-destructive
// 空状态
p-8 rounded-xl bg-muted/30 border border-dashed border-border/80 text-center
// 图标按钮容器
flex h-8 w-8 items-center justify-center rounded-md border border-input bg-background shadow-sm
// 焦点环
focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2
```
### 12.2 相关文件
| 文件 | 说明 |
| ------------------------------------- | ----------------------- |
| `src/index.css` | CSS 变量定义、全局样式 |
| `tailwind.config.js` | Tailwind 配置、色彩映射 |
| `src/lib/utils.ts` | `cn()` 工具函数 |
| `src/components/ui/*.tsx` | shadcn/ui 基础组件 |
| `src/config/features.tsx` | 工具配置、色彩分配 |
| `src/providers/ThemeModeProvider.tsx` | 主题模式管理 |
### 12.3 参考资源
- [shadcn/ui 文档](https://ui.shadcn.com/docs)
- [Tailwind CSS 文档](https://tailwindcss.com/docs)
- [Radix UI 文档](https://www.radix-ui.com/)
- [Lucide Icons](https://lucide.dev/)
---
_本文档随项目迭代更新。新增组件或修改视觉风格时,请同步更新此文档。_