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

26 KiB
Raw Permalink Blame History

Testing Tools — 视觉规范文档

版本: 1.0.0
日期: 2026-05-29
适用范围: 所有新页面、新组件、UI 修改
设计系统: 基于 shadcn/ui + Tailwind CSS


目录

  1. 设计原则
  2. 色彩系统
  3. 排版规范
  4. 间距与布局
  5. 圆角与阴影
  6. 组件规范
  7. 交互与动效
  8. 暗色模式
  9. 工具色彩标识
  10. 代码规范
  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 使用规范

// ✅ 正确:使用 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 默认),不引入自定义字体

/* 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 排版模式

// 区域标签(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 容器尺寸

// 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 布局模式

页面布局

// 标准页面结构
<div className="p-4 w-full flex flex-col space-y-4 min-h-[500px] select-none">{/* 页面内容 */}</div>

卡片布局

// 标准卡片
<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">

双栏网格

// 响应式双栏
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 items-stretch">

工具卡片网格(Dashboard

// 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 彩色阴影规范(工具卡片专用)

// 工具卡片悬停阴影 — 必须使用 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 图标按钮

使用示例

// 主操作
<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

// 标准输入框
<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-1040px
  • 等宽字体用于数据输入
  • 占位符使用 text-muted-foreground/60
  • 错误时边框变红并调整焦点环

6.3 SwitchButtonGroup

来源:src/components/ui/switch.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
  • 尺寸:small32px)用于工具页,medium36px)标准

6.4 Card(工具卡片)

来源:src/pages/Dashboard/ToolCard.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

// 多行文本输入区
<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

// 对话框内容
<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

<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

// 标准复选框
<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

// 标准复制按钮
<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 焦点状态

所有可交互元素必须有可见的焦点指示器:

// 标准焦点环
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 悬停状态

// 按钮悬停
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 提供的动画:

// 淡入
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 禁用状态

// 统一禁用样式
disabled:pointer-events-none disabled:opacity-50

8. 暗色模式

8.1 实现方式

通过 darkMode: 'class'Tailwind 配置)+ .dark 类切换:

// ThemeModeProvider 自动处理
document.documentElement.classList.toggle('dark', resolvedMode === 'dark');

8.2 暗色模式下的特殊处理

// 彩色阴影增强(暗色模式下阴影需要更高透明度)
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

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 工具色彩使用规范

// 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)组合类名,按以下顺序排列:

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 及其透明度变体
  • 文字层级使用 foregroundmuted-foregroundmuted-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 新增页面模板

// 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 色彩反模式

// ❌ 硬编码颜色
<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 布局反模式

// ❌ 固定高度导致内容截断
<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 组件反模式

// ❌ 修改 shadcn/ui 基础组件样式
// 如需修改,通过 className 覆盖或创建包装组件

// ❌ 内联样式用于颜色(工具色彩除外)
<div style={{ backgroundColor: '#f1f5f9' }}>

// ❌ 混合使用不同圆角体系
<Button className="rounded-lg">  // Button 应为 rounded-md

// ❌ 忽略焦点状态
<button className="...">  // 缺少 focus-visible 样式

11.4 暗色模式反模式

// ❌ 仅适配部分元素
<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 参考资源


本文档随项目迭代更新。新增组件或修改视觉风格时,请同步更新此文档。