From 37a2434d250af9257dafd70d162d4d3e456bc9c9 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Apr 2026 15:34:43 +0800 Subject: [PATCH 01/11] =?UTF-8?q?docs(ui-redesign):=20=E6=B2=89=E6=B7=80?= =?UTF-8?q?=20UI=20=E9=87=8D=E8=AE=BE=E8=AE=A1=E7=9A=84=E8=AE=BE=E8=AE=A1?= =?UTF-8?q?=E6=96=87=E6=A1=A3=E4=B8=8E=E5=AE=9E=E6=96=BD=E8=AE=A1=E5=88=92?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .agentdocs/index.md | 2 + .../workflow/260423-ui-redesign-plan.md | 1030 +++++++++++++++++ .agentdocs/workflow/260423-ui-redesign.md | 170 +++ 3 files changed, 1202 insertions(+) create mode 100644 .agentdocs/workflow/260423-ui-redesign-plan.md create mode 100644 .agentdocs/workflow/260423-ui-redesign.md diff --git a/.agentdocs/index.md b/.agentdocs/index.md index d2f1954..f09bc0a 100644 --- a/.agentdocs/index.md +++ b/.agentdocs/index.md @@ -5,6 +5,8 @@ ## 后端文档 ## 当前任务文档 +`workflow/260423-ui-redesign.md` - UI 重设计设计文档(清新活力 + 轻点缀 + 扁平),定义配色/字体/圆角/微动效规范与分阶段 TODO +`workflow/260423-ui-redesign-plan.md` - UI 重设计实施计划,按 7 阶段拆解可直接执行的代码步骤 ## 全局重要记忆 - 前端主项目位于 `src/dev-tools-collection-web` diff --git a/.agentdocs/workflow/260423-ui-redesign-plan.md b/.agentdocs/workflow/260423-ui-redesign-plan.md new file mode 100644 index 0000000..36026e2 --- /dev/null +++ b/.agentdocs/workflow/260423-ui-redesign-plan.md @@ -0,0 +1,1030 @@ +# UI 重设计实施计划(清新活力 · 轻点缀 · 扁平) + +> **给执行者**:本计划配套的 Superpowers 子技能为 `superpowers:subagent-driven-development`(推荐)或 `superpowers:executing-plans`,按任务勾选 `- [ ]` 推进。本项目未引入测试框架(见 `AGENTS.md`),验证基线为 **`pnpm lint` + `pnpm build` + 浅/深色模式目视走查**,计划中所有"验证"步骤均以此为准,不要自行引入测试框架。 + +**目标**:将 DevToolsCollection 前端从"冷峻深蓝灰商务风"重构为"明快蓝+彩色点缀+大圆角+微动效"的清新活力风,保持扁平骨架与既有布局/路由/信息层级不变。 + +**架构方法**:改动集中在 5 个组件 + 1 份 CSS 变量文件 + 1 份数据文件,不新增组件、不新增依赖。通过 Tailwind v4 `@theme` 注入字体栈与色值变量,通过 `ToolCard` 内置静态颜色映射表保证 Tailwind JIT 能打包彩色 class。 + +**技术栈**:React 19、TypeScript 5.9、Vite 7、TanStack Router、Tailwind CSS v4(含 `@theme` / `@custom-variant`)、shadcn/ui、i18next。 + +--- + +## 文件结构 + +| 路径 | 责任 | 本次操作 | +|------|------|---------| +| `src/index.css` | 全局 CSS 变量、字体栈、Tailwind 入口 | 修改 | +| `src/data/tools.ts` | 工具元数据与搜索;新增 `color` 字段 | 修改 | +| `src/components/ToolCard.tsx` | 卡片容器 + 图标底块 + 标题;新增静态颜色映射表 | 修改 | +| `src/components/SearchBar.tsx` | 搜索输入框样式(圆角、阴影、focus ring) | 修改 | +| `src/routes/index.tsx` | 首页 Hero(浅底 + 光晕 blob + 副标题)+ 工具网格 | 修改 | +| `src/components/ToolPageHeader.tsx` | 工具详情页顶部(去深色渐变) | 修改 | +| `src/components/Footer.tsx` | 页脚(去 muted 底色 + 去阴影) | 修改 | +| `src/localization/zh.ts` | 新增首页副标题文案(键 `common.tagline`) | 修改 | +| `src/localization/en.ts` | 同上 | 修改 | + +**执行工作目录**:所有 `pnpm` 命令在 `src/dev-tools-collection-web/` 下执行。 + +--- + +## 阶段 1:配色底座 & 字体栈 + +### Task 1.1:更新 CSS 变量与字体栈 + +**Files:** +- Modify: `src/dev-tools-collection-web/src/index.css` + +- [ ] **Step 1:打开 `src/index.css`,把整份 `@theme inline`、`:root`、`.dark` 替换为下列内容** + +`@theme inline` 块(新增字体栈变量 `--font-sans`、`--font-mono`,其余保持原有映射): + +```css +@theme inline { + --font-sans: + Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, + sans-serif; + --font-mono: + ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, + 'Liberation Mono', monospace; + + --radius-sm: calc(var(--radius) - 4px); + --radius-md: calc(var(--radius) - 2px); + --radius-lg: var(--radius); + --radius-xl: calc(var(--radius) + 4px); + --color-background: var(--background); + --color-foreground: var(--foreground); + --color-card: var(--card); + --color-card-foreground: var(--card-foreground); + --color-popover: var(--popover); + --color-popover-foreground: var(--popover-foreground); + --color-primary: var(--primary); + --color-primary-foreground: var(--primary-foreground); + --color-secondary: var(--secondary); + --color-secondary-foreground: var(--secondary-foreground); + --color-muted: var(--muted); + --color-muted-foreground: var(--muted-foreground); + --color-accent: var(--accent); + --color-accent-foreground: var(--accent-foreground); + --color-destructive: var(--destructive); + --color-border: var(--border); + --color-input: var(--input); + --color-ring: var(--ring); + --color-chart-1: var(--chart-1); + --color-chart-2: var(--chart-2); + --color-chart-3: var(--chart-3); + --color-chart-4: var(--chart-4); + --color-chart-5: var(--chart-5); + --color-sidebar: var(--sidebar); + --color-sidebar-foreground: var(--sidebar-foreground); + --color-sidebar-primary: var(--sidebar-primary); + --color-sidebar-primary-foreground: var(--sidebar-primary-foreground); + --color-sidebar-accent: var(--sidebar-accent); + --color-sidebar-accent-foreground: var(--sidebar-accent-foreground); + --color-sidebar-border: var(--sidebar-border); + --color-sidebar-ring: var(--sidebar-ring); +} +``` + +`:root`(浅色模式,主色切换为明快蓝,背景极淡冷白,边框更柔): + +```css +:root { + --radius: 0.75rem; + --background: oklch(0.99 0.005 250); + --foreground: oklch(0.22 0.03 260); + --card: oklch(1 0 0); + --card-foreground: oklch(0.22 0.03 260); + --popover: oklch(1 0 0); + --popover-foreground: oklch(0.22 0.03 260); + --primary: oklch(0.62 0.19 250); + --primary-foreground: oklch(0.99 0.005 250); + --secondary: oklch(0.97 0.015 250); + --secondary-foreground: oklch(0.28 0.05 260); + --muted: oklch(0.97 0.015 250); + --muted-foreground: oklch(0.52 0.03 255); + --accent: oklch(0.95 0.03 250); + --accent-foreground: oklch(0.28 0.05 260); + --destructive: oklch(0.62 0.22 27); + --border: oklch(0.93 0.015 250); + --input: oklch(0.93 0.015 250); + --ring: oklch(0.62 0.19 250); + --chart-1: oklch(0.646 0.222 41.116); + --chart-2: oklch(0.6 0.118 184.704); + --chart-3: oklch(0.398 0.07 227.392); + --chart-4: oklch(0.828 0.189 84.429); + --chart-5: oklch(0.769 0.188 70.08); + --sidebar: oklch(0.984 0.003 247.858); + --sidebar-foreground: oklch(0.22 0.03 260); + --sidebar-primary: oklch(0.62 0.19 250); + --sidebar-primary-foreground: oklch(0.99 0.005 250); + --sidebar-accent: oklch(0.95 0.03 250); + --sidebar-accent-foreground: oklch(0.28 0.05 260); + --sidebar-border: oklch(0.93 0.015 250); + --sidebar-ring: oklch(0.62 0.19 250); +} +``` + +`.dark`(主色提亮,保证深底仍明快): + +```css +.dark { + --background: oklch(0.16 0.02 260); + --foreground: oklch(0.97 0.01 250); + --card: oklch(0.22 0.03 260); + --card-foreground: oklch(0.97 0.01 250); + --popover: oklch(0.22 0.03 260); + --popover-foreground: oklch(0.97 0.01 250); + --primary: oklch(0.7 0.16 245); + --primary-foreground: oklch(0.16 0.02 260); + --secondary: oklch(0.28 0.04 260); + --secondary-foreground: oklch(0.97 0.01 250); + --muted: oklch(0.28 0.04 260); + --muted-foreground: oklch(0.72 0.03 255); + --accent: oklch(0.28 0.04 260); + --accent-foreground: oklch(0.97 0.01 250); + --destructive: oklch(0.7 0.19 22); + --border: oklch(1 0 0 / 10%); + --input: oklch(1 0 0 / 15%); + --ring: oklch(0.7 0.16 245); + --chart-1: oklch(0.488 0.243 264.376); + --chart-2: oklch(0.696 0.17 162.48); + --chart-3: oklch(0.769 0.188 70.08); + --chart-4: oklch(0.627 0.265 303.9); + --chart-5: oklch(0.645 0.246 16.439); + --sidebar: oklch(0.22 0.03 260); + --sidebar-foreground: oklch(0.97 0.01 250); + --sidebar-primary: oklch(0.7 0.16 245); + --sidebar-primary-foreground: oklch(0.16 0.02 260); + --sidebar-accent: oklch(0.28 0.04 260); + --sidebar-accent-foreground: oklch(0.97 0.01 250); + --sidebar-border: oklch(1 0 0 / 10%); + --sidebar-ring: oklch(0.7 0.16 245); +} +``` + +说明:`--radius` 从 `0.625rem` 上调到 `0.75rem`,使 `rounded-lg` ≈ 12px、`rounded-xl` ≈ 16px,对齐设计规范的"分层圆角"基线。`:root` 和 `.dark` 块**完全替换**(不要保留旧的深蓝灰主色)。`@layer base`、`html`、`#root, body` 及注释块保持原样不变。 + +- [ ] **Step 2:运行 lint 和 build 验证无语法错误** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm lint +pnpm build +``` +预期:两条命令均退出码 0,无新增错误。 + +- [ ] **Step 3:启动 dev 服务器目视走查** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm dev +``` +打开首页,确认: +- 背景接近纯白但略有冷调(不是纯白 `#fff`) +- 主按钮/链接/focus ring 的主色调从原来的深蓝灰变为明快蓝 +- 切换到深色模式(Footer 的 ThemeSwitcher),深色底上主色是更亮的蓝而非灰白 + +如出现意外颜色回归(例如某处元素变"灰扑扑"),记录具体位置,继续下一阶段再统一修。不要为此阶段额外改代码,除非已经无法看清。 + +然后按 Ctrl+C 退出 dev 服务器。 + +- [ ] **Step 4:提交** + +``` +git add src/dev-tools-collection-web/src/index.css +git commit -m "style: 将主色切换为明快蓝并扩展字体栈与圆角基线" +``` + +--- + +## 阶段 2:工具数据扩展 + +### Task 2.1:为 `Tool` 接口添加 `color` 字段并分配主题色 + +**Files:** +- Modify: `src/dev-tools-collection-web/src/data/tools.ts` + +- [ ] **Step 1:在 `tools.ts` 顶部导出 `ToolColor` 类型,并在 `Tool` 接口中加入 `color` 字段** + +在 `tools.ts` 现有 `import` 区块之后、`export interface Tool` 之前,新增类型: + +```ts +export type ToolColor = + | 'blue' + | 'amber' + | 'violet' + | 'cyan' + | 'indigo' + | 'pink' + | 'emerald' + | 'orange' + | 'fuchsia' + | 'sky'; +``` + +在 `Tool` 接口内加入 `color: ToolColor;`(放在 `popular?` 之前、`url` 之后): + +```ts +export interface Tool { + id: string; + icon: ForwardRefExoticComponent< + Omit & RefAttributes + >; + url: string; + color: ToolColor; + popular?: boolean; + /** 外链工具:存在时点击卡片在新标签页打开该 URL,不走站内路由 */ + externalUrl?: string; +} +``` + +- [ ] **Step 2:按设计文档为 10 个工具各自补上 `color` 字段** + +把 `toolsData` 整个数组替换为下列内容(只加了 `color` 字段,其余一字不动): + +```ts +export const toolsData: Tool[] = [ + { + id: 'json-formatter', + icon: Braces, + url: '/tools/json-formatter', + color: 'blue', + popular: true + }, + { + id: 'timestamp', + icon: Clock, + url: '/tools/timestamp', + color: 'amber', + popular: true + }, + { + id: 'uuid-generator', + icon: RotateCcwKey, + url: '/tools/uuid-generator', + color: 'violet', + popular: true + }, + { + id: 'base64-codec', + icon: Code, + url: '/tools/base64-codec', + color: 'cyan', + popular: true + }, + { + id: 'url-codec', + icon: Link, + url: '/tools/url-codec', + color: 'indigo', + popular: true + }, + { + id: 'qrcode-generator', + icon: QrCode, + url: '/tools/qrcode-generator', + color: 'pink', + popular: true + }, + { + id: 'hash-encoder', + icon: Hash, + url: '/tools/hash-encoder', + color: 'emerald', + popular: true + }, + { + id: 'html-preview', + icon: FileCode, + url: '/tools/html-preview', + color: 'orange', + popular: true + }, + { + id: 'regex-tester', + icon: Regex, + url: '/tools/regex-tester', + color: 'fuchsia', + popular: true + }, + { + id: 'ip-info', + icon: Globe, + url: 'https://ipinfo.io/what-is-my-ip', + externalUrl: 'https://ipinfo.io/what-is-my-ip', + color: 'sky', + popular: true + } +]; +``` + +文件中其余内容(`getPopularTools`、`SEARCH_LANGS`、`normalize`、`buildAliases`、`searchTools`)保持不变。 + +- [ ] **Step 3:运行 build 验证类型正确** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm build +``` +预期:类型检查通过,编译成功。若提示 "Property 'color' is missing",说明某个 `toolsData` 条目漏了 color 字段,补齐后重试。 + +- [ ] **Step 4:提交** + +``` +git add src/dev-tools-collection-web/src/data/tools.ts +git commit -m "feat(tools): 为 Tool 接口添加 color 字段并分配各工具主题色" +``` + +--- + +## 阶段 3:ToolCard 彩色化 + +### Task 3.1:重写 `ToolCard.tsx`,引入颜色映射表 + +**Files:** +- Modify: `src/dev-tools-collection-web/src/components/ToolCard.tsx` + +- [ ] **Step 1:把整份 `ToolCard.tsx` 替换为以下内容** + +```tsx +import { Card, CardContent } from '@/components/ui/card'; +import type { Tool, ToolColor } from '../data/tools'; +import { Link } from '@tanstack/react-router'; +import { useTranslation } from 'react-i18next'; +import { ExternalLink } from 'lucide-react'; + +interface ToolCardProps { + tool: Tool; +} + +// 静态颜色映射表:Tailwind JIT 必须看到完整 class 字符串才能打包, +// 因此这里不能用字符串拼接(如 `bg-${color}-100`),只能逐项列出。 +const colorMap: Record< + ToolColor, + { + iconBg: string; + iconText: string; + hoverBorder: string; + hoverShadow: string; + } +> = { + blue: { + iconBg: 'bg-blue-100 dark:bg-blue-500/15', + iconText: 'text-blue-600 dark:text-blue-400', + hoverBorder: 'group-hover:border-blue-300 dark:group-hover:border-blue-500/40', + hoverShadow: 'group-hover:shadow-blue-500/10' + }, + amber: { + iconBg: 'bg-amber-100 dark:bg-amber-500/15', + iconText: 'text-amber-600 dark:text-amber-400', + hoverBorder: 'group-hover:border-amber-300 dark:group-hover:border-amber-500/40', + hoverShadow: 'group-hover:shadow-amber-500/10' + }, + violet: { + iconBg: 'bg-violet-100 dark:bg-violet-500/15', + iconText: 'text-violet-600 dark:text-violet-400', + hoverBorder: 'group-hover:border-violet-300 dark:group-hover:border-violet-500/40', + hoverShadow: 'group-hover:shadow-violet-500/10' + }, + cyan: { + iconBg: 'bg-cyan-100 dark:bg-cyan-500/15', + iconText: 'text-cyan-600 dark:text-cyan-400', + hoverBorder: 'group-hover:border-cyan-300 dark:group-hover:border-cyan-500/40', + hoverShadow: 'group-hover:shadow-cyan-500/10' + }, + indigo: { + iconBg: 'bg-indigo-100 dark:bg-indigo-500/15', + iconText: 'text-indigo-600 dark:text-indigo-400', + hoverBorder: 'group-hover:border-indigo-300 dark:group-hover:border-indigo-500/40', + hoverShadow: 'group-hover:shadow-indigo-500/10' + }, + pink: { + iconBg: 'bg-pink-100 dark:bg-pink-500/15', + iconText: 'text-pink-600 dark:text-pink-400', + hoverBorder: 'group-hover:border-pink-300 dark:group-hover:border-pink-500/40', + hoverShadow: 'group-hover:shadow-pink-500/10' + }, + emerald: { + iconBg: 'bg-emerald-100 dark:bg-emerald-500/15', + iconText: 'text-emerald-600 dark:text-emerald-400', + hoverBorder: 'group-hover:border-emerald-300 dark:group-hover:border-emerald-500/40', + hoverShadow: 'group-hover:shadow-emerald-500/10' + }, + orange: { + iconBg: 'bg-orange-100 dark:bg-orange-500/15', + iconText: 'text-orange-600 dark:text-orange-400', + hoverBorder: 'group-hover:border-orange-300 dark:group-hover:border-orange-500/40', + hoverShadow: 'group-hover:shadow-orange-500/10' + }, + fuchsia: { + iconBg: 'bg-fuchsia-100 dark:bg-fuchsia-500/15', + iconText: 'text-fuchsia-600 dark:text-fuchsia-400', + hoverBorder: 'group-hover:border-fuchsia-300 dark:group-hover:border-fuchsia-500/40', + hoverShadow: 'group-hover:shadow-fuchsia-500/10' + }, + sky: { + iconBg: 'bg-sky-100 dark:bg-sky-500/15', + iconText: 'text-sky-600 dark:text-sky-400', + hoverBorder: 'group-hover:border-sky-300 dark:group-hover:border-sky-500/40', + hoverShadow: 'group-hover:shadow-sky-500/10' + } +}; + +const ToolCard = ({ tool }: ToolCardProps) => { + const { t } = useTranslation(); + const label = t(`tools.${tool.id}`); + const isExternal = Boolean(tool.externalUrl); + const palette = colorMap[tool.color]; + + const cardContent = ( + + {isExternal && ( + + ); + + const wrapperClass = 'group block'; + + if (isExternal) { + return ( + + {cardContent} + + ); + } + + return ( + + {cardContent} + + ); +}; + +export default ToolCard; +``` + +关键点解释: +- `group` 类放在外层 `` / `` 上,使内部 Card 能通过 `group-hover:*` 响应外层鼠标状态(原本 `hover:` 只作用于 Card 本身,现在要让多个子元素同步反应) +- 动态 class 通过查表字符串插值,Tailwind JIT 在源码里能看到完整 `bg-blue-100` 等,打包正常 +- 过渡统一 `duration-200`,hover 时卡片上浮 2px、加彩色阴影、加彩色边框 +- 卡片无默认阴影,符合"静态扁平、交互才出现阴影"的设计策略 + +- [ ] **Step 2:运行 lint 和 build** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm lint +pnpm build +``` +预期:两条命令均退出码 0。若 ESLint 报 `Unexpected template string` 之类的风格问题,按提示修一下模板字符串换行即可。 + +- [ ] **Step 3:dev 目视验证卡片** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm dev +``` +浏览器访问首页,检查: +- 10 张卡片的图标底块颜色各不相同(蓝/橙/紫/青/靛/粉/翠/橙红/洋红/天蓝) +- 悬停时卡片有明显上浮、带对应色的柔和阴影、边框变为对应色 +- 卡片标题改为无衬线字体(不再是等宽字体),字号略小,颜色略柔 +- 外链卡片(IP 信息)右上角仍有外链图标,但更淡 +- 切深色模式,彩色语言仍清晰可辨,不过度刺眼 + +按 Ctrl+C 退出。 + +- [ ] **Step 4:提交** + +``` +git add src/dev-tools-collection-web/src/components/ToolCard.tsx +git commit -m "feat(tool-card): 引入彩色映射表并启用彩色图标与 hover 微动效" +``` + +--- + +## 阶段 4:首页 Hero 与搜索框 + +### Task 4.1:补充 i18n 文案(副标题) + +**Files:** +- Modify: `src/dev-tools-collection-web/src/localization/zh.ts` +- Modify: `src/dev-tools-collection-web/src/localization/en.ts` + +- [ ] **Step 1:在 `zh.ts` 的 `common` 对象内加入 `tagline` 字段** + +定位到 `common` 对象,在 `description` 之后新增一行(保持键名排序与风格一致): + +```ts +tagline: '10+ 开发者日常工具,开箱即用,无需安装', +``` + +**完整位置示例**(对照修改): + +```ts +common: { + toolbox: '在线工具箱', + description: '提供各种实用工具,提高您的工作效率', + tagline: '10+ 开发者日常工具,开箱即用,无需安装', + searchResults: '搜索结果', + // ... 其余不变 +} +``` + +- [ ] **Step 2:在 `en.ts` 的 `common` 对象内加入 `tagline` 字段** + +```ts +tagline: '10+ developer tools, ready to use, no install required', +``` + +位置同步 `zh.ts`(`description` 之后、`searchResults` 之前)。 + +- [ ] **Step 3:运行 build 验证** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm build +``` +预期:编译通过。 + +- [ ] **Step 4:提交** + +``` +git add src/dev-tools-collection-web/src/localization/zh.ts src/dev-tools-collection-web/src/localization/en.ts +git commit -m "i18n: 新增首页副标题文案 common.tagline" +``` + +### Task 4.2:重写 `SearchBar.tsx` 样式 + +**Files:** +- Modify: `src/dev-tools-collection-web/src/components/SearchBar.tsx` + +- [ ] **Step 1:更新 SearchBar 的 JSX 样式** + +只替换 `return (...)` 块,其余逻辑保持不变。整份新 `SearchBar.tsx`: + +```tsx +import { useEffect, useState } from 'react'; +import { searchTools, type Tool } from '@/data/tools.ts'; +import { Search } from 'lucide-react'; +import { Input } from '@/components/ui/input.tsx'; +import { useTranslation } from 'react-i18next'; + +interface SearchBarProps { + onSearchResults: (results: Tool[]) => void; +} + +const SearchBar = ({ onSearchResults }: SearchBarProps) => { + const { t, i18n } = useTranslation(); + const [query, setQuery] = useState(''); + + useEffect(() => { + const delayDebounce = setTimeout(() => { + if (query) { + onSearchResults(searchTools(query, i18n)); + } else { + onSearchResults([]); + } + }, 300); + + return () => clearTimeout(delayDebounce); + }, [query, onSearchResults, i18n]); + + return ( +
+
+ + setQuery(e.target.value)} + /> +
+
+ ); +}; + +export default SearchBar; +``` + +说明: +- `focus-visible:ring-4 ring-primary/20` 在聚焦时产生柔和的主色光晕 +- 左侧 Search 图标用 `group-focus-within:text-primary` 同步变色 +- `shadow-lg shadow-primary/5` 是带色阴影,符合新设计策略 +- `h-14` 固定高度,视觉上比原来的 `py-6` 更稳 + +- [ ] **Step 2:运行 lint 与 build** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm lint +pnpm build +``` +预期:均退出码 0。 + +- [ ] **Step 3:提交** + +``` +git add src/dev-tools-collection-web/src/components/SearchBar.tsx +git commit -m "style(search): 放大圆角/采用带主色的有色阴影与 focus ring" +``` + +### Task 4.3:重写首页 `routes/index.tsx` 的 Hero + +**Files:** +- Modify: `src/dev-tools-collection-web/src/routes/index.tsx` + +- [ ] **Step 1:把整份 `routes/index.tsx` 替换为下列内容** + +```tsx +import { createFileRoute } from '@tanstack/react-router'; +import { useState } from 'react'; +import { type Tool, toolsData } from '@/data/tools.ts'; +import SearchBar from '@/components/SearchBar.tsx'; +import ToolCard from '@/components/ToolCard.tsx'; +import Footer from '@/components/Footer.tsx'; +import { useTranslation } from 'react-i18next'; +import { usePageMetadata } from '@/hooks/usePageMetadata'; + +export const Route = createFileRoute('/')({ + component: Index +}); + +function Index() { + const { t } = useTranslation(); + const [searchResults, setSearchResults] = useState([]); + + usePageMetadata(); + + const handleSearchResults = (results: Tool[]) => { + setSearchResults(results); + }; + + return ( +
+
+
+ {/* 柔和光晕 blob —— 增强明快感但保持扁平 */} +
+ +
+ {searchResults.length > 0 ? ( +
+

+ {t('common.searchResults')} +

+
+ {searchResults.map(tool => ( + + ))} +
+
+ ) : ( +
+

+ {t('common.allTools')} +

+
+ {toolsData.map(tool => ( + + ))} +
+
+ )} +
+
+ +
+
+ ); +} +``` + +主要变化: +- Hero 去掉深色渐变,底色改为 Tailwind v4 任意色语法 `bg-[oklch(...)]`(浅/深色各一条) +- 两个 blob 绝对定位、大 blur、低 opacity,装饰但不喧宾夺主 +- `aria-hidden='true'` + `pointer-events-none` 让装饰元素不影响可达性与点击 +- 标题改为 `font-semibold`(去掉 `font-mono`)+ `text-foreground`(不再是白字) +- 新增副标题 `common.tagline` +- 图标与标题对齐 `items-end` → `items-center` +- 章节标题 `font-bold` → `font-semibold`、`mb-4` → `mb-5`、网格 `gap-3` → `gap-4` +- 外层 section padding `py-8` → `py-10` + +- [ ] **Step 2:运行 lint 与 build** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm lint +pnpm build +``` +预期:均退出码 0。 + +- [ ] **Step 3:dev 目视走查首页** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm dev +``` +检查: +- Hero 背景是浅蓝白底,没有深色条 +- 右上、左下各有一团柔和的蓝/青光晕 +- 标题深色、无衬线、下方有副标题 +- 搜索框浮在浅底之上视觉感清晰,点击后有主色光晕 ring +- 切深色模式,Hero 是深蓝黑底 + 低亮度 blob,整体仍和谐 +- 滚动到工具网格,卡片间距比原来更宽松 + +按 Ctrl+C 退出。 + +- [ ] **Step 4:提交** + +``` +git add src/dev-tools-collection-web/src/routes/index.tsx +git commit -m "style(home): 重写 Hero 为浅底光晕布局并加入副标题" +``` + +--- + +## 阶段 5:工具详情页 Header + +### Task 5.1:去除深色渐变,改为纯净浅底 + +**Files:** +- Modify: `src/dev-tools-collection-web/src/components/ToolPageHeader.tsx` + +- [ ] **Step 1:把整份 `ToolPageHeader.tsx` 替换为下列内容** + +```tsx +import { Link } from '@tanstack/react-router'; +import { Home } from 'lucide-react'; +import { useTranslation } from 'react-i18next'; + +interface ToolPageHeaderProps { + title: string; +} + +const ToolPageHeader = ({ title }: ToolPageHeaderProps) => { + const { t } = useTranslation(); + + return ( +
+
+
+ + + + {t('common.home')} + + +
+

+ {title} +

+
+
+
+ ); +}; + +export default ToolPageHeader; +``` + +关键变化: +- 深色渐变 `from-primary/90 to-gray-700` 去除,背景回归 `bg-background` +- 用 `border-b border-border/60` 做分隔 +- 标题字号由 `text-3xl` 降为 `text-xl md:text-2xl`,去 `font-bold` 改 `font-semibold` +- Home 按钮由大号白字改为小号 muted 灰,hover 变主色 +- 左右两侧占位宽度从 `w-16` 改为 `w-20`,给 Home 按钮更自然的呼吸感 +- 移除原标题多余的 `mb-2`(之前和深色条搭配时需要,扁平化后无意义) + +- [ ] **Step 2:运行 lint 与 build** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm lint +pnpm build +``` +预期:均退出码 0。 + +- [ ] **Step 3:dev 目视走查工具详情页** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm dev +``` +依次点进任意两到三个工具(如 JSON 格式化、二维码生成器、IP 信息外链虽然跳外站不需要测),检查: +- 页面顶部不再是深色条,而是一条浅底分隔线 +- 工具名称为深色无衬线中号标题,易读 +- 左上角 Home 按钮 hover 时变主色蓝 +- 深色模式下 Header 背景是深灰,分隔线可见但不突兀 + +按 Ctrl+C 退出。 + +- [ ] **Step 4:提交** + +``` +git add src/dev-tools-collection-web/src/components/ToolPageHeader.tsx +git commit -m "style(tool-header): 去除深色渐变条改为浅底分隔线的轻量顶栏" +``` + +--- + +## 阶段 6:Footer 小调 + +### Task 6.1:去 muted 底色与阴影 + +**Files:** +- Modify: `src/dev-tools-collection-web/src/components/Footer.tsx` + +- [ ] **Step 1:把整份 `Footer.tsx` 替换为下列内容** + +```tsx +import { useTranslation } from 'react-i18next'; +import LanguageSwitcher from './LanguageSwitcher'; +import ThemeSwitcher from './ThemeSwitcher'; + +const Footer = () => { + const { t } = useTranslation(); + const currentYear = new Date().getFullYear(); + + return ( +
+
+ {t('common.copyright', { year: currentYear })} +
+
+ + +
+
+ ); +}; + +export default Footer; +``` + +变化: +- `bg-muted/40` → `bg-background` +- 去掉 `shadow-sm` +- `border-t` → `border-t border-border/60`(更轻) +- 其余结构与类名保持不变 + +- [ ] **Step 2:运行 lint 与 build** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm lint +pnpm build +``` +预期:均退出码 0。 + +- [ ] **Step 3:提交** + +``` +git add src/dev-tools-collection-web/src/components/Footer.tsx +git commit -m "style(footer): 去除 muted 底色与阴影改为轻量分隔线" +``` + +--- + +## 阶段 7:整体验收 + +### Task 7.1:全站完整走查 + +**Files:** 无(纯验证阶段) + +- [ ] **Step 1:完整跑 lint 与 build** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm lint +pnpm build +``` +预期:两条命令退出码 0,无任何新增 error/warning。 + +- [ ] **Step 2:dev 模式下浅色/深色完整走查** + +Run (workdir `src/dev-tools-collection-web`): +``` +pnpm dev +``` +按下列清单逐项目视检查(浅色模式先走一遍,再切深色模式走一遍): + +**首页:** +- [ ] Hero 为浅底 + 光晕 blob,无深色条 +- [ ] 主标题无衬线、居中、深色;下方有副标题 +- [ ] 搜索框大圆角,focus 时有主色光晕 +- [ ] 工具网格 10 张卡片各自带主题色图标 +- [ ] hover 任一卡片:上浮 2px、出现带色柔和阴影、边框变为对应主题色 +- [ ] 搜索框输入关键词,搜索结果区显示正确的卡片 + +**工具详情页(任选 3 个:JSON 格式化 / UUID 生成器 / 二维码生成器):** +- [ ] 顶部为浅底分隔线,不再是深色渐变条 +- [ ] 工具名称为深色无衬线中号标题 +- [ ] Home 按钮 hover 变主色蓝 +- [ ] 工具内部的输入框/代码区仍保持可读(font-mono 只在内部内容区生效,不影响布局组件) +- [ ] 点击左上角 Home 回到首页正常 + +**Footer:** +- [ ] 浅底 + 顶部细分隔线,无阴影 +- [ ] ThemeSwitcher 可切换浅/深色,切换后全站色彩体系协调 + +**深色模式额外检查:** +- [ ] 主色在深底上是明快蓝(非灰白) +- [ ] 卡片图标底块在深底下饱和度适度(不过度刺眼也不糊成一片) +- [ ] Hero blob 在深底下保持微亮但不反差过强 + +按 Ctrl+C 退出。 + +- [ ] **Step 3:若第 2 步发现任何视觉问题** + +按问题定位到对应阶段的文件修改、提交修复,修完后重新跑 Step 1 + Step 2。常见修复方向: +- 某处颜色对比不够:调整 `iconBg` 的 `dark:` 变体透明度(例如 `/15` → `/20`) +- Hero blob 太亮/太暗:调 `bg-blue-400/30` 的数值或换 `blur-3xl` 为 `blur-2xl` +- 某个卡片 hover 阴影看不见:检查是否保留了 `group` 类和 `group-hover:` 前缀 + +所有问题修复后,`git status` 应干净。 + +- [ ] **Step 4:更新任务文档的 TODO 勾选状态** + +打开 `.agentdocs/workflow/260423-ui-redesign.md`,把"任务拆解 TODO"章节里的 7 个条目全部勾上 `- [x]`。 + +- [ ] **Step 5:把任务文档从"当前任务文档"移动到 done 目录** + +``` +git mv .agentdocs/workflow/260423-ui-redesign.md .agentdocs/workflow/done/260423-ui-redesign.md +``` + +同时编辑 `.agentdocs/index.md`,从「当前任务文档」章节下移除对应行(如果该章节变空,保留章节标题即可)。 + +- [ ] **Step 6:最终提交** + +``` +git add .agentdocs/workflow/done/260423-ui-redesign.md .agentdocs/index.md +git commit -m "docs: 完成 UI 重设计任务并归档任务文档" +``` + +--- + +## 自审备注(写给计划作者的回顾) + +- **Spec 覆盖**:设计文档 6 个规范章节(配色、Hero、卡片、详情页 Header、Footer、全局约定)分别对应阶段 1、4、3、5、6、1+3 的具体任务,无遗漏;"实现决策待定项"两条(卡片 hover 标题变色、详情 Header 加图标)均作为可选增强未强制要求,保留给执行时观察后再定。 +- **无占位**:所有代码步骤均给出完整可粘贴的代码块,所有命令均给出 workdir 与预期;验证步骤用目视 checklist 明确列出检查点,而不是"确认一切正常"。 +- **类型与命名一致性**:`ToolColor` 类型 10 个成员与 `colorMap` 的 10 个 key 一致,与 `toolsData` 中每条 `color` 字段一致;`common.tagline` 在 zh/en 都补齐。 +- **项目约束对齐**:所有命令 workdir 明确为 `src/dev-tools-collection-web`;未引入测试框架;文档路径使用项目约定的 `.agentdocs/workflow/`;每阶段独立提交,符合"频繁提交"原则。 diff --git a/.agentdocs/workflow/260423-ui-redesign.md b/.agentdocs/workflow/260423-ui-redesign.md new file mode 100644 index 0000000..5e17782 --- /dev/null +++ b/.agentdocs/workflow/260423-ui-redesign.md @@ -0,0 +1,170 @@ +# UI 重设计 — 清新活力 · 轻点缀 · 扁平 + +## 任务目标 + +保持现有扁平风骨架不变,通过 **配色系统 + 字体 + 圆角 + 微动效** 四个杠杆,把整体气质从"冷峻商务"切换为"明快亲和"。 + +骨架(布局结构、信息层级、组件划分、路由、间距尺度)不动;不新增组件、不新增依赖。 + +## 设计方向(已与用户对齐) + +- **风格基调**:清新活力型 — 以明快蓝为主色锚点,辅以柔和彩色点缀,对标 Linear / Raycast / Vercel 早期风格 +- **装饰丰富度**:轻点缀级 — Hero 加入柔和光晕 blob,卡片图标用彩色 tint,其余区域保持纯净 +- **字体与形状**:现代无衬线(Inter 系统栈)+ 更大圆角;font-mono 仅保留给工具内部的代码输入/输出区 + +## 当前 UI 诊断 + +- 主色 `oklch(0.208 0.042 265.755)` 是深蓝灰,饱和度极低(chroma 0.042),像企业后台 +- 首页 Hero `from-primary/90 to-gray-700` 渐变两端都很暗,白字等宽标题压头 +- 10 张工具卡片完全同质(同边框、同色图标底块、同色标题),扫视无法快速识别 +- 大量 `font-mono` 用于标题与卡片名称,强化"极客严肃"感 +- 缺少任何点缀色、微动效、有色阴影 + +## 设计规范 + +### 1. 配色系统(`src/index.css` CSS 变量层) + +浅色模式主要变更: + +``` +--background: oklch(0.99 0.005 250) 极淡冷白,避免纯白刺眼 +--primary: oklch(0.62 0.19 250) 明快蓝(≈ #3B82F6) +--primary-hover: oklch(0.56 0.20 250) hover/active 深一档 +--primary-soft: oklch(0.97 0.03 250) 极淡蓝,用于 Hero 底色与 tint +--border: oklch(0.94 0.01 250) 柔和冷灰 +--accent-cyan: oklch(0.78 0.13 200) 点缀/成功态 +--accent-coral: oklch(0.72 0.17 30) 警示/强调点缀 +``` + +深色模式主要变更: + +``` +--primary: oklch(0.70 0.16 245) 更亮的主色,保证深底上仍明快 +``` + +其余变量保持现有结构,仅按上述主色/边框变化做协调性微调。 + +### 2. 首页 Hero 与搜索区(`src/routes/index.tsx`) + +- Hero 背景:`--primary-soft` 底 + 右上/左下两个柔和径向渐变 blob(蓝/青、`blur-3xl opacity-40`),可叠加极淡点阵(opacity ≤ 0.3) +- Hero 高度:`py-8` → `py-16 md:py-20` +- 标题:去掉 `font-mono`,改为 `font-semibold`,颜色从白字改为 `text-foreground`;图标与标题对齐方式由 `items-end` 改为 `items-center` +- 新增副标题:位于主标题下方,`text-muted-foreground`,例如"10+ 开发者日常工具,开箱即用,无需安装"(i18n 配置,zh/en 各一条) +- 搜索框(`SearchBar.tsx`): + - `bg-background/80` → `bg-background` + - `rounded-lg` → `rounded-2xl` + - `shadow-sm` → `shadow-lg shadow-primary/5` + - 边框更淡 `border-border/60` + - `focus:` 态:边框变 `--primary`,外圈 `ring-4 ring-primary/20`,左侧 Search 图标颜色由 `muted-foreground` 变 `primary` + +### 3. 工具卡片(`ToolCard.tsx` + `data/tools.ts`) + +**工具主题色分配**: + +| 工具 | 主题色 | +|------|-------| +| json-formatter | blue | +| timestamp | amber | +| uuid-generator | violet | +| base64-codec | cyan | +| url-codec | indigo | +| qrcode-generator | pink | +| hash-encoder | emerald | +| html-preview | orange | +| regex-tester | fuchsia | +| ip-info | sky | + +**`Tool` 接口扩展**(`data/tools.ts`): + +```ts +export type ToolColor = + | 'blue' | 'amber' | 'violet' | 'cyan' | 'indigo' + | 'pink' | 'emerald' | 'orange' | 'fuchsia' | 'sky'; + +export interface Tool { + // ... 既有字段 + color: ToolColor; +} +``` + +**卡片样式变更**: + +- 容器 + - `bg-card/50` → `bg-card` + - `rounded-xl` → `rounded-2xl` + - 边框 `border-border/60` + - 去掉默认 `backdrop-blur-[1px]` 与默认阴影 + - hover:`-translate-y-0.5` + `shadow-lg shadow--500/10` + 边框色变为 `-300`(见下方映射) + - 过渡统一 `transition-all duration-200` +- 图标底块 + - `bg-accent/70 border border-border/50 shadow-sm p-3 rounded-lg` → `bg--100 dark:bg--500/15 p-3.5 rounded-xl`(无边框、无阴影) + - 图标尺寸 `h-5 w-5` → `h-6 w-6` + - 图标颜色 `text-primary` → `text--600 dark:text--400` +- 标题 + - `font-mono text-base font-medium` → `text-sm font-medium text-foreground/90` + - hover 可选:文字颜色变为对应主题色(见实现决策) +- 间距 + - 卡片 `p-4` → `p-5` + - 图标到文字 `mb-3` → `mb-3.5` + - 网格 `gap-3` → `gap-4` +- `ExternalLink` 图标颜色由 `muted-foreground` 改为 `muted-foreground/60` + +**Tailwind JIT 兼容**:动态颜色 class 必须通过完整字符串映射表声明,不能拼接。在 `ToolCard.tsx` 内置一张静态 `Record` 映射表。 + +### 4. 工具详情页 Header(`ToolPageHeader.tsx`) + +- 去除 `from-primary/90 to-gray-700` 渐变背景与白色文字 +- 背景改为 `bg-background`,底部加 `border-b border-border/60` +- 高度 `py-4` → `py-5` +- 左侧 Home 按钮:`text-muted-foreground hover:text-primary` +- 中间标题:`text-3xl font-bold`(白) → `text-xl md:text-2xl font-semibold`(深色、无衬线) +- 右侧 `w-16` 占位维持三栏对齐 +- 可选增强:在标题左侧加入当前工具的小尺寸主题色图标(和首页卡片的彩色语言呼应) + +### 5. Footer(`Footer.tsx`) + +- `bg-muted/40` → `bg-background` +- 去掉 `shadow-sm` +- 保留 `border-t border-border/60` 作为分隔 +- 结构不变(版权、ThemeSwitcher、LanguageSwitcher) + +### 6. 全局约定(`src/index.css` 与组件约束) + +- 字体栈:`font-sans` 指向 `Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, sans-serif`;`font-mono` 保留给 JSON 编辑器、Base64 输入框、Hash 输出等技术内容 +- 过渡动效统一 `transition-all duration-200`,禁止超过 300ms +- 圆角分层: + - `rounded-md` (6px) — tag、小按钮 + - `rounded-lg` (8px) — 输入框、普通按钮 + - `rounded-xl` (12px) — 图标底块 + - `rounded-2xl` (16px) — 卡片、搜索框 +- 阴影策略:静态元素不用阴影;交互元素 hover 时出现带主色/主题色的有色阴影(`shadow-/10`) + +## 实现决策待定项 + +以下两项留到 planning/实现阶段再决定,不阻塞设计: + +- 卡片 hover 时标题是否跟随变彩色(可能过于抢眼,实现时 A/B 观察) +- 工具详情 Header 是否显示工具图标(视集成复杂度与视觉效果决定) + +## 验证基线 + +在 `src/dev-tools-collection-web` 目录执行: + +``` +pnpm lint +pnpm build +``` + +视觉验证需在浅色模式与深色模式下分别目视检查:首页 / 任一工具详情页。 + +## 任务拆解 TODO + +- [ ] **阶段 1:配色底座** — 修改 `index.css` CSS 变量(浅色/深色双模式),确认全站基础色调切换无回归 +- [ ] **阶段 2:工具数据扩展** — 给 `tools.ts` 的 `Tool` 接口加 `color` 字段并为 10 个工具各分配主题色 +- [ ] **阶段 3:ToolCard 重构** — 引入颜色映射表,重写卡片样式(容器/图标底块/标题/hover 动效) +- [ ] **阶段 4:首页 Hero** — 重写 Hero 背景与光晕、标题字体、副标题、搜索框样式(`index.tsx` + `SearchBar.tsx` + i18n 文案) +- [ ] **阶段 5:工具详情 Header** — 去深色条、改浅底标题(`ToolPageHeader.tsx`) +- [ ] **阶段 6:Footer + 全局过渡与字体** — Footer 小调 + `index.css` 字体栈 +- [ ] **阶段 7:验收** — `pnpm lint` + `pnpm build` 通过,浅色/深色模式目视走查首页与全部 10 个工具页 + +每完成一个阶段,勾选上方对应项,不允许跨阶段累积未验证改动。 From 60ee197aeb7b60bd2314ce03d9de40f1aeb2f033 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Apr 2026 15:45:58 +0800 Subject: [PATCH 02/11] =?UTF-8?q?style:=20=E5=B0=86=E4=B8=BB=E8=89=B2?= =?UTF-8?q?=E5=88=87=E6=8D=A2=E4=B8=BA=E6=98=8E=E5=BF=AB=E8=93=9D=E5=B9=B6?= =?UTF-8?q?=E6=89=A9=E5=B1=95=E5=AD=97=E4=BD=93=E6=A0=88=E4=B8=8E=E5=9C=86?= =?UTF-8?q?=E8=A7=92=E5=9F=BA=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dev-tools-collection-web/src/index.css | 100 +++++++++++---------- 1 file changed, 53 insertions(+), 47 deletions(-) diff --git a/src/dev-tools-collection-web/src/index.css b/src/dev-tools-collection-web/src/index.css index 2355ad8..14b07b1 100644 --- a/src/dev-tools-collection-web/src/index.css +++ b/src/dev-tools-collection-web/src/index.css @@ -4,6 +4,12 @@ @custom-variant dark (&:is(.dark *)); @theme inline { + --font-sans: + Inter, -apple-system, BlinkMacSystemFont, 'Segoe UI', system-ui, sans-serif; + --font-mono: + ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, 'Liberation Mono', + monospace; + --radius-sm: calc(var(--radius) - 4px); --radius-md: calc(var(--radius) - 2px); --radius-lg: var(--radius); @@ -42,72 +48,72 @@ } :root { - --radius: 0.625rem; - --background: oklch(1 0 0); - --foreground: oklch(0.129 0.042 264.695); + --radius: 0.75rem; + --background: oklch(0.99 0.005 250); + --foreground: oklch(0.22 0.03 260); --card: oklch(1 0 0); - --card-foreground: oklch(0.129 0.042 264.695); + --card-foreground: oklch(0.22 0.03 260); --popover: oklch(1 0 0); - --popover-foreground: oklch(0.129 0.042 264.695); - --primary: oklch(0.208 0.042 265.755); - --primary-foreground: oklch(0.984 0.003 247.858); - --secondary: oklch(0.968 0.007 247.896); - --secondary-foreground: oklch(0.208 0.042 265.755); - --muted: oklch(0.968 0.007 247.896); - --muted-foreground: oklch(0.554 0.046 257.417); - --accent: oklch(0.968 0.007 247.896); - --accent-foreground: oklch(0.208 0.042 265.755); - --destructive: oklch(0.577 0.245 27.325); - --border: oklch(0.929 0.013 255.508); - --input: oklch(0.929 0.013 255.508); - --ring: oklch(0.704 0.04 256.788); + --popover-foreground: oklch(0.22 0.03 260); + --primary: oklch(0.62 0.19 250); + --primary-foreground: oklch(0.99 0.005 250); + --secondary: oklch(0.97 0.015 250); + --secondary-foreground: oklch(0.28 0.05 260); + --muted: oklch(0.97 0.015 250); + --muted-foreground: oklch(0.52 0.03 255); + --accent: oklch(0.95 0.03 250); + --accent-foreground: oklch(0.28 0.05 260); + --destructive: oklch(0.62 0.22 27); + --border: oklch(0.93 0.015 250); + --input: oklch(0.93 0.015 250); + --ring: oklch(0.62 0.19 250); --chart-1: oklch(0.646 0.222 41.116); --chart-2: oklch(0.6 0.118 184.704); --chart-3: oklch(0.398 0.07 227.392); --chart-4: oklch(0.828 0.189 84.429); --chart-5: oklch(0.769 0.188 70.08); --sidebar: oklch(0.984 0.003 247.858); - --sidebar-foreground: oklch(0.129 0.042 264.695); - --sidebar-primary: oklch(0.208 0.042 265.755); - --sidebar-primary-foreground: oklch(0.984 0.003 247.858); - --sidebar-accent: oklch(0.968 0.007 247.896); - --sidebar-accent-foreground: oklch(0.208 0.042 265.755); - --sidebar-border: oklch(0.929 0.013 255.508); - --sidebar-ring: oklch(0.704 0.04 256.788); + --sidebar-foreground: oklch(0.22 0.03 260); + --sidebar-primary: oklch(0.62 0.19 250); + --sidebar-primary-foreground: oklch(0.99 0.005 250); + --sidebar-accent: oklch(0.95 0.03 250); + --sidebar-accent-foreground: oklch(0.28 0.05 260); + --sidebar-border: oklch(0.93 0.015 250); + --sidebar-ring: oklch(0.62 0.19 250); } .dark { - --background: oklch(0.129 0.042 264.695); - --foreground: oklch(0.984 0.003 247.858); - --card: oklch(0.208 0.042 265.755); - --card-foreground: oklch(0.984 0.003 247.858); - --popover: oklch(0.208 0.042 265.755); - --popover-foreground: oklch(0.984 0.003 247.858); - --primary: oklch(0.929 0.013 255.508); - --primary-foreground: oklch(0.208 0.042 265.755); - --secondary: oklch(0.279 0.041 260.031); - --secondary-foreground: oklch(0.984 0.003 247.858); - --muted: oklch(0.279 0.041 260.031); - --muted-foreground: oklch(0.704 0.04 256.788); - --accent: oklch(0.279 0.041 260.031); - --accent-foreground: oklch(0.984 0.003 247.858); - --destructive: oklch(0.704 0.191 22.216); + --background: oklch(0.16 0.02 260); + --foreground: oklch(0.97 0.01 250); + --card: oklch(0.22 0.03 260); + --card-foreground: oklch(0.97 0.01 250); + --popover: oklch(0.22 0.03 260); + --popover-foreground: oklch(0.97 0.01 250); + --primary: oklch(0.7 0.16 245); + --primary-foreground: oklch(0.16 0.02 260); + --secondary: oklch(0.28 0.04 260); + --secondary-foreground: oklch(0.97 0.01 250); + --muted: oklch(0.28 0.04 260); + --muted-foreground: oklch(0.72 0.03 255); + --accent: oklch(0.28 0.04 260); + --accent-foreground: oklch(0.97 0.01 250); + --destructive: oklch(0.7 0.19 22); --border: oklch(1 0 0 / 10%); --input: oklch(1 0 0 / 15%); - --ring: oklch(0.551 0.027 264.364); + --ring: oklch(0.7 0.16 245); --chart-1: oklch(0.488 0.243 264.376); --chart-2: oklch(0.696 0.17 162.48); --chart-3: oklch(0.769 0.188 70.08); --chart-4: oklch(0.627 0.265 303.9); --chart-5: oklch(0.645 0.246 16.439); - --sidebar: oklch(0.208 0.042 265.755); - --sidebar-foreground: oklch(0.984 0.003 247.858); - --sidebar-primary: oklch(0.488 0.243 264.376); - --sidebar-primary-foreground: oklch(0.984 0.003 247.858); - --sidebar-accent: oklch(0.279 0.041 260.031); - --sidebar-accent-foreground: oklch(0.984 0.003 247.858); + --sidebar: oklch(0.22 0.03 260); + --sidebar-foreground: oklch(0.97 0.01 250); + --sidebar-primary: oklch(0.7 0.16 245); + --sidebar-primary-foreground: oklch(0.16 0.02 260); + --sidebar-accent: oklch(0.28 0.04 260); + --sidebar-accent-foreground: oklch(0.97 0.01 250); --sidebar-border: oklch(1 0 0 / 10%); - --sidebar-ring: oklch(0.551 0.027 264.364); + --sidebar-ring: oklch(0.7 0.16 245); } @layer base { From f26c1f503ed94a1cc2210cce48a2609b0fc863c7 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Apr 2026 15:53:36 +0800 Subject: [PATCH 03/11] =?UTF-8?q?feat(tools):=20=E4=B8=BA=20Tool=20?= =?UTF-8?q?=E6=8E=A5=E5=8F=A3=E6=B7=BB=E5=8A=A0=20color=20=E5=AD=97?= =?UTF-8?q?=E6=AE=B5=E5=B9=B6=E5=88=86=E9=85=8D=E5=90=84=E5=B7=A5=E5=85=B7?= =?UTF-8?q?=E4=B8=BB=E9=A2=98=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/data/tools.ts | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/dev-tools-collection-web/src/data/tools.ts b/src/dev-tools-collection-web/src/data/tools.ts index eeed462..2c11022 100644 --- a/src/dev-tools-collection-web/src/data/tools.ts +++ b/src/dev-tools-collection-web/src/data/tools.ts @@ -14,12 +14,25 @@ import { import type { ForwardRefExoticComponent, RefAttributes } from 'react'; import type { i18n as I18nInstance } from 'i18next'; +export type ToolColor = + | 'blue' + | 'amber' + | 'violet' + | 'cyan' + | 'indigo' + | 'pink' + | 'emerald' + | 'orange' + | 'fuchsia' + | 'sky'; + export interface Tool { id: string; icon: ForwardRefExoticComponent< Omit & RefAttributes >; url: string; + color: ToolColor; popular?: boolean; /** 外链工具:存在时点击卡片在新标签页打开该 URL,不走站内路由 */ externalUrl?: string; @@ -30,54 +43,63 @@ export const toolsData: Tool[] = [ id: 'json-formatter', icon: Braces, url: '/tools/json-formatter', + color: 'blue', popular: true }, { id: 'timestamp', icon: Clock, url: '/tools/timestamp', + color: 'amber', popular: true }, { id: 'uuid-generator', icon: RotateCcwKey, url: '/tools/uuid-generator', + color: 'violet', popular: true }, { id: 'base64-codec', icon: Code, url: '/tools/base64-codec', + color: 'cyan', popular: true }, { id: 'url-codec', icon: Link, url: '/tools/url-codec', + color: 'indigo', popular: true }, { id: 'qrcode-generator', icon: QrCode, url: '/tools/qrcode-generator', + color: 'pink', popular: true }, { id: 'hash-encoder', icon: Hash, url: '/tools/hash-encoder', + color: 'emerald', popular: true }, { id: 'html-preview', icon: FileCode, url: '/tools/html-preview', + color: 'orange', popular: true }, { id: 'regex-tester', icon: Regex, url: '/tools/regex-tester', + color: 'fuchsia', popular: true }, { @@ -85,6 +107,7 @@ export const toolsData: Tool[] = [ icon: Globe, url: 'https://ipinfo.io/what-is-my-ip', externalUrl: 'https://ipinfo.io/what-is-my-ip', + color: 'sky', popular: true } ]; From 4b2ae82d3f4dc222f5cc5e158445c4e802a27f63 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Apr 2026 16:09:04 +0800 Subject: [PATCH 04/11] =?UTF-8?q?feat(tool-card):=20=E5=BC=95=E5=85=A5?= =?UTF-8?q?=E5=BD=A9=E8=89=B2=E6=98=A0=E5=B0=84=E8=A1=A8=E5=B9=B6=E5=90=AF?= =?UTF-8?q?=E7=94=A8=E5=BD=A9=E8=89=B2=E5=9B=BE=E6=A0=87=E4=B8=8E=20hover?= =?UTF-8?q?=20=E5=BE=AE=E5=8A=A8=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ToolCard.tsx | 106 ++++++++++++++++-- 1 file changed, 97 insertions(+), 9 deletions(-) diff --git a/src/dev-tools-collection-web/src/components/ToolCard.tsx b/src/dev-tools-collection-web/src/components/ToolCard.tsx index ab05117..3d8f2f6 100644 --- a/src/dev-tools-collection-web/src/components/ToolCard.tsx +++ b/src/dev-tools-collection-web/src/components/ToolCard.tsx @@ -1,5 +1,5 @@ import { Card, CardContent } from '@/components/ui/card'; -import type { Tool } from '../data/tools'; +import type { Tool, ToolColor } from '../data/tools'; import { Link } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; import { ExternalLink } from 'lucide-react'; @@ -8,25 +8,111 @@ interface ToolCardProps { tool: Tool; } +// 静态颜色映射表:Tailwind JIT 必须看到完整 class 字符串才能打包, +// 因此这里不能用字符串拼接(如 `bg-${color}-100`),只能逐项列出。 +const colorMap: Record< + ToolColor, + { + iconBg: string; + iconText: string; + hoverBorder: string; + hoverShadow: string; + } +> = { + blue: { + iconBg: 'bg-blue-100 dark:bg-blue-500/15', + iconText: 'text-blue-600 dark:text-blue-400', + hoverBorder: + 'group-hover:border-blue-300 dark:group-hover:border-blue-500/40', + hoverShadow: 'group-hover:shadow-blue-500/10' + }, + amber: { + iconBg: 'bg-amber-100 dark:bg-amber-500/15', + iconText: 'text-amber-600 dark:text-amber-400', + hoverBorder: + 'group-hover:border-amber-300 dark:group-hover:border-amber-500/40', + hoverShadow: 'group-hover:shadow-amber-500/10' + }, + violet: { + iconBg: 'bg-violet-100 dark:bg-violet-500/15', + iconText: 'text-violet-600 dark:text-violet-400', + hoverBorder: + 'group-hover:border-violet-300 dark:group-hover:border-violet-500/40', + hoverShadow: 'group-hover:shadow-violet-500/10' + }, + cyan: { + iconBg: 'bg-cyan-100 dark:bg-cyan-500/15', + iconText: 'text-cyan-600 dark:text-cyan-400', + hoverBorder: + 'group-hover:border-cyan-300 dark:group-hover:border-cyan-500/40', + hoverShadow: 'group-hover:shadow-cyan-500/10' + }, + indigo: { + iconBg: 'bg-indigo-100 dark:bg-indigo-500/15', + iconText: 'text-indigo-600 dark:text-indigo-400', + hoverBorder: + 'group-hover:border-indigo-300 dark:group-hover:border-indigo-500/40', + hoverShadow: 'group-hover:shadow-indigo-500/10' + }, + pink: { + iconBg: 'bg-pink-100 dark:bg-pink-500/15', + iconText: 'text-pink-600 dark:text-pink-400', + hoverBorder: + 'group-hover:border-pink-300 dark:group-hover:border-pink-500/40', + hoverShadow: 'group-hover:shadow-pink-500/10' + }, + emerald: { + iconBg: 'bg-emerald-100 dark:bg-emerald-500/15', + iconText: 'text-emerald-600 dark:text-emerald-400', + hoverBorder: + 'group-hover:border-emerald-300 dark:group-hover:border-emerald-500/40', + hoverShadow: 'group-hover:shadow-emerald-500/10' + }, + orange: { + iconBg: 'bg-orange-100 dark:bg-orange-500/15', + iconText: 'text-orange-600 dark:text-orange-400', + hoverBorder: + 'group-hover:border-orange-300 dark:group-hover:border-orange-500/40', + hoverShadow: 'group-hover:shadow-orange-500/10' + }, + fuchsia: { + iconBg: 'bg-fuchsia-100 dark:bg-fuchsia-500/15', + iconText: 'text-fuchsia-600 dark:text-fuchsia-400', + hoverBorder: + 'group-hover:border-fuchsia-300 dark:group-hover:border-fuchsia-500/40', + hoverShadow: 'group-hover:shadow-fuchsia-500/10' + }, + sky: { + iconBg: 'bg-sky-100 dark:bg-sky-500/15', + iconText: 'text-sky-600 dark:text-sky-400', + hoverBorder: + 'group-hover:border-sky-300 dark:group-hover:border-sky-500/40', + hoverShadow: 'group-hover:shadow-sky-500/10' + } +}; + const ToolCard = ({ tool }: ToolCardProps) => { const { t } = useTranslation(); const label = t(`tools.${tool.id}`); const isExternal = Boolean(tool.externalUrl); + const palette = colorMap[tool.color]; const cardContent = ( - + {isExternal && (
-
- +
+
-

