Communitygithub.com

0xAlchemist-cs/personal-ui-system

使用 Chakra UI v3 构建响应式、可访问的 UI 组件和布局;在新建或现有项目中安装或配置 Chakra UI;并使用 tokens、semantic tokens、recipes 和 slot recipes 设计可扩展的主题。

personal-ui-system 是什么?

该技能旨在帮助开发者使用 Chakra UI v3 高效构建用户界面。它首先检查项目上下文(如 package.json、框架类型、包管理器),确保生成的代码与项目现有约定一致。技能涵盖了完整的项目设置流程,包括安装依赖、使用 CLI 生成代码片段并配置 Provider,并提供了常见问题的解决方案。在构建组件时,它强调使用语义化 token 以支持明暗主题自动适配,采用 mobile-first 的响应式样式策略,并通过 Field.Root 等组件确保表单的可访问性。对于 Next.js 应用,技能指导开发者合理区分 Server 和 Client 组件,以优化性能。最后,它建议在满足特定条件时提取组件、使用 recipe 或 slot recipe,并指出在进行深入主题定制或处理图表请求时应参考专门的文档,确保所有输出都是完整可运行且符合最佳实践的。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/0xAlchemist-cs/personal-ui-system/tree/HEAD/.agents/skills/chakra-ui-builder

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Chakra UI Builder

你正在使用 Chakra UI v3 构建 UI,并帮助开发者在他们的项目中设置 Chakra UI。你的工作是生成符合项目需求的、干净、可访问、响应式的代码——而不是通用模板。先阅读项目上下文,然后再构建或设置。


步骤 1 — 阅读项目上下文

如果可用,检查 package.json。查找:

  • Chakra UI 版本(默认使用 v3 模式;仅在明确使用 v2 时才使用 v2)
  • 框架:Next.js App Router、Pages Router、Vite、纯 React
  • TypeScript 或 JavaScript
  • 包管理器(根据锁定文件:pnpm-lock.yaml、yarn.lock、bun.lock、package-lock.json)

如果用户引用了现有组件,也要快速浏览一下,以便你的代码符合已有的约定(命名、文件结构、导入风格)。

如果需求含糊不清,或者组件复杂到需要做出选择(布局方向、数据结构、颜色调色板、变体数量),在构建之前先询问,而不是生成一些可能需要废弃的东西。


项目设置

如果尚未安装 Chakra UI,请先完成设置再构建。

安装

# npm
npm install @chakra-ui/react @emotion/react

# pnpm
pnpm add @chakra-ui/react @emotion/react

# yarn
yarn add @chakra-ui/react @emotion/react

# bun
bun add @chakra-ui/react @emotion/react

使用 CLI 生成代码片段

npx @chakra-ui/cli snippet add

不带参数时,会添加推荐的集合——provider、toaster 和 tooltip——并自动安装所需的依赖(包括 next-themes)。使用 --all 添加所有代码片段,或先使用 snippet list 浏览。

CLI 会检测你的框架并将文件写入正确的位置:

框架输出路径
Next.js(带 src/)src/components/ui/
Next.js(无 src/)components/ui/
Vite / 纯 Reactsrc/components/ui/
Remixapp/components/ui/

配置 Provider

Next.js App Router(app/layout.tsx):

import { Provider } from "@/components/ui/provider"

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en" suppressHydrationWarning>
      <body>
        <Provider>{children}</Provider>
      </body>
    </html>
  )
}

suppressHydrationWarning 可防止因 next-themes 注入颜色模式类名导致的不匹配。不要在 layout.tsx 中添加 "use client"——生成的 provider 文件已经包含了它。

Next.js Pages Router(pages/_app.tsx):

import { Provider } from "@/components/ui/provider"

export default function App({ Component, pageProps }) {
  return (
    <Provider>
      <Component {...pageProps} />
    </Provider>
  )
}

Vite(src/main.tsx):

import { Provider } from "./components/ui/provider"

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <Provider>
      <App />
    </Provider>
  </StrictMode>,
)

手动创建 Provider(如果 CLI 不可用)

如果 CLI 失败,手动创建 components/ui/provider.tsx 并单独安装 next-themes:

"use client"
import { ChakraProvider, defaultSystem } from "@chakra-ui/react"
import { ThemeProvider } from "next-themes"

export function Provider({ children }: { children: React.ReactNode }) {
  return (
    <ChakraProvider value={defaultSystem}>
      <ThemeProvider attribute="class" disableTransitionOnChange>
        {children}
      </ThemeProvider>
    </ChakraProvider>
  )
}

常见设置问题

  • 组件无样式——应用未包裹在 <Provider> 中。检查导入路径以及 Provider 是否包裹了组件树。
  • 水合不匹配——在 App Router 中的 <html> 上添加 suppressHydrationWarning。
  • 找不到 next-themes——安装它:npm install next-themes(仅在手动回退时需要;CLI 会自动处理)。
  • extendTheme 未导出——这是 v2 的模式。在 v3 中使用 createSystem。

步骤 2 — 选择正确的布局原语

选择正确的 Chakra 原语,而不是将所有内容都包裹在 Box 中:

需求使用
垂直堆叠的项目Stack(默认)或 VStack
水平行HStack 或 Flex
CSS GridGrid + GridItem
等分列网格SimpleGrid columns={N}
居中的页面内容Container maxW="container.lg"
完整的 flexbox 控制Flex 配合显式属性
语义化的章节/文章Box as="section" / Box as="article"

避免深层嵌套。如果嵌套了三层 Box 而没有语义原因,请将其扁平化。优先使用 gap 而不是兄弟元素之间的 margin。


步骤 3 — 使用 tokens,而不是原始值

Chakra v3 提供了可自动适应亮/暗模式的语义化 tokens。优先使用它们而不是硬编码的调色板值——它们使组件能从主题感知,而无需额外工作。

// 优先使用语义化 tokens
<Box bg="bg.subtle" color="fg.default" borderColor="border.subtle" />
<Text color="fg.muted" />
<Box shadow="md" rounded="lg" />

// 对交互式组件使用 colorPalette(而不是 colorScheme)
<Button colorPalette="blue">提交</Button>
<Badge colorPalette="green">活跃</Badge>

仅在特定颜色是有意使用且不应随颜色模式改变时,才使用原始调色板值(blue.500、gray.100)。


步骤 4 — 响应式样式

Chakra 使用移动优先的断点。一致地使用数组或对象语法:

// 数组:[base, sm, md, lg, xl]
<Box px={[4, 6, 8]} fontSize={["sm", "md", "lg"]} />

// 对象:显式断点
<SimpleGrid columns={{ base: 1, md: 2, lg: 3 }} gap={6} />
<Stack direction={{ base: "column", md: "row" }} gap={4} />

每个布局组件至少应处理 base(移动端)和 md(桌面端)断点,除非请求明确仅限桌面端。


步骤 5 — 表单

对所有表单字段使用 Field.Root——它能正确连接标签、输入框、错误信息和帮助文本:

<Field.Root invalid={!!error} required>
  <Field.Label>电子邮件地址</Field.Label>
  <Input type="email" placeholder="[email protected]" />
  <Field.ErrorText>{error}</Field.ErrorText>
  <Field.HelpText>我们绝不会分享您的电子邮件。</Field.HelpText>
</Field.Root>

对于表单提交状态,在输入框和按钮上使用 disabled(而不是 isDisabled)。将相关字段分组放在 Stack gap={4} 中。


步骤 6 — 可访问性

Chakra 的内置组件会自动处理大部分可访问性问题——不要覆盖它。你需要提供的内容:

  • 纯图标按钮:始终添加 aria-label
    <IconButton aria-label="关闭对话框" icon={<CloseIcon />} />
    
  • 图片:始终传递有意义的 alt 文本(装饰性图片用 alt="")
  • 表单标签:使用 Field.Label 或确保 htmlFor 与输入框的 id 匹配
  • 交互式自定义元素:如果你在 Box 上使用 onClick 构建某些内容,请使用 as="button" 或实际的 <button>,以便键盘导航正常工作
  • 语义化标题:使用 h1–h6 层级;不要跳级
  • 颜色对比度:不要在白色背景上使用浅灰色文本;依赖经过对比度测试的语义化 tokens

步骤 7 — Next.js:在哪里添加 "use client"

在 Next.js App Router 中,Server Components 是默认的。仅在需要的文件中添加 "use client",而不是在整个布局或页面中添加。

组件在以下情况下需要 "use client":

  • 使用 React hooks(useState、useEffect、useContext 等)
  • 处理浏览器事件(带有状态的 onClick、表单提交等)
  • 使用浏览器 API
// Server Component — 不需要指令
export default function ProductCard({ name, price }: Props) {
  return (
    <Box p={4} borderWidth={1} rounded="md">
      <Text fontWeight="bold">{name}</Text>
      <Text color="fg.muted">{price}</Text>
    </Box>
  )
}

// Client Component — 需要指令
;("use client")
export function AddToCartButton({ productId }: { productId: string }) {
  const [added, setAdded] = useState(false)
  return (
    <Button onClick={() => setAdded(true)} colorPalette="blue">
      {added ? "已添加!" : "加入购物车"}
    </Button>
  )
}

目标是将交互性推向叶子节点——尽可能保持组件树的更多部分为 Server Components。


步骤 8 — 何时提取组件、使用 recipe 以及自定义主题

当相同结构出现超过两次,或者某个部分复杂到值得命名以使父组件更清晰时,提取组件。

当组件具有开发者可能想要自定义的有意义的样式变体时,建议使用 recipe。对于具有多个协调部分的组件(带有 header/body/footer 的卡片、带有 label/value/icon 的统计信息等),建议使用 slot recipe。

对于更深层次的主题工作——定义品牌颜色 tokens、带有暗色模式值的语义化 tokens、完整的 recipe/slot-recipe 编写、typegen 或导出默认主题——在回复之前阅读 references/theming.md。它涵盖了完整的 defineConfig / createSystem API,并附有完整示例。

对于任何图表请求——条形图、面积图、折线图、饼图/环形图、BarList、BarSegment 或任何涉及 @chakra-ui/charts 的内容——在回复之前阅读 references/charts.md。它涵盖了 useChart 钩子、所有三种图表类型、Recharts 集成、颜色 tokens 以及完整的可运行示例。

当你不确定使用哪个组件,或者用户没有指定时,阅读 references/component-decision-tree.md。它涵盖了每个 Chakra 组件,并提供了在类似替代方案中选择的指导。


输出格式

生成:

  1. 完整、可运行的代码——正确的导入,没有像 TODO 或 ...rest of component 这样的占位符
  2. 正确的 import 语句——先分组 Chakra 导入,然后是本地导入
  3. 组件分离——如果组件复杂或包含明显可分离的部分,则拆分为多个组件/文件
  4. 响应式样式——布局至少包含 base 和 md 断点
  5. 代码后的简要说明——2-4 句话说明所做的关键决策(布局方法、可访问性选择、响应式策略)。如果请求很简单,可以省略说明。
// 好的导入风格
import { Box, Button, Field, Stack, Text } from "@chakra-ui/react"
// 然后是本地导入
import { SomeLocalComponent } from "./SomeLocalComponent"

何时先询问

如果请求足够清晰,可以生成有用的东西,则立即构建。在以下情况下先询问:

  • 数据结构未知,并且它会改变整个结构(例如,“构建一个表格”——多少列?什么数据?)
  • 存在用户可能关心的有意义的设计选择(布局方向、列数、颜色调色板)
  • 用户引用了你尚未看到的文件或现有组件

如有疑问,在回复顶部说明你的假设,然后构建——用户从一个具体的东西开始调整,比从无到有好得多。

相关技能