关于 frontend-ui
用于构建和审查 SaaS 界面的前端技能,涵盖 React 19、Tailwind CSS v4 和 shadcn/ui,支持 Next.js 或 Vite。它结合组件与工作流指导,并纳入无障碍、响应式设计和性能检查。
主要功能
- 01Next.js 与 Vite React 项目设置
- 02Tailwind CSS v4 和 shadcn/ui 模式
- 03SaaS 仪表盘与定价页面布局
- 04WCAG 2.1 AA 无障碍指导
- 05表单、数据获取与性能检查
适用场景
- 01构建 SaaS 仪表盘和定价页面
- 02审查键盘与屏幕阅读器无障碍体验
- 03创建经过验证的表单和数据表格
- 04检查前端 Core Web Vitals
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ scientiacapital/ skills --skill frontend-ui安装后在 Claude Code 中输入/frontend-ui确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
config.jsonreference/accessibility-checklist.mdcomponent-patterns.mdform-patterns.mdperformance-optimization.mdsaas-dashboard.mdsaas-pricing-checkout.mdshadcn-setup.mdspa-routing.mdtailwind-v4-setup.mdvite-react-setup.mdtemplates/component-with-variants.tsxdashboard-layout.tsxform-with-server-action.tsxnextjs-tailwind-v4-setup.csspricing-page.tsxspa-app-layout.tsxvite-react-config.ts详细介绍
用于生产环境的 SaaS:仪表板、定价页面、数据表格、引导流程、基于角色的 UI,并内置 WCAG 2.1 AA 无障碍支持和 Core Web Vitals 性能优化。
<quick_start>
设置:Tailwind v4 + shadcn/ui
npx create-next-app@latest my-app --typescript --tailwind --eslint --app --src-dir
cd my-app && npx shadcn@latest init
npx shadcn@latest add button card dialog table form
Tailwind v4 — CSS 优先(无 tailwind.config.js)
/* app/globals.css */
@import "tailwindcss";
@theme inline {
--color-background: oklch(1 0 0);
--color-foreground: oklch(0.145 0 0);
--color-primary: oklch(0.205 0.042 264.695);
--color-primary-foreground: oklch(0.985 0 0);
--radius-lg: 0.5rem;
--radius-md: calc(var(--radius-lg) - 2px);
--radius-sm: calc(var(--radius-lg) - 4px);
}
组件结构(shadcn/ui 2026)
import { cva, type VariantProps } from "class-variance-authority"
import { cn } from "@/lib/utils"
const buttonVariants = cva(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors",
{
variants: {
variant: { default: "bg-primary text-primary-foreground", outline: "border border-input" },
size: { default: "h-10 px-4 py-2", sm: "h-9 px-3", lg: "h-11 px-8" },
},
defaultVariants: { variant: "default", size: "default" },
}
)
// React 19: ref is a regular prop — no forwardRef
// data-slot: styling hook for parent overrides
function Button({ className, variant, size, ref, ...props }:
React.ComponentProps<"button"> & VariantProps<typeof buttonVariants>) {
return <button ref={ref} data-slot="button"
className={cn(buttonVariants({ variant, size, className }))} {...props} />
}
cn() 工具函数
import { clsx, type ClassValue } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs: ClassValue[]) { return twMerge(clsx(inputs)) }
Vite SPA 替代方案
npm create vite@latest my-app -- --template react-ts
cd my-app && npm i -D @tailwindcss/vite && npx shadcn@latest init
与 Next.js 的主要区别:
- 使用
@tailwindcss/vite插件(而非 postcss)——HMR 更快,并原生集成 Vite - 使用
VITE_环境变量前缀(而非NEXT_PUBLIC_),通过import.meta.env访问 - 仅客户端运行——没有 Server Components,使用 React Query 获取数据
- 代码分割使用
React.lazy()+<Suspense>,替代dynamic() - 使用 React Router v7 或 TanStack Router 路由(而非基于文件的路由)
Tailwind v4、shadcn/ui、组件模式、无障碍、表单和性能指南同样适用于 Vite SPA。只有路由和数据获取确实有所不同。
参见 reference/vite-react-setup.md 和 reference/spa-routing.md。
</quick_start>
<success_criteria> 企业级 SaaS 前端满足以下条件即可投入生产:
- 无障碍: 符合 WCAG 2.1 AA——键盘导航、屏幕阅读器、焦点管理、4.5:1 对比度
- 高性能: 4G 移动网络下 LCP < 2.5s、INP < 200ms、CLS < 0.1
- 响应式: 移动优先,适配 320px-2560px,并为组件使用容器查询
- 安全: 不存在 XSS 攻击向量,设置 CSP 标头,清理用户内容
- 主题化: 通过 CSS 实现深色模式,在 @theme 中定义设计令牌,间距和颜色保持一致
- 可组合: 默认使用 Server Components,将客户端边界推至叶子组件
- 类型化: TypeScript 严格模式,所有表单均使用 Zod 验证,不使用
any</success_criteria>
<core_principles>
- 服务端优先 — 默认使用 Server Components。仅在需要交互时添加
"use client"。将客户端边界推至叶子组件。 - 默认无障碍 — 优先使用语义化 HTML(
<nav>、<main>、<article>)。仅在原生语义不足时使用 ARIA。 - 组合优于配置 — 使用小型、可组合的组件。复杂 UI 使用复合模式。在边界处使用 Context。
- 渐进式呈现 — 优先展示关键信息。按需呈现复杂内容。降低认知负担。
- 移动优先 — 从最小屏幕开始设计,再逐步增强。组件使用容器查询。触控目标 >= 44px。
- 设计令牌 — 所有视觉值都定义在 CSS
@theme中。切勿硬编码。使用 OKLCH 实现感知均匀性。 - 端到端类型安全 — 客户端与服务端共享 Zod 模式。优先使用
React.ComponentProps<>,而非手动定义接口。 </core_principles>
<tailwind_v4>
Tailwind CSS v4 — 相较于 v3 的主要变化
- 不再需要
tailwind.config.js— 所有配置都通过 CSS@theme指令完成 @import "tailwindcss"— 替代@tailwind base/components/utilities- OKLCH 颜色 — 感知均匀,替代 hex/HSL
- 内置容器查询 — 使用
@container、@md:、@lg:前缀 @source— 原生 CSS 文件扫描方式(替代content数组)- CSS 体积缩小 70% — 自动移除未使用的样式
@theme inline— shadcn/ui 桥接方式:使用令牌,但不生成工具类
@theme {
--color-brand-500: oklch(0.55 0.15 250);
--font-sans: "Inter", system-ui, sans-serif;
--breakpoint-xs: 475px;
--animate-slide-in: slide-in 0.2s ease-out;
}
// Container queries — component-level responsive
<div className="@container">
<div className="grid grid-cols-1 @md:grid-cols-2 @lg:grid-cols-3 gap-4">
{items.map(item => <Card key={item.id} {...item} />)}
</div>
</div>
迁移: npx @tailwindcss/upgrade — 参见 reference/tailwind-v4-setup.md。
</tailwind_v4>
<shadcn_ui>
shadcn/ui 2026
@theme inline— 将令牌与 Tailwind v4 桥接data-slot— 基于属性的样式钩子(替代 className 覆盖)- 不再使用
forwardRef— React 19 将 ref 作为 prop tw-animate-css— 替代tailwindcss-animate,以兼容 v4- Radix 或 Base UI — 选择基础组件库
// data-slot: parent can target child styles
function Card({ className, ref, ...props }: React.ComponentProps<"div">) {
return <div ref={ref} data-slot="card" className={cn("rounded-xl border bg-card", className)} {...props} />
}
// Style from parent:
<div className="[&_[data-slot=card]]:shadow-lg">
<Card>...</Card>
</div>
深色模式: 使用 .dark 类切换 CSS 自定义属性。参见 reference/shadcn-setup.md。
</shadcn_ui>
<component_architecture>
服务端组件与客户端组件
| 服务端组件(默认) | 客户端组件("use client") |
|---|---|
| 异步数据获取、数据库访问 | useState、useEffect、事件处理器 |
| 零 JS 包体积、可访问机密 | 浏览器 API、第三方客户端库 |
规则: 尽可能将 "use client" 推到最小的叶子节点。
// Server page with client island
export default async function DashboardPage() {
const metrics = await getMetrics()
return (
<main>
<KPICards data={metrics} /> {/* Server-rendered */}
<RevenueChart data={metrics} /> {/* Client island */}
</main>
)
}
关键模式
- 复合组件 —
<Table>/<TableRow>/<TableCell>命名空间组合 - cva 变体 — 使用
class-variance-authority实现类型安全的样式变体 - React.ComponentProps — 替代手动定义的接口,将 ref 作为常规属性
- data-slot — 为父子组件覆盖样式提供外部钩子
- 多态(asChild) — 使用
Slot模式渲染为不同元素 - SPA 代码拆分 — 使用
React.lazy()+<Suspense>替代 Next.jsdynamic()
完整示例请参阅 reference/component-patterns.md。
</component_architecture>
<saas_patterns>
企业级 SaaS 模式
仪表盘:侧边栏 + 页眉 + 主内容区
<div className="flex h-screen">
<Sidebar className="w-64 hidden lg:flex" />
<div className="flex-1 flex flex-col">
<Header /> {/* Search, user menu, notifications */}
<main className="flex-1 overflow-auto p-6">
<KPIGrid metrics={metrics} />
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6">
<RevenueChart data={revenue} />
<ActivityFeed items={activities} />
</div>
</main>
</div>
</div>
定价(三级转化)
入门档(低价) | 转化目标(突出显示,“最受欢迎”) | 企业版(定制)
月付/年付切换、功能对比表、社会认同。请参阅 templates/pricing-page.tsx。
数据表格 — shadcn Table + TanStack Table,用于排序/筛选/分页
状态三件套 — 每个数据组件都需要:加载中(Skeleton)| 错误(重试操作)| 空状态(提供指引)
基于角色的 UI — 使用 hasPermission(user, "scope") 守卫实现条件渲染
请参阅 reference/saas-dashboard.md 和 reference/saas-pricing-checkout.md。
</saas_patterns>
优先使用语义化 HTML — <header>、<nav>、<main>、<article>、<section>、<footer>
| 模式 | 实现方式 |
|---|---|
| 键盘导航 | Tab/Shift+Tab、方向键用于菜单/标签页、Escape 关闭 |
| 焦点管理 | 在对话框中锁定,关闭时恢复,提供跳过链接 |
| ARIA 实时区域 | 对动态内容使用 aria-live="polite" |
| 表单错误 | aria-invalid、aria-describedby、role="alert" |
| 加载状态 | 在加载中的按钮上设置 aria-busy={true} |
| 对比度 | 文本 4.5:1,UI 组件 3:1(OKLCH 亮度通道) |
// Skip link
<a href="#main-content" className="sr-only focus:not-sr-only focus:absolute focus:z-50">
Skip to main content
</a>
各组件的 ARIA 模式请参阅 reference/accessibility-checklist.md。
<state_management>
状态决策树
| 状态类型 | 解决方案 | 示例 |
|---|---|---|
| URL 状态 | nuqs / useSearchParams | 筛选器、分页、标签页 |
| 服务端数据 | React Query / SWR | API 数据、用户资料 |
| 本地 UI | useState | 表单输入、切换开关 |
| 共享的父子状态 | 提升状态 / Context | 手风琴组 |
| 复杂的跨组件状态 | Zustand | 购物车、向导、通知 |
优先使用 URL 状态 — 可分享、可加入书签,刷新后仍然保留。 </state_management>
<data_fetching>
数据获取
| 模式 | 适用场景 | 实现方式 |
|---|---|---|
| 服务端组件 | 默认 | async function Page() { const data = await db.query() } |
| Suspense 流式传输 | 慢速数据 | <Suspense fallback={<Skeleton/>}><SlowComponent/></Suspense> |
| 服务端操作 | 数据变更 | "use server" + revalidatePath() |
| React Query | 客户端实时数据 | useQuery({ queryKey, queryFn, refetchInterval }) |
| React Query(SPA) | 仅客户端应用 | 使用加载器配合 useQuery({ queryKey, queryFn }) — 替代服务端组件 |
| </data_fetching> |
- 共享 Zod 架构 — 客户端验证和服务端操作的唯一事实来源
- React Hook Form — 使用
zodResolver配置useForm,并设置mode: "onBlur" - shadcn Form —
<Form>/<FormField>/<FormItem>/<FormLabel>/<FormMessage> - 服务端操作 — 在服务端执行
safeParse,返回字段错误,并调用revalidatePath
const schema = z.object({
name: z.string().min(2),
email: z.string().email(),
})
请参阅 reference/form-patterns.md 和 templates/form-with-server-action.tsx。
| 指标 | 目标 | 快速见效的方法 |
|---|---|---|
| LCP < 2.5s | 主内容可见 | 使用带有 priority 的 next/image、next/font |
| INP < 200ms | 交互响应迅速 | 使用 dynamic() 对大型组件进行代码拆分 |
| CLS < 0.1 | 布局不偏移 | 为图片和字体预留空间,使用骨架屏加载器 |
Tailwind v4 会自动生成体积缩小 70% 的 CSS。请参阅 reference/performance-optimization.md。
无障碍访问
- 所有交互元素均支持键盘导航
- 屏幕阅读器能清晰播报内容含义
- 焦点指示器清晰可见,并提供跳过链接
- 颜色对比度 >= 4.5:1(文本),>= 3:1(UI)
性能
- LCP < 2.5s、INP < 200ms、CLS < 0.1
- 图片使用 next/image,字体使用 next/font
- 使用 dynamic() 对大型组件进行代码拆分
响应式
- 适配 320px-2560px,触控目标 >= 44px
- 为可复用组件使用容器查询
安全
- 未经清理,不注入原始 HTML
- 配置 CSP 标头,并在客户端和服务端使用 Zod 验证
用户体验
- 所有数据视图均提供加载、错误和空状态
- 数据变更时显示 Toast,执行破坏性操作时要求确认
写入结果 Sidecar
最后一步,将内容写入 ~/.claude/skill-analytics/last-outcome-frontend-ui.json:
{"ts":"[UTC ISO8601]","skill":"frontend-ui","version":"1.0.0","variant":"default",
"status":"[success|partial|error]","runtime_ms":[estimated ms from start],
"metrics":{"components_built":[n],"pages_created":[n],"a11y_checks_passed":[bool]},
"error":null,"session_id":"[YYYY-MM-DD]"}
如果部分阶段失败但仍生成了结果,请使用状态 "partial"。仅当未生成任何输出时,才使用 "error"。