+

{label}

@@ -34,13 +120,15 @@ const ToolCard = ({ tool }: ToolCardProps) => { ); + const wrapperClass = 'group block'; + if (isExternal) { return (
{cardContent} @@ -49,7 +137,7 @@ const ToolCard = ({ tool }: ToolCardProps) => { } return ( - + {cardContent} ); From 96a4983fe6a6b6e6b00998fe56334088ef98f720 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Apr 2026 16:16:23 +0800 Subject: [PATCH 05/11] =?UTF-8?q?refactor(tool-card):=20=E4=BD=BF=E7=94=A8?= =?UTF-8?q?=20cn=20=E5=B9=B6=E7=A7=BB=E9=99=A4=20Card=20=E4=B8=8A=E5=86=97?= =?UTF-8?q?=E4=BD=99=E7=9A=84=20group=20=E7=B1=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dev-tools-collection-web/src/components/ToolCard.tsx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/dev-tools-collection-web/src/components/ToolCard.tsx b/src/dev-tools-collection-web/src/components/ToolCard.tsx index 3d8f2f6..9f92365 100644 --- a/src/dev-tools-collection-web/src/components/ToolCard.tsx +++ b/src/dev-tools-collection-web/src/components/ToolCard.tsx @@ -1,4 +1,5 @@ import { Card, CardContent } from '@/components/ui/card'; +import { cn } from '@/lib/utils'; import type { Tool, ToolColor } from '../data/tools'; import { Link } from '@tanstack/react-router'; import { useTranslation } from 'react-i18next'; @@ -99,7 +100,12 @@ const ToolCard = ({ tool }: ToolCardProps) => { const cardContent = ( {isExternal && ( Date: Thu, 23 Apr 2026 16:20:06 +0800 Subject: [PATCH 06/11] =?UTF-8?q?i18n:=20=E6=96=B0=E5=A2=9E=E9=A6=96?= =?UTF-8?q?=E9=A1=B5=E5=89=AF=E6=A0=87=E9=A2=98=E6=96=87=E6=A1=88=20common?= =?UTF-8?q?.tagline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dev-tools-collection-web/src/localization/en.ts | 1 + src/dev-tools-collection-web/src/localization/zh.ts | 1 + 2 files changed, 2 insertions(+) diff --git a/src/dev-tools-collection-web/src/localization/en.ts b/src/dev-tools-collection-web/src/localization/en.ts index 5cfd7dc..85d1f7d 100644 --- a/src/dev-tools-collection-web/src/localization/en.ts +++ b/src/dev-tools-collection-web/src/localization/en.ts @@ -3,6 +3,7 @@ export default { toolbox: 'Online Toolbox', description: 'Provides various practical tools to improve your work efficiency', + tagline: '10+ developer tools, ready to use, no install required', searchResults: 'Search Results', allTools: 'All Tools', tool: 'Tool', diff --git a/src/dev-tools-collection-web/src/localization/zh.ts b/src/dev-tools-collection-web/src/localization/zh.ts index 3140947..89c5bc9 100644 --- a/src/dev-tools-collection-web/src/localization/zh.ts +++ b/src/dev-tools-collection-web/src/localization/zh.ts @@ -2,6 +2,7 @@ export default { common: { toolbox: '在线工具箱', description: '提供各种实用工具,提高您的工作效率', + tagline: '10+ 开发者日常工具,开箱即用,无需安装', searchResults: '搜索结果', allTools: '所有工具', tool: '工具', From fe6a673abdb877206d20618bb92c787cd0e9a87f Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Apr 2026 16:25:12 +0800 Subject: [PATCH 07/11] =?UTF-8?q?style(search):=20=E6=94=BE=E5=A4=A7?= =?UTF-8?q?=E5=9C=86=E8=A7=92/=E9=87=87=E7=94=A8=E5=B8=A6=E4=B8=BB?= =?UTF-8?q?=E8=89=B2=E7=9A=84=E6=9C=89=E8=89=B2=E9=98=B4=E5=BD=B1=E4=B8=8E?= =?UTF-8?q?=20focus=20ring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dev-tools-collection-web/src/components/SearchBar.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/dev-tools-collection-web/src/components/SearchBar.tsx b/src/dev-tools-collection-web/src/components/SearchBar.tsx index 2f4b2ed..3914852 100644 --- a/src/dev-tools-collection-web/src/components/SearchBar.tsx +++ b/src/dev-tools-collection-web/src/components/SearchBar.tsx @@ -26,15 +26,15 @@ const SearchBar = ({ onSearchResults }: SearchBarProps) => { return (
-
+
setQuery(e.target.value)} /> From da404ada88b422728a59588cbaf1a7da8872a342 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Apr 2026 16:32:43 +0800 Subject: [PATCH 08/11] =?UTF-8?q?style(home):=20=E9=87=8D=E5=86=99=20Hero?= =?UTF-8?q?=20=E4=B8=BA=E6=B5=85=E5=BA=95=E5=85=89=E6=99=95=E5=B8=83?= =?UTF-8?q?=E5=B1=80=E5=B9=B6=E5=8A=A0=E5=85=A5=E5=89=AF=E6=A0=87=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/routes/index.tsx | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/dev-tools-collection-web/src/routes/index.tsx b/src/dev-tools-collection-web/src/routes/index.tsx index 04cf697..f2ccdd2 100644 --- a/src/dev-tools-collection-web/src/routes/index.tsx +++ b/src/dev-tools-collection-web/src/routes/index.tsx @@ -24,27 +24,38 @@ function Index() { return (
-
-
-
+
+ {/* 柔和光晕 blob —— 增强明快感但保持扁平 */} +
-
+
{searchResults.length > 0 ? (
-

+

{t('common.searchResults')}

-
+
{searchResults.map(tool => ( ))} @@ -52,10 +63,10 @@ function Index() {
) : (
-

+

{t('common.allTools')}

-
+
{toolsData.map(tool => ( ))} From 5c463f434ce7ba93b199c3cbe72c4a663c82c55c Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Apr 2026 16:38:26 +0800 Subject: [PATCH 09/11] =?UTF-8?q?style(tool-header):=20=E5=8E=BB=E9=99=A4?= =?UTF-8?q?=E6=B7=B1=E8=89=B2=E6=B8=90=E5=8F=98=E6=9D=A1=E6=94=B9=E4=B8=BA?= =?UTF-8?q?=E6=B5=85=E5=BA=95=E5=88=86=E9=9A=94=E7=BA=BF=E7=9A=84=E8=BD=BB?= =?UTF-8?q?=E9=87=8F=E9=A1=B6=E6=A0=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/ToolPageHeader.tsx | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/src/dev-tools-collection-web/src/components/ToolPageHeader.tsx b/src/dev-tools-collection-web/src/components/ToolPageHeader.tsx index c0916a0..f0a24e1 100644 --- a/src/dev-tools-collection-web/src/components/ToolPageHeader.tsx +++ b/src/dev-tools-collection-web/src/components/ToolPageHeader.tsx @@ -10,19 +10,21 @@ const ToolPageHeader = ({ title }: ToolPageHeaderProps) => { const { t } = useTranslation(); return ( -
-
-
+
+
+
- - {t('common.home')} + + {t('common.home')}
-

{title}

-
+

+ {title} +

+
); From ef5459b20c33ed91332baf2eea072e2421cfe4d0 Mon Sep 17 00:00:00 2001 From: Test User Date: Thu, 23 Apr 2026 16:40:58 +0800 Subject: [PATCH 10/11] =?UTF-8?q?style(footer):=20=E5=8E=BB=E9=99=A4=20mut?= =?UTF-8?q?ed=20=E5=BA=95=E8=89=B2=E4=B8=8E=E9=98=B4=E5=BD=B1=E6=94=B9?= =?UTF-8?q?=E4=B8=BA=E8=BD=BB=E9=87=8F=E5=88=86=E9=9A=94=E7=BA=BF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/dev-tools-collection-web/src/components/Footer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/dev-tools-collection-web/src/components/Footer.tsx b/src/dev-tools-collection-web/src/components/Footer.tsx index 8d3028a..de61dfe 100644 --- a/src/dev-tools-collection-web/src/components/Footer.tsx +++ b/src/dev-tools-collection-web/src/components/Footer.tsx @@ -7,7 +7,7 @@ const Footer = () => { const currentYear = new Date().getFullYear(); return ( -