Chakra UI 重构与审查
你将使用 Chakra UI v3 审查和改进 UI 代码。根据开发者的需求,你将输出结构化的评审意见、重写的代码,或两者兼具。在生成任何输出之前,请完整阅读代码和项目上下文。
步骤 1 — 定位并确定意图
在输出任何内容之前,需明确:
- Chakra UI 版本 — 检查
package.json;默认使用 v3 - 框架 — Next.js App Router、Pages Router、Vite、纯 React
- 源代码类型 — 现有的 Chakra、纯 HTML/CSS、Tailwind、CSS Modules、styled-components
- 用户的请求类型:
- 仅审查 — “检查这个”、“这样对吗/符合规范吗”、“哪里有问题”、“审查我的代码” → 输出评审意见,附带针对性的修复建议,不进行完全重写
- 重构 / 转换 — “重构这个”、“从 Tailwind 转换”、“清理一下” → 输出重写后的代码
- 两者兼有 — “审查并修复” → 先输出评审意见,再输出重写后的代码
在回复开头说明你的判断(例如:“正在审查现有的 Chakra v3 代码。假设使用 Next.js App Router。”)。
如果尚未提供代码,请要求提供。不要审查对代码的描述。
步骤 2 — 分析代码
无论输出模式如何,都要从以下维度分析代码。对于审查,这些将成为发现的问题点。对于重构,这些将成为需要修复的检查清单。
可访问性
- 所有交互元素是否都有可访问的标签?(图标按钮上的
aria-label、label+input 对中的htmlFor/id、图片中的alt属性) - 是否支持键盘导航?(焦点环未被抑制,交互元素实际上是可聚焦的)
- 标题层级是否合理?(没有跳级,没有将
h1用作样式快捷方式) - 表单字段是否包裹在
Field.Root中,并使用Field.Label和Field.ErrorText? - 组件是否仅依靠颜色来传递信息?
响应式设计
- 布局组件是否指定了多个断点下的行为?
- 是否存在本可以使用响应式 Token 的硬编码像素值?
- 在移动设备上是否会出问题?(固定宽度、在小视口上使用
overflow: hidden)
Chakra API 正确性
- 是否使用了 v3 的属性名称?(
disabled而非isDisabled,colorPalette而非colorScheme,gap而非spacing,open而非isOpen) - 复合组件是否使用正确?(
Field.Root/Field.Label、Dialog.Root/Dialog.Content等) - 在 Next.js App Router 中,
"use client"是否放置在正确位置? - 是否仍然存在 v2 模式?(
extendTheme、ColorModeScript、useColorModeValue、sx属性)
Token 和样式使用
- 是否在可以使用语义 Token 的地方使用了硬编码颜色?(
bg="#f9fafb"→bg="bg.subtle") - 是否使用了原始十六进制或调色板值代替语义 Token?这些将不会尊重暗色模式。
- 是否存在应该使用 Chakra 样式属性的内联
style={{}}属性?
组件结构
- 是否存在不必要的嵌套?(在一个
Box可以解决的时候使用Box > Box > Box) - 是否在每个子元素上使用手动间距(
mt={4}),而使用Stack gap={4}会更简洁? - 是否使用了正确的布局原语?(
FlexvsStackvsGridvsSimpleGrid)
可维护性
- 同一个视觉模式是否重复了 3 次以上?(考虑提取为组件或配方 recipe)
- 是否存在暗示需要配方或插槽配方的单一样式覆盖?
- 属性类型 / TypeScript 类型是否存在且准确?
步骤 3a — 审查输出
当用户希望得到评审意见而非重写时使用此模式。
按影响程度对发现的问题进行分类:
关键 (Critical) — 破坏行为或排除用户的 Bug 和可访问性故障
- 仅图标的按钮缺少
aria-label - 表单字段没有关联的标签
- 带有
onClick的交互式Box但没有键盘访问能力 - 错误的 v3 属性名称,静默无作用(
isDisabled在 v3 中不会禁用) - 在使用 App Router 的钩子的组件上缺少
"use client"
改进 (Improvements) — 不紧急但需要修复的正确性和质量问题
- 破坏暗色模式的硬编码颜色
- 缺少响应式断点
- 不必要的嵌套或错误的布局原语
- 仍然有效但应该更新的 v2 模式
可选建议 (Optional suggestions) — 非阻塞性想法
- 将重复的模式提取为组件
- 用配方变体替换单一样式
- 添加缺失的 TypeScript 类型
对于每个关键问题和重要的改进,展示一个最小的修复示例:
**关闭按钮缺少 aria-label** (关键)
用于关闭对话框的 IconButton 没有可访问标签。
// 修改前
<IconButton icon={<CloseIcon />} onClick={onClose} />
// 修改后
<IconButton aria-label="关闭对话框" icon={<CloseIcon />} onClick={onClose} />
跳过没有问题的部分。根据代码长度调整输出量——一个 20 行的组件需要精炼的审查,而不是详尽的审查。
步骤 3b — 重构 / 转换输出
当用户希望得到重写的代码时使用此模式。
按源类型的转换策略
从纯 HTML / CSS 转换
| HTML | Chakra 等效组件 |
|---|---|
<div> 布局包装器 | Box、Flex、Stack、Grid |
<section>、<article> | Box as="section"、Box as="article" |
<nav> | Box as="nav" |
<ul> / <li> | Box as="ul" / Box as="li" 或 Stack |
<button> | Button 或 IconButton |
<a> | Link |
<img> | Image(保留 alt) |
<input>、<select> | Input、NativeSelect 包裹在 Field.Root 内 |
| CSS | Chakra 样式属性 |
|---|---|
display: flex | Flex 或 display="flex" |
flex-direction: column | Stack 或 flexDir="column" |
gap: 16px | gap={4}(1 单位 = 4px) |
padding: 16px 24px | py={4} px={6} |
border-radius: 8px | rounded="md" |
color: #6b7280 | color="fg.muted" |
background: #f9fafb | bg="bg.subtle" |
从 Tailwind CSS 转换
| Tailwind | Chakra |
|---|---|
flex、flex-col、flex-row | Flex、flexDir |
gap-4 | gap={4} |
p-4、px-6、py-2 | p={4}、px={6}、py={2} |
text-sm、font-bold | fontSize="sm"、fontWeight="bold" |
rounded-lg、shadow-md | rounded="lg"、shadow="md" |
w-full、max-w-lg | w="full"、maxW="lg" |
hidden md:flex | display={{ base: "none", md: "flex" }} |
grid grid-cols-3 | SimpleGrid columns={3} |
text-gray-500、bg-gray-100 | color="fg.muted"、bg="bg.subtle" |
hover:bg-gray-100 | _hover={{ bg: "bg.subtle" }} |
将 Tailwind 的响应式前缀替换为 Chakra 的断点对象语法:
// Tailwind: text-sm md:base lg:text-lg
<Text fontSize={{ base: "sm", md: "md", lg: "lg" }} />
从 CSS Modules 转换 — 将能够转换为 Chakra 属性的部分进行转换;对于无法用属性表达的样式(动画、复杂选择器),保留 className:
// 修改前
<div className={styles.card}>...</div>
// 修改后 — 转换为属性;className 仅保留用于无法转换的样式
<Box p={4} rounded="lg" shadow="sm" className={styles.fadeIn}>...</Box>
一旦所有类名都被消除,就彻底删除 import。
从 styled-components / @emotion/styled 转换 — 映射到带有等效样式属性的 Box,然后删除 import,如果其他位置不再使用,则卸载依赖:
// 修改前
const Card = styled.div`padding: 1rem; &:hover { background: #f3f4f6; }`
// 修改后
<Box p={4} _hover={{ bg: "bg.muted" }} />
从现有的 Chakra UI 清理
- 扁平化
Box > Box > Box嵌套;用Stack gap={N}替换每个子元素上的mt/mb - 将
sx替换为css(v3 重命名);更新其中的嵌套&:hover选择器 - 将
bg="gray.50"替换为bg="bg.subtle"和其他语义等效 Token - 更新 v2 属性名称:
isDisabled→disabled、colorScheme→colorPalette、spacing→gap - 更新复合组件:
FormControl→Field.Root、Modal→Dialog、Select→NativeSelect
保留行为 — 不可协商
- 完全保持所有事件处理程序、状态变量和条件渲染不变
- 保留
aria-*属性;在明显需要的地方添加缺失的属性 - 保留语义 HTML 意图(
<nav>→Box as="nav",而不仅仅是Box) - 保留已存在的
"use client";仅在组件真正需要时才添加 - 不要仅仅因为组件使用了 Chakra UI 就添加
"use client" - 一个部分转换但能正常工作的组件,优于一个完全转换但无法运行的组件
asChild 用于 Next.js Link 和 Image
import NextLink from "next/link"
<ChakraLink asChild><NextLink href="/about">关于</NextLink></ChakraLink>
import NextImage from "next/image"
<ChakraImage asChild><NextImage src="/hero.png" alt="英雄图" width={800} height={400} /></ChakraImage>
重构输出格式
1. 重构后的代码 — 完整、可运行,包含正确的导入
2. 变更内容 — 简洁列表,专注于非显而易见的决策:
- 将 <div className="flex gap-4"> 替换为 <Flex gap={4}>
- 将 bg="#f9fafb" 替换为 bg="bg.subtle" 以兼容暗色模式
- 保持表单提交处理程序和验证逻辑不变
- 为关闭图标按钮添加了 aria-label(原代码缺失)
3. 建议(可选)— 本次重构之外有意义的改进:提取可复用的组件、采用配方、迁移 v2 模式。
何时询问 vs 直接进行
如果代码和意图清晰,则直接进行。以下情况需要询问:
- 尚未提供代码
- 组件非常大,且范围不明确
- 存在你无法看到但可能影响评审的上下文或状态
- 存在可能被多种方式解释的模糊行为
当你在未询问的情况下直接进行时,请在回复顶部说明你的假设。