Browse Source

feat(skills): 新增 frontend-design 前端设计美学 skill

按技术栈拆分为 SKILL.md 核心规范 + Tailwind v4 / Ant Design v6 / Laravel Blade / design-tokens 参考,供 DSH skill 机制自动加载
visuddhinanda 3 days ago
parent
commit
4d2f781333

+ 95 - 0
.agents/skills/frontend-design/SKILL.md

@@ -0,0 +1,95 @@
+---
+name: frontend-design
+description: 前端视觉设计与美学规范:视觉层级、8pt 间距、60-30-10 配色、字号阶梯、组件质量与响应式。覆盖 Tailwind CSS v4、Ant Design v6、React/Inertia、Laravel Blade 与原生 HTML/CSS。目标是让生成/修改的界面「美观、专业、克制」而不只是能用。
+whenToUse: 生成或修改任何页面、组件、样式时;用户要求「好看 / 美观 / 美化 / 重设计 / 更现代 / 精致」时;编写 HTML、CSS、React、Blade、Tailwind、Ant Design 代码时。
+---
+
+# Frontend Design 美学与实现规范
+
+> 目标:让界面在「能用」之上达到「美观、专业、克制」。美观不是堆特效,而是 **一致性 + 层次 + 留白 + 克制的色彩**。
+
+## 0. 动手前:先对齐现有系统(最高优先级)
+
+1. 先读项目现有代码,找到已有的设计系统 / 主题 / 组件,**优先复用,绝不另起炉灶**。
+2. 本仓库技术栈分工:
+   - `api-v13/`:Laravel + Inertia(React 19)+ **Tailwind CSS v4**(主样式方案),图标用 `@tabler/icons` + FontAwesome。
+   - `dashboard-v6/`:React 19 + **Ant Design v6**(`antd` + ProComponents + `@ant-design/charts`)。
+   - 存量页面可能残留 Bootstrap 5 / Bulma / 原生 CSS——只在维护它们时才用对应框架;**新建页面一律用该子项目的主方案**。
+3. 一个子项目只允许一套主样式体系:不混用 Tailwind 与 Bootstrap/Bulma 于同一页面。
+
+## 1. 核心美学原则
+
+### 1.1 视觉层级:一屏只有一个主角
+- 每个页面有且仅有一个**主焦点**(标题、关键数据或主操作)。
+- 用「字号 / 字重 / 颜色 / 间距」四件套制造层级,而不是「更大 + 更亮 + 更粗」一起堆。
+- 次要信息主动降噪:降低对比度、缩小字号、移到次要位置。
+
+### 1.2 间距:8pt 网格
+- 间距一律取 4 或 8 的倍数:`4 8 12 16 24 32 48 64 96`。
+- 卡片内边距 `16 / 20 / 24`;卡片间距 `16 / 24`;区块间距 `32 / 48 / 64`。
+- 禁止出现 `7px`、`13px`、`19px` 这类零碎值。
+
+### 1.3 色彩:60-30-10 + 语义色 + 对比度
+- 60% 中性底色(背景 / 卡片)、30% 主色(品牌 / 交互)、10% 强调色(关键状态 / CTA)。
+- 主色只用于交互与强调,**不要大面积铺满**。
+- 状态色语义固定:success=绿、warning=黄/橙、danger=红、info=蓝;不要用彩色随意表达「好看」。
+- 文字与背景对比度满足 WCAG AA(正文 ≥ 4.5:1,大字 ≥ 3:1)。灰色文字不要浅到看不清。
+
+### 1.4 排版
+- 每个页面用**同一套字号阶梯**(见 `references/design-tokens.md`),不即兴造字号。
+- 标题与正文要有明确字重 / 字号对比;正文行高 1.5–1.6,标题行高 1.2–1.3。
+- 数字 / 金额 / 表格用 tabular-nums 并右对齐,便于扫描。
+
+### 1.5 留白与密度
+- 默认**宁可多留白**:拥挤是「丑」的头号来源。
+- 相关元素靠得近(成组),不相关元素拉开距离(分隔)。
+
+### 1.6 一致性(重中之重)
+- 圆角、阴影、边框、图标风格、按钮尺寸在一页内**统一**。
+- 圆角:小元素 `6–8px`、卡片 `10–12px`、全圆(头像 / 徽章)。同一界面最多 2 档圆角。
+- 阴影只用 1–2 层、且非常克制(hover / 浮层才用),不要到处投影。
+
+### 1.7 克制
+- 动画只用于反馈(hover / 进入 / 加载),时长 150–250ms,缓动一致。
+- 少用高饱和渐变、玻璃拟态、发光、大图背景;需要时只做点缀。
+- 图标统一一套(`@tabler/icons` 或 antd icons),不混搭风格。
+
+## 2. 布局模式(可直接套用)
+
+- **卡片**:标题 + 正文 + 操作区(右下角)。用边框或浅色底代替重阴影。
+- **表单**:标签在输入框上方、左对齐;必填用 `*`;错误信息贴近字段且为红色;主提交按钮位置固定。
+- **表格**:表头加粗、行高 48–56px、斑马纹可选但弱;数字右对齐;操作列固定右侧。
+- **导航**:当前项高亮(主色或字重),hover 有反馈;层级清晰。
+- **空状态**:图标 + 一句说明 + 一个行动按钮,不要留白一片。
+- **加载态**:骨架屏或 spinner + 文案;不要在空白里闪一下。
+- **错误 / 异常态**:清楚说明「发生了什么 + 用户可以做什么」,不要只抛红字。
+
+## 3. 组件质量红线(每个组件交付前自查)
+
+- [ ] 交互元素有 hover / focus / disabled 三态。
+- [ ] 键盘可达,表单 / 弹窗 / 菜单有正确的 `aria-*`。
+- [ ] 长文本不截断不溢出(ellipsis 或换行策略)。
+- [ ] 响应式:至少 3 档断点不塌;移动端可点区域 ≥ 44×44px。
+- [ ] 间距、圆角、阴影、颜色来自 token,不写魔法数字。
+- [ ] 空 / 加载 / 错误三态齐全。
+
+## 4. 技术栈指引(按子项目阅读对应参考)
+
+- Tailwind CSS v4(api-v13 主方案)→ 读 `references/tailwind-v4.md`
+- Ant Design v6(dashboard-v6)→ 读 `references/ant-design-v6.md`
+- Laravel Blade + Inertia → 读 `references/laravel-blade.md`
+- 具体 token 数值 → 读 `references/design-tokens.md`
+
+## 5. 反模式(出现即视为不合格)
+
+- ❌ 原生浏览器默认样式直接上线(默认按钮、无样式表格、蓝色下划线链接墙)。
+- ❌ 满屏高饱和色 / 彩虹配色 / 大面积纯黑纯白强对比。
+- ❌ 间距凌乱(`7px / 13px`)或元素挤成一团。
+- ❌ 同一页面混用多套圆角、多套阴影、多套图标风格。
+- ❌ 纯功能无状态:没有空态、加载态、错误态、hover 反馈。
+- ❌ 无响应式:只适配一种宽度。
+- ❌ 文案未对齐、数字不对齐、按钮大小不一。
+
+## 6. 完成标准(Definition of Done)
+
+提交前逐条确认:层级清晰、8pt 间距、色彩克制且对比达标、字号阶梯统一、圆角 / 阴影 / 图标统一、三态齐全、响应式通过、无魔法数字、与项目现有设计系统一致。

+ 74 - 0
.agents/skills/frontend-design/references/ant-design-v6.md

@@ -0,0 +1,74 @@
+# Ant Design v6(dashboard-v6)
+
+> dashboard-v6 用 **antd v6**(`antd` + `@ant-design/pro-components` + `@ant-design/charts`/`plots`)。核心:**用 ConfigProvider 的 theme 统一风格,不要在每个组件上写内联样式**。
+
+## 统一主题(最重要)
+
+在应用根节点用 `ConfigProvider` 设 token,全站一致:
+
+```tsx
+import { ConfigProvider, theme } from 'antd'
+
+<ConfigProvider
+  theme={{
+    token: {
+      colorPrimary: '#2563eb',
+      borderRadius: 8,
+      fontSize: 14,
+      colorText: '#1f2937',
+      colorTextSecondary: '#6b7280',
+      colorBorder: '#e5e7eb',
+    },
+    components: {
+      Table: { headerBg: '#f8fafc' },
+      Card: { borderRadiusLG: 12 },
+    },
+  }}
+>
+  <App />
+</ConfigProvider>
+```
+
+- 颜色、圆角、字号都在 token 层定义;单个组件的特殊样式用 `components.*` 覆盖,而非散落 `style={{...}}`。
+- 主色、语义色(`colorSuccess` / `colorWarning` / `colorError` / `colorInfo`)与 `design-tokens.md` 对齐。
+
+## ProComponents 用法
+
+- `ProTable`:表格优先用它,自带查询表单、分页、工具栏;列定义里数字列用 `align: 'right'`。
+- `ProForm`:表单优先用它,`ProFormText` / `ProFormSelect` 等;校验错误、必填标记自动处理。
+- 不要为了「看起来高级」而手写原生 table/form 绕开 Pro 组件——那会破坏一致性。
+
+## 图表(@ant-design/charts)
+
+- 图表配色与主色一致,最多 5–6 种区分色,不要默认彩虹色。
+- 数字轴右对齐、单位标注清楚;图例可读;空数据给占位提示。
+
+```tsx
+import { Column } from '@ant-design/plots'
+
+<Column
+  data={rows}
+  xField="month"
+  yField="value"
+  color="#2563eb"
+  columnStyle={{ radius: [6, 6, 0, 0] }}
+/>
+```
+
+## 布局与间距
+
+- 用 antd 的 `Space`(`size` 取 8 / 16 / 24)和 `Row/Col`(`gutter` 取 16 / 24)控制间距,不写零碎 `margin`。
+- 卡片间距 `16`,区块间距 `24`,与 8pt 网格一致。
+- 表格行高、弹窗宽度、按钮尺寸都用默认档位,不要逐处改。
+
+## 状态与反馈
+
+- 空状态:`Empty` + 一句说明 + 一个行动按钮,不要空白。
+- 加载:`Skeleton` 或 `Spin`,表格用 `Table loading` / `ProTable` 自带。
+- 错误:`Alert` 或 `Result status="error"` 说明「发生什么 + 怎么处理」。
+
+## 一致性红线
+
+- 同一页面圆角 / 阴影 / 图标风格统一(图标用 `@ant-design/icons`)。
+- 别混用 antd 组件与手写 div 模拟相同语义(如用 div 造按钮)。
+- 弹窗、抽屉、消息(`message` / `notification`)走 antd 全局实例,不自己写。

+ 81 - 0
.agents/skills/frontend-design/references/design-tokens.md

@@ -0,0 +1,81 @@
+# Design Tokens(具体数值)
+
+> 统一的设计变量。Tailwind 项目落到 `@theme`,AntD 项目落到 `ConfigProvider` 的 `theme.token`。同一界面不要出现 token 之外的魔法数值。
+
+## 中性色(背景 / 文字 / 边框)
+
+| Token | 值 | 用途 |
+|---|---|---|
+| `bg-page` | `#f7f8fa`(或 `slate-50`) | 页面底色 |
+| `bg-surface` | `#ffffff` | 卡片 / 面板 |
+| `text-primary` | `#1f2937`(`slate-800`) | 主文字 |
+| `text-secondary` | `#6b7280`(`slate-500`) | 次要文字 |
+| `text-muted` | `#9ca3af`(`slate-400`) | 占位 / 禁用 |
+| `border` | `#e5e7eb`(`slate-200`) | 分割线 / 卡片描边 |
+
+主文字与次要文字、次要与占位之间要拉开对比,避免「一片灰」。
+
+## 主色与强调色
+
+| Token | 建议值 | 用途 |
+|---|---|---|
+| `brand` | `#2563eb`(`blue-600`) | 主操作 / 链接 / 选中态 |
+| `brand-hover` | `#1d4ed8`(`blue-700`) | 主色 hover |
+| `brand-soft` | `#eff6ff`(`blue-50`) | 选中底色 / 浅底标签 |
+
+> 主色可换成项目已有的品牌色,但**选定后全局统一**,不要一个页面两个主色。
+
+## 语义色
+
+| 语义 | 前景 | 浅底 | 描边 |
+|---|---|---|---|
+| success | `#16a34a` | `#f0fdf4` | `#bbf7d0` |
+| warning | `#d97706` | `#fffbeb` | `#fde68a` |
+| danger | `#dc2626` | `#fef2f2` | `#fecaca` |
+| info | `#2563eb` | `#eff6ff` | `#bfdbfe` |
+
+状态提示统一用「浅底 + 前景字 + 描边」三件套,不要用纯色块糊脸。
+
+## 间距阶梯(8pt)
+
+`0 4 8 12 16 24 32 48 64 96`
+
+- 组件内部元素间:`4 / 8 / 12`
+- 卡片内边距:`16 / 20 / 24`
+- 卡片间距:`16 / 24`
+- 区块间距:`32 / 48 / 64`
+
+## 字号阶梯
+
+| 层级 | 字号 | 行高 | 字重 |
+|---|---|---|---|
+| display(页面大标题) | 30 / 36 | 1.2 | 700 |
+| h1 | 24 | 1.25 | 700 |
+| h2 | 20 | 1.3 | 600 |
+| h3 | 16 | 1.4 | 600 |
+| body | 14 | 1.6 | 400 |
+| small / caption | 12 | 1.5 | 400 |
+
+> 具体字号可随项目微调,但**比例关系要稳定**:每一级差约 1.2–1.25 倍,不要相邻两级只差 1px。
+
+## 圆角
+
+| Token | 值 | 用途 |
+|---|---|---|
+| `radius-sm` | 6px | 按钮 / 输入框 / 徽章 |
+| `radius-md` | 10px | 卡片 / 弹窗 |
+| `radius-full` | 9999px | 头像 / 胶囊 / 全圆徽章 |
+
+同一界面最多 2 档圆角。
+
+## 阴影(克制使用)
+
+| Token | 值 | 用途 |
+|---|---|---|
+| `shadow-sm` | `0 1px 2px rgba(0,0,0,.05)` | 卡片默认 |
+| `shadow-md` | `0 4px 12px rgba(0,0,0,.08)` | 悬浮 / 弹窗 |
+| `shadow-lg` | `0 12px 32px rgba(0,0,0,.12)` | 下拉 / 模态框 |
+
+## z-index 约定
+
+`0 内容` → `10 吸顶导航` → `20 下拉/浮层` → `30 模态框` → `40 toast/通知`

+ 98 - 0
.agents/skills/frontend-design/references/laravel-blade.md

@@ -0,0 +1,98 @@
+# Laravel Blade + Inertia
+
+> api-v13 是 Laravel + **Inertia(React)** 项目:页面主体是 Inertia 的 React 页面,Blade 负责应用外壳与纯服务端页面。两者都用 Tailwind v4 样式。
+
+## Blade 目录与职责
+
+- `resources/views/layouts/`:布局骨架(`@yield` / `@stack`)。
+- `resources/views/components/`:匿名组件(`<x-*>`)或类组件。
+- `resources/views/partials/`:可复用片段(`@include`)。
+- `resources/js/Pages/`:Inertia 页面(React 组件)。
+
+## 布局继承
+
+```blade
+{{-- layouts/app.blade.php --}}
+<!DOCTYPE html>
+<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1">
+  @vite(['resources/css/app.css', 'resources/js/app.jsx'])
+  @stack('head')
+</head>
+<body class="bg-slate-50 text-slate-800">
+  <main>@yield('content')</main>
+  @stack('scripts')
+</body>
+</html>
+```
+
+```blade
+{{-- 页面 --}}
+@extends('layouts.app')
+
+@section('content')
+  <x-card class="max-w-2xl">
+    <h1 class="text-2xl font-bold">标题</h1>
+    {{-- ... --}}
+  </x-card>
+@endsection
+```
+
+## 匿名组件(推荐)
+
+```blade
+{{-- resources/views/components/card.blade.php --}}
+@props(['title' => null])
+
+<div {{ $attributes->merge(['class' => 'rounded-md border border-slate-200 bg-white p-6']) }}>
+  @if ($title)
+    <h3 class="text-base font-semibold text-slate-800">{{ $title }}</h3>
+  @endif
+  {{ $slot }}
+</div>
+```
+
+- 用 `$attributes->merge(['class' => ...])` 让外部能追加 class,别把样式写死在外层 div 上。
+- 需要透传数据的用类组件(`php artisan make:component`),否则优先匿名组件。
+
+## 表单与安全
+
+```blade
+<form method="POST" action="{{ route('term.update', $term) }}" class="space-y-4">
+  @csrf
+  @method('PATCH')
+  {{-- 字段 --}}
+  <button type="submit" class="...">保存</button>
+</form>
+```
+
+- 写操作必带 `@csrf`,PUT/PATCH/DELETE 带 `@method`。
+- 校验错误用 `@error('field') <p class="text-xs text-red-600">{{ $message }}</p> @enderror`。
+
+## Inertia 集成
+
+```tsx
+import { Head, Link, useForm } from '@inertiajs/react'
+
+export default function TermEdit({ term, errors }) {
+  const { data, setData, post, processing } = useForm({ name: term.name })
+  return (
+    <>
+      <Head title="编辑术语" />
+      {/* 页面主体,Tailwind v4 样式 */}
+      <Link href="/terms">返回</Link>
+    </>
+  )
+}
+```
+
+- 页面内导航用 Inertia 的 `<Link>`(保留 SPA 状态),不要用整页 `<a>` 跳转。
+- 每个页面加 `<Head>` 设标题。
+- Inertia 页面样式走 Tailwind v4(见 `tailwind-v4.md`),不引入 antd。
+
+## i18n 与命名
+
+- 文案用 `__('messages.xxx')` 或 Blade `@lang('messages.xxx')`,不硬编码中文散落各处。
+- 路由命名、Blade 组件命名遵循现有约定(小写 kebab-case)。

+ 131 - 0
.agents/skills/frontend-design/references/tailwind-v4.md

@@ -0,0 +1,131 @@
+# Tailwind CSS v4 + React(api-v13 主方案)
+
+> api-v13 用 Tailwind **v4**(不是 v3)。v4 是 CSS-first 配置,语法与 v3 有差异,务必按 v4 写法,别搬 v3 的 `tailwind.config.js` 老套路。
+
+## v4 与 v3 的关键差异
+
+- **无 `tailwind.config.js`**:v4 默认零配置,直接在 CSS 里配置。
+- **入口**:`@import "tailwindcss";`
+- **自定义 token**:用 `@theme` 定义设计变量,之后自动生成对应 utility。
+
+```css
+/* resources/css/app.css */
+@import "tailwindcss";
+
+@theme {
+  --color-brand: #2563eb;
+  --color-brand-hover: #1d4ed8;
+  --color-brand-soft: #eff6ff;
+  --radius-card: 10px;
+}
+```
+
+- **自定义工具类**:用 `@utility` 而非 `@layer utilities`。
+- **ring 语法**:v4 用 `ring` / `ring-2`,不再有 `ring-opacity-*`(用 `/50` 透明度写法)。
+- **`outline-none` → `outline-hidden`**:隐藏焦点环改用 `outline-hidden`(`outline-none` 语义变了)。
+- 动态 class 拼接(`bg-${color}`)在 v4 的 JIT 下不会生成——颜色必须写全称或通过 token。
+
+## 项目里的组合工具
+
+`class-variance-authority`(cva)+ `clsx` + `tailwind-merge` 是依赖里已装的,用它做**变体组件**:
+
+```tsx
+import { cva } from 'class-variance-authority'
+import { cn } from '@/lib/cn' // clsx + tailwind-merge 封装
+
+const button = cva('inline-flex items-center gap-2 rounded-md font-medium transition-colors', {
+  variants: {
+    variant: {
+      primary: 'bg-brand text-white hover:bg-brand-hover',
+      ghost: 'text-secondary hover:bg-slate-100',
+      danger: 'bg-red-600 text-white hover:bg-red-700',
+    },
+    size: {
+      sm: 'h-8 px-3 text-xs',
+      md: 'h-10 px-4 text-sm',
+    },
+  },
+  defaultVariants: { variant: 'primary', size: 'md' },
+})
+
+export function Button({ className, variant, size, ...props }) {
+  return <button className={cn(button({ variant, size }), className)} {...props} />
+}
+```
+
+## 常用漂亮组件配方
+
+**卡片**
+
+```tsx
+<div className="rounded-md border border-slate-200 bg-white p-6">
+  <h3 className="text-base font-semibold text-slate-800">标题</h3>
+  <p className="mt-2 text-sm leading-6 text-slate-500">正文……</p>
+  <div className="mt-4 flex justify-end gap-2">{/* 操作区右下角 */}</div>
+</div>
+```
+
+**表单输入(含错误态)**
+
+```tsx
+<div>
+  <label className="mb-1.5 block text-sm font-medium text-slate-700">
+    字段名 <span className="text-red-500">*</span>
+  </label>
+  <input
+    className={cn(
+      'h-10 w-full rounded-md border bg-white px-3 text-sm outline-hidden',
+      'focus:ring-2 focus:ring-brand/30',
+      error ? 'border-red-500' : 'border-slate-200',
+    )}
+  />
+  {error && <p className="mt-1.5 text-xs text-red-600">{error}</p>}
+</div>
+```
+
+**徽章 / 状态标签**(浅底 + 前景 + 描边)
+
+```tsx
+<span className="inline-flex items-center rounded-full bg-green-50 px-2.5 py-0.5 text-xs font-medium text-green-700 ring-1 ring-green-200">
+  已完成
+</span>
+```
+
+**表格**
+
+```tsx
+<table className="w-full text-sm">
+  <thead className="border-b border-slate-200 text-left text-xs uppercase tracking-wide text-slate-500">
+    <tr>{/* th py-3 px-4 */}</tr>
+  </thead>
+  <tbody className="divide-y divide-slate-100">
+    <tr className="hover:bg-slate-50">{/* td py-3 px-4 */}</tr>
+  </tbody>
+</table>
+```
+
+**空状态**
+
+```tsx
+<div className="flex flex-col items-center justify-center gap-3 py-16 text-center">
+  <Icon className="size-10 text-slate-300" />
+  <p className="text-sm text-slate-500">暂无数据</p>
+  <Button size="sm">新建</Button>
+</div>
+```
+
+**骨架屏**
+
+```tsx
+<div className="animate-pulse space-y-3">
+  <div className="h-4 w-1/3 rounded bg-slate-200" />
+  <div className="h-4 w-full rounded bg-slate-200" />
+  <div className="h-4 w-2/3 rounded bg-slate-200" />
+</div>
+```
+
+## 图标与响应式约定
+
+- 图标统一 `@tabler/icons-react`(`@tabler/icons` 已装),尺寸用 `size-4` / `size-5`,别混 FontAwesome 与 Tabler。
+- 断点用 `sm md lg xl`,移动端先写、桌面端 `md:` 增强。
+- 间距 / 圆角 / 颜色都从 `@theme` 的 token 取,不写魔法数字。