Ark UI
基于 Zag.js 状态机的跨框架无样式可访问组件库
Ark UI 是 Chakra UI 团队打造的无样式组件库,基于 Zag.js 有限状态机构建 45+ 可访问组件原语,支持 React、Vue、Solid、Svelte 四框架统一的组件行为抽象。
Ark UI
基于 Zag.js 状态机的跨框架无样式可访问组件库,为 React、Vue、Solid、Svelte 提供统一的组件行为抽象
技术简介说明
Ark UI 是由 Chakra UI 团队(Segun Adebayo)打造的无样式(headless)组件库,提供 45+ 个可访问的 UI 组件原语。其核心差异化在于基于 Zag.js 有限状态机构建所有组件逻辑,使得同一套状态机代码能够跨 React、Vue、Solid、Svelte 四个框架运行,实现真正的"一次编写,处处使用"。
Ark UI 的设计哲学是将交互行为与视觉样式彻底解耦——组件只负责可访问性、键盘导航、焦点管理、状态逻辑,样式完全由开发者控制。它与 Park UI(基于 Panda CSS 的样式层)搭配使用,可以快速构建完整的设计系统。
核心价值
- 状态机驱动:每个组件的交互逻辑由 Zag.js 有限状态机建模,行为可预测、易调试
- 跨框架一致性:同一套状态机逻辑适配 React / Vue / Solid / Svelte,API 统一
- 无样式设计:零样式注入,完全由开发者控制 CSS,适配任意样式方案
- 可访问性优先:WAI-ARIA 模式内置,键盘导航、焦点管理、屏幕阅读器支持开箱即用
- 设计系统友好:通过
data-scope/data-part/data-state属性暴露组件结构,方便样式化
基本信息
| 项目 | 详情 |
|---|---|
| 官网 | https://ark-ui.com |
| GitHub | https://github.com/chakra-ui/ark |
| License | MIT |
| 最新版本 | v5.37.x(2026年6月持续更新) |
| 主要维护者 | Segun Adebayo、Christian Schröter、Esther Agbaje(Chakra UI 团队) |
| 技术栈 | TypeScript、Zag.js(有限状态机)、React / Vue / Solid / Svelte |
| Stars | 5.3k+(GitHub) |
| npm 下载 | 周下载量 630k+(@ark-ui/react) |
| 组件数量 | 45+ 组件 + 15+ 工具函数 |
产品矩阵
Ark UI 生态包含以下主要产品:
- Ark UI:无样式跨框架组件库(核心产品)
- Zag.js:底层状态机引擎,独立可用
- Park UI:基于 Ark UI + Panda CSS 的带样式设计系统
- @ark-ui/mcp:Model Context Protocol 服务器,支持 AI 辅助开发
快速上手
安装
Ark UI 按框架提供独立包,每个组件按需导入:
# React
npm install @ark-ui/react
# Vue
npm install @ark-ui/vue
# Solid
npm install @ark-ui/solid
# Svelte
npm install @ark-ui/svelte基础配置
Ark UI 无需特殊配置,直接导入使用。组件采用命名空间导入模式:
// React 示例
import { Dialog } from '@ark-ui/react/dialog'
import { Slider } from '@ark-ui/react/slider'
import { Tabs } from '@ark-ui/react/tabs'最小示例
==== React ====
import { Dialog } from '@ark-ui/react/dialog'
import { Portal } from '@ark-ui/react/portal'
export function MyDialog() {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button>打开对话框</button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop className="dialog-backdrop" />
<Dialog.Positioner className="dialog-positioner">
<Dialog.Content className="dialog-content">
<Dialog.Title>标题</Dialog.Title>
<Dialog.Description>描述内容</Dialog.Description>
<Dialog.CloseTrigger asChild>
<button>关闭</button>
</Dialog.CloseTrigger>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
)
}==== Vue ====
<script setup>
import { Dialog } from '@ark-ui/vue/dialog'
import { Portal } from '@ark-ui/vue/portal'
</script>
<template>
<Dialog.Root>
<Dialog.Trigger asChild>
<button>打开对话框</button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop class="dialog-backdrop" />
<Dialog.Positioner class="dialog-positioner">
<Dialog.Content class="dialog-content">
<Dialog.Title>标题</Dialog.Title>
<Dialog.Description>描述内容</Dialog.Description>
<Dialog.CloseTrigger asChild>
<button>关闭</button>
</Dialog.CloseTrigger>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
</template>==== Solid ====
import { Dialog } from '@ark-ui/solid/dialog'
import { Portal } from '@ark-ui/solid/portal'
export function MyDialog() {
return (
<Dialog.Root>
<Dialog.Trigger asChild>
<button>打开对话框</button>
</Dialog.Trigger>
<Portal>
<Dialog.Backdrop class="dialog-backdrop" />
<Dialog.Positioner class="dialog-positioner">
<Dialog.Content class="dialog-content">
<Dialog.Title>标题</Dialog.Title>
<Dialog.Description>描述内容</Dialog.Description>
<Dialog.CloseTrigger asChild>
<button>关闭</button>
</Dialog.CloseTrigger>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
)
}Slider 示例(展示受控模式)
import { Slider } from '@ark-ui/react/slider'
import { useState } from 'react'
export function SliderDemo() {
const [value, setValue] = useState([50])
return (
<Slider.Root
value={value}
onValueChange={(details) => setValue(details.value)}
min={0}
max={100}
>
<Slider.Label>音量</Slider.Label>
<Slider.Control>
<Slider.Track>
<Slider.Range />
</Slider.Track>
<Slider.Thumb index={0} />
</Slider.Control>
<Slider.ValueText>{value[0]}%</Slider.ValueText>
</Slider.Root>
)
}核心概念与架构
三层架构
Ark UI 采用清晰的三层架构分离关注点:
┌─────────────────────────────────────────┐
│ Ark UI(框架适配层) │
│ React / Vue / Solid / Svelte 包装器 │
├─────────────────────────────────────────┤
│ Zag.js(状态机引擎层) │
│ machine / context / connect / service │
├─────────────────────────────────────────┤
│ @zag-js/core(有限状态机) │
│ 基于 XState FSM 的最小化状态机实现 │
└─────────────────────────────────────────┘
Zag.js 状态机模型
每个组件的交互逻辑被建模为有限状态机,包含三个核心概念:
- Machine(机器):定义组件的所有状态、上下文和转换逻辑
- Service(服务):通过
useMachine初始化,创建运行中的状态机实例 - Connect(连接器):将运行中的状态机转化为框架可用的 API(HTML 属性、事件处理器)
// Zag.js 状态机伪代码(以 NumberInput 为例)
import * as numberInput from '@zag-js/number-input'
// 1. 定义机器
const machine = numberInput.machine({
context: { value: 0, min: 0, max: 100 },
})
// 2. 创建服务(框架适配层处理)
const service = useMachine(machine, { id: 'number-input' })
// 3. 连接为 API
const api = numberInput.connect(service, service, normalizeProps)
// api.value → 当前值
// api.increment() → 增加
// api.decrement() → 减少状态机驱动的可预测行为
状态机确保组件在任何时刻的行为都是确定的:
[空闲] → 点击增加 → [递增中] → 动画完成 → [空闲]
[空闲] → 长按增加 → [持续递增] → 松开 → [空闲]
[空闲] → 获得焦点 → [聚焦] → 失去焦点 → [空闲]
框架适配策略
Ark UI 的跨框架实现策略:
- Zag.js 编写纯 JavaScript 状态机逻辑(与框架无关)
- 各框架适配器将状态机输出映射为框架特定的响应式原语
- React → hooks(
useState、useEffect) - Vue →
$ref、$computed - Solid →
$state、$derived - Svelte →
$state、$effect
- React → hooks(
- 确保 API 一致性:所有框架使用相同的组件名、属性名、事件名
组件解剖(Anatomy)
每个组件由多个语义化部分组成,通过 data-scope 和 data-part 属性暴露:
// Slider 组件解剖
<Slider.Root data-scope="slider">
<Slider.Label data-part="label">音量</Slider.Label>
<Slider.Control data-part="control">
<Slider.Track data-part="track">
<Slider.Range data-part="range" />
</Slider.Track>
<Slider.Thumb data-part="thumb" data-index="0" />
</Slider.Control>
<Slider.ValueText data-part="value-text">50%</Slider.ValueText>
</Slider.Root>Controlled 与 Uncontrolled 模式
所有交互组件同时支持受控和非受控状态:
// 非受控
<Dialog.Root defaultOpen={false}>
// 受控
<Dialog.Root open={isOpen} onOpenChange={(details) => setIsOpen(details.open)}>核心特性
1. 状态机驱动(State Machine Powered)
- 每个组件的交互逻辑由 Zag.js 有限状态机建模
- 行为可预测,状态转换显式定义
- 复杂交互(如 Date Picker、Combobox)的逻辑清晰可维护
- 状态机可独立测试,不依赖 DOM
2. 跨框架一致性(Framework Agnostic)
- 唯一支持 React / Vue / Solid / Svelte 的 headless 组件库
- 所有框架共享同一套状态机逻辑,行为完全一致
- API 统一:组件名、属性名、事件名在所有框架中相同
- 适合多框架团队维护统一的设计系统
3. 完全可访问(Accessible by Default)
- WAI-ARIA 模式内置,符合 WCAG 2.1 标准
- 完整的键盘导航支持(Tab、Enter、Space、Arrow 键)
- 焦点管理自动处理,包括焦点陷阱和焦点恢复
- 屏幕阅读器友好,正确的
role、aria-*属性 - 支持
prefers-reduced-motion减少动画偏好
4. 真正的无样式(Truly Headless)
- 零样式注入,不产生任何 CSS
- 通过
data-scope、data-part、data-state属性暴露组件结构 - 支持任意 CSS 方案:Tailwind CSS、Panda CSS、CSS Modules、styled-components、Emotion、纯 CSS
asChild属性支持组件合并,避免额外 DOM 节点
// Tailwind CSS 样式化
<Dialog.Content
data-state="open"
className="fixed inset-0 z-50 bg-white rounded-lg shadow-xl
data-[state=open]:animate-in data-[state=closed]:animate-out"
>5. 丰富的组件集合
45+ 个组件覆盖常见交互场景:
交互组件:Accordion、Dialog、Drawer、Menu、Popover、Tabs、Tooltip、Tour
数据输入:Checkbox、Radio Group、Select、Combobox、Slider、Switch、Number Input、Pin Input、Tags Input、Date Picker、Date Input、Color Picker、File Upload、Password Input、Editable
数据展示:Avatar、Carousel、Progress(线性/环形)、Rating Group、QR Code、Tree View、Listbox、Navigation Menu、Segment Group、Steps、Marquee
高级组件:Floating Panel、Splitter、Image Cropper、Signature Pad、Timer、Clipboard、Toggle Group、Angle Slider
工具函数:Focus Trap、Presence、Portal、Locale、Client Only、Highlight、JSON Tree View、Swap、Frame
6. 完善的 TypeScript 支持
- 完整的 TypeScript 类型定义
- 泛型组件支持类型推断
- 所有回调函数的参数类型完善
- IDE 智能提示友好
<Select.Root<{ items: Array<{ label: string; value: string }> }}>
{/* 类型安全的 items 和 onValueChange */}
</Select.Root>7. 服务端渲染(SSR)支持
- 完全支持 Next.js、Nuxt、SolidStart、SvelteKit 等 SSR 框架
- Portal 组件正确处理客户端 hydration
ClientOnly组件处理浏览器专属逻辑- Nuxt 模块
ark-ui官方支持
8. 动画与过渡支持
- 内置
Presence组件处理挂载/卸载动画 - 通过
data-state驱动 CSS 动画 - 支持 Framer Motion、Vue Transition 等动画库
data-[state=open]/data-[state=closed]状态样式
/* CSS 动画 */
[data-scope="dialog"][data-part="content"][data-state='open'] {
animation: fadeIn 200ms ease-out;
}
[data-scope="dialog"][data-part="content"][data-state='closed'] {
animation: fadeOut 200ms ease-in;
}9. 国际化支持
Locale工具组件支持多语言/多时区- Date Picker 支持区域化日期格式
- 支持 RTL(从右到左)布局
FormatByte、FormatTime、FormatRelativeTime等格式化工具
10. 活跃维护与持续迭代
- 2026 年保持每月多次发布的节奏
- 持续新增组件(Date Input、Timer、Tour 等近期加入)
- 社区响应积极,GitHub Issue 处理及时
- Changesets 管理版本,发布流程规范
生态图
核心生态
- Zag.js:底层状态机引擎,可独立使用,提供框架无关的组件交互逻辑
- Park UI:基于 Ark UI + Panda CSS 的完整设计系统,提供预样式化组件,对标 shadcn/ui
- @ark-ui/mcp:Model Context Protocol 服务器,让 AI 编码助手理解 Ark UI 组件 API
配套工具
- Panda CSS:Chakra UI 团队打造的原子化 CSS 引擎,与 Ark UI 深度集成
- Nuxt 模块:
nuxt.com/modules/ark-ui官方 Nuxt 集成模块 - Storybook:项目内置
.storybook/配置,组件开发可视化调试
适用场景
1. 多框架设计系统
场景:团队同时维护 React、Vue、Solid 或 Svelte 项目,需要统一的组件行为和设计语言
优势:
- 唯一支持 4 个主流框架的 headless 组件库
- 状态机逻辑跨框架一致,行为不会出现框架间差异
- 减少多框架设计系统的维护成本
案例:大型企业的多技术栈产品矩阵、跨框架组件库团队
2. 自定义品牌设计系统
场景:需要构建完全定制的品牌设计系统,不允许第三方样式干扰
优势:
- 零样式注入,完全控制视觉表现
data-scope/data-part属性暴露组件结构,样式化清晰- 搭配 Park UI 可快速获得基础样式,再按需定制
案例:中大型企业内部组件库、SaaS 产品的品牌化 UI
3. 高可访问性要求项目
场景:政府网站、教育平台、医疗系统,需符合 WCAG 2.1 AA/AAA 标准
优势:
- WAI-ARIA 模式内置,无需手动实现复杂交互模式
- 状态机确保焦点管理、键盘导航的正确性
- 组件行为经过充分测试,边界情况覆盖完善
案例:政府门户网站、在线学习平台、医疗信息系统、银行/金融平台
4. 复杂交互组件
场景:需要实现 Date Picker、Combobox、Color Picker、Image Cropper 等复杂交互组件
优势:
- 状态机建模使复杂交互逻辑清晰可维护
- 处理了焦点陷阱、键盘导航、多状态同步等复杂场景
- 避免重复实现容易出错的交互逻辑
案例:企业级应用后台、CMS 系统、数据可视化平台
5. Panda CSS 技术栈项目
场景:使用 Panda CSS 作为样式方案,需要无缝集成的组件库
优势:
- Ark UI 与 Panda CSS 同属 Chakra UI 团队,深度集成
- Park UI 提供开箱即用的 Panda CSS 样式化组件
defineSlotRecipe函数组织组件变体和部件样式
案例:使用 Panda CSS 的新项目、从 Chakra UI v2 迁移的项目
6. 服务端渲染项目
场景:Next.js、Nuxt、SolidStart 等 SSR 框架项目,需要 SEO 优化
优势:
- 完全支持 SSR,正确处理 hydration
ClientOnly组件处理浏览器专属逻辑- Nuxt 官方模块支持
案例:电商网站、内容平台、营销落地页、文档站
7. AI 辅助开发工作流
场景:使用 AI 编码助手(Claude、Cursor 等)开发 UI 组件
优势:
@ark-ui/mcp提供 Model Context Protocol 支持- AI 助手可直接查询 Ark UI 组件 API 和用法
- 完善的
llms.txt文档供 AI 模型参考
案例:AI 驱动的快速原型开发、自动化组件生成
开发与工程化
项目结构
node_modules/
├── @ark-ui/
│ ├── react/ # React 组件包
│ │ ├── accordion/
│ │ ├── dialog/
│ │ ├── slider/
│ │ └── ...
│ ├── vue/ # Vue 组件包
│ ├── solid/ # Solid 组件包
│ └── svelte/ # Svelte 组件包
├── @zag-js/ # Zag.js 状态机(Ark UI 内部依赖)
│ ├── core/
│ ├── accordion/
│ ├── dialog/
│ └── ...
TypeScript 配置
{
"compilerOptions": {
"strict": true,
"jsx": "react-jsx",
"moduleResolution": "bundler"
}
}Ark UI 使用 TypeScript 严格模式,所有组件提供完善的类型定义。
测试策略
import { render, screen } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { Dialog } from '@ark-ui/react/dialog'
test('打开对话框', async () => {
const user = userEvent.setup()
render(
<Dialog.Root>
<Dialog.Trigger>打开</Dialog.Trigger>
<Dialog.Positioner>
<Dialog.Content>
<Dialog.Title>标题</Dialog.Title>
<Dialog.Description>描述内容</Dialog.Description>
</Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>
)
await user.click(screen.getByText('打开'))
expect(screen.getByRole('dialog')).toBeInTheDocument()
})样式集成
Tailwind CSS
// 使用 data 属性选择器
<Tabs.Root className="w-full">
<Tabs.List className="flex border-b">
<Tabs.Trigger
value="tab1"
className="px-4 py-2 data-[state=active]:border-b-2
data-[state=active]:border-blue-500"
>
标签 1
</Tabs.Trigger>
</Tabs.List>
<Tabs.Content value="tab1">内容 1</Tabs.Content>
</Tabs.Root>Panda CSS(推荐)
import { defineSlotRecipe } from '@pandacss/dev'
const dialogRecipe = defineSlotRecipe({
className: 'dialog',
slots: ['trigger', 'backdrop', 'positioner', 'content', 'title', 'description', 'closeTrigger'],
base: {
backdrop: {
bg: 'blackAlpha.600',
backdropFilter: 'blur(4px)',
},
content: {
bg: 'white',
rounded: 'lg',
shadow: 'xl',
p: '6',
},
title: {
fontSize: 'xl',
fontWeight: 'bold',
},
},
})纯 CSS(data 属性选择器)
/* 通过 data-scope 和 data-part 定位组件部件 */
[data-scope="slider"][data-part="track"] {
height: 4px;
background: #e2e8f0;
border-radius: 2px;
}
[data-scope="slider"][data-part="range"] {
background: #3b82f6;
height: 100%;
border-radius: 2px;
}
[data-scope="slider"][data-part="thumb"] {
width: 16px;
height: 16px;
background: white;
border: 2px solid #3b82f6;
border-radius: 50%;
}
/* 通过 data-state 处理状态样式 */
[data-scope="dialog"][data-part="content"][data-state="open"] {
opacity: 1;
transform: scale(1);
}
[data-scope="dialog"][data-part="content"][data-state="closed"] {
opacity: 0;
transform: scale(0.95);
}构建优化
- Tree Shaking:每个组件独立子包(如
@ark-ui/react/dialog),只打包使用的组件 - 代码分割:动态导入复杂组件(如 Date Picker、Color Picker)
- Zag.js 共享:多个组件共享 Zag.js 状态机引擎,避免重复打包
// 动态导入复杂组件
const DatePicker = lazy(() => import('./DatePicker'))调试技巧
// 1. 检查 data 属性了解组件结构
// DevTools 中查看 data-scope、data-part、data-state
// 2. 状态机调试(开发环境)
// Zag.js 支持状态机日志输出
// 3. 检查 ARIA 属性
// 验证 role、aria-* 属性是否正确
// 4. 键盘导航测试
// Tab / Shift+Tab 焦点移动
// Enter / Space 触发操作
// Arrow 键导航列表
// Escape 关闭弹出层性能与安全
性能优化
- 状态机轻量化:Zag.js 基于 XState FSM 的最小化实现,只保留 UI 组件所需的状态机能力
- 按需加载:每个组件独立子包,支持 tree shaking
- 框架适配器薄封装:框架适配层极薄,开销可忽略
- Portal 优化:弹出层渲染到 DOM 根部,避免 z-index 和溢出问题
- 动画性能:使用 CSS transform/opacity,避免 layout 抖动
Bundle 体积
Ark UI 组件体积取决于框架适配器和 Zag.js 状态机。由于状态机逻辑共享,组件数量增加时边际成本递减。
| 组件 | 大致 gzip 体积 |
|---|---|
| Dialog | ~4-5 KB |
| Slider | ~3-4 KB |
| Tabs | ~3-4 KB |
| Select | ~5-6 KB |
| Date Picker | ~8-10 KB |
| Zag.js 核心(共享) | ~2-3 KB |
注:Ark UI 的组件包含 Zag.js 状态机逻辑,体积略高于纯 React headless 库,但换来的是跨框架一致性和行为可预测性。
安全考虑
- XSS 防护:不直接注入 HTML,使用
dangerouslySetInnerHTML需谨慎 - 焦点陷阱:Dialog、Drawer 等组件自动处理焦点陷阱,防止焦点逃逸
- 点击外部关闭:安全处理点击事件,避免误触发
- 键盘事件:防止键盘事件冒泡导致的意外行为
- Portal 安全:Portal 渲染到指定容器,避免 DOM 注入风险
可访问性测试
import { axe, toHaveNoViolations } from 'jest-axe'
test('无可访问性违规', async () => {
const { container } = render(
<Dialog.Root open>
<Dialog.Positioner>
<Dialog.Content>
<Dialog.Title>标题</Dialog.Title>
</Dialog.Content>
</Dialog.Positioner>
</Dialog.Root>
)
const results = await axe(container)
expect(results).toHaveNoViolations()
})技术对比
Headless 组件库横向对比
| 特性 | Ark UI | Radix UI | Base UI | React Aria | Headless UI | Ariakit |
|---|---|---|---|---|---|---|
| 框架支持 | React / Vue / Solid / Svelte | React | React | React | React | React |
| 组件数量 | 45+ | 30+ | 25+ | 40+ | 16 | 25+ |
| 底层技术 | Zag.js 状态机 | 原生 React | 原生 React | Hooks | 原生 React | 原生 React |
| 无障碍 | WAI-ARIA 内置 | WAI-ARIA 内置 | WAI-ARIA 内置 | 最严格 WAI-ARIA | WAI-ARIA 内置 | WAI-ARIA 内置 |
| 样式方案 | 无样式 | 无样式 | 无样式 | 无样式(提供 CSS 类) | 无样式 | 无样式 |
| TypeScript | 完善 | 完善 | 完善 | 完善 | 完善 | 完善 |
| SSR 支持 | 完整 | 完整 | 完整 | 完整 | 完整 | 完整 |
| 维护者 | Chakra UI 团队 | WorkOS | MUI 团队 | Adobe | Tailwind Labs | 社区 |
| Stars | 5.3k+ | 15k+ | 9.5k+ | 15.1k+ | 28.6k+ | 8.6k+ |
| 周下载量 | 630k+ | 4.4M+ | 3.7M+ | 4.47M+ | 5.49M+ | 698k+ |
Ark UI vs Radix UI
| 特性 | Ark UI | Radix UI |
|---|---|---|
| 跨框架 | React / Vue / Solid / Svelte | 仅 React |
| 底层实现 | Zag.js 状态机(可预测) | 原生 React hooks |
| 组件数量 | 45+(含 Date Picker、Color Picker 等高级组件) | 30+(缺少部分高级组件) |
| 样式化方式 | data-scope / data-part 属性 | data-state 属性 |
| 生态 | Park UI(Panda CSS) | shadcn/ui(Tailwind CSS) |
| 维护状态 | 活跃开发,每月多次发布 | 维护放缓(WorkOS 收购后) |
| 学习曲线 | 中等(需理解状态机概念) | 中等(Compound Component 模式) |
| 适用场景 | 多框架设计系统 | React 单框架项目 |
Ark UI vs React Aria
| 特性 | Ark UI | React Aria |
|---|---|---|
| API 风格 | 组件式(Compound Component) | Hooks 式(useButton、useDialog) |
| 预构建组件 | 是,提供完整组件 | 否,只提供 hooks,需自行构建 |
| 国际化 | 基础支持(Locale 工具) | 深度支持(RTL、区域化日期/数字) |
| 无障碍严格度 | 优秀 | 最严格(Adobe 级别) |
| 开发效率 | 更高(组件即插即用) | 较低(需更多代码组装) |
| 适用场景 | 快速构建设计系统 | 对无障碍有合同级要求的企业项目 |
选择决策指南
| 如果你需要… | 选择 |
|---|---|
| 多框架统一设计系统(React + Vue + Solid + Svelte) | Ark UI |
| React 项目 + Tailwind CSS + 成熟生态 | Radix UI / shadcn/ui |
| 最严格的无障碍合规 | React Aria |
| 最活跃的维护团队 | Base UI |
| Tailwind 优先 + 最简 API | Headless UI |
| 最小 bundle 体积 | Ariakit |
| 开箱即用 + 复杂组件(Date Picker、Color Picker) | Ark UI |
最佳实践
1. 使用 Park UI 快速起步
对于新项目,推荐直接使用 Park UI(基于 Ark UI + Panda CSS)获得预样式化组件,再按需定制:
# Park UI 基于 Panda CSS
npm install @park-ui/react2. 样式组织策略
// 推荐:使用 data-scope 和 data-part 组织样式
// CSS 文件按组件 scope 组织
// slider.css
[data-scope="slider"][data-part="root"] { ... }
[data-scope="slider"][data-part="track"] { ... }
[data-scope="slider"][data-part="range"] { ... }
[data-scope="slider"][data-part="thumb"] { ... }3. z-index 管理
// 推荐:使用 CSS 变量统一管理 z-index
// 所有浮层组件共享基础 z-index,通过 --layer-index 偏移
const zIndexVars = {
'--layer-index': '1000', // 基础层
}
// 避免:直接设置过高的 z-index
// 不要:style={{ zIndex: 99999 }}
// Portal 组件使用 --layer-index 偏移
<Portal style={{ '--layer-index': '100' }}>4. 组件封装
// 封装通用的确认对话框
function ConfirmDialog({
open,
onOpenChange,
title,
description,
onConfirm,
children,
}: {
open: boolean
onOpenChange: (details: { open: boolean }) => void
title: string
description: string
onConfirm: () => void
children: React.ReactNode
}) {
return (
<Dialog.Root open={open} onOpenChange={onOpenChange}>
<Portal>
<Dialog.Backdrop />
<Dialog.Positioner>
<Dialog.Content>
<Dialog.Title>{title}</Dialog.Title>
<Dialog.Description>{description}</Dialog.Description>
<div className="flex justify-end gap-2 mt-4">
<Dialog.CloseTrigger asChild>
<button>取消</button>
</Dialog.CloseTrigger>
<button onClick={onConfirm}>{children || '确认'}</button>
</div>
</Dialog.Content>
</Dialog.Positioner>
</Portal>
</Dialog.Root>
)
}5. 表单集成
import { useForm, Controller } from 'react-hook-form'
import { Select } from '@ark-ui/react/select'
function FormWithSelect() {
const { control, handleSubmit } = useForm()
return (
<form onSubmit={handleSubmit(data => console.log(data))}>
<Controller
name="category"
control={control}
render={({ field }) => (
<Select.Root
items={[
{ label: '技术', value: 'tech' },
{ label: '设计', value: 'design' },
]}
value={[field.value]}
onValueChange={(details) => field.onChange(details.value[0])}
>
<Select.Label>分类</Select.Label>
<Select.Control>
<Select.Trigger>
<Select.ValueText placeholder="请选择" />
</Select.Trigger>
</Select.Control>
<Portal>
<Select.Positioner>
<Select.Content>
{/* items 自动渲染 */}
</Select.Content>
</Select.Positioner>
</Portal>
</Select.Root>
)}
/>
</form>
)
}6. 动画实现
import { Presence } from '@ark-ui/react/presence'
import { useState } from 'react'
function AnimatedDialog() {
const [open, setOpen] = useState(false)
return (
<>
<button onClick={() => setOpen(true)}>打开</button>
<Presence present={open}>
<div
data-state={open ? 'open' : 'closed'}
className="dialog-content
data-[state=open]:animate-in data-[state=open]:fade-in
data-[state=closed]:animate-out data-[state=closed]:fade-out"
>
对话框内容
<button onClick={() => setOpen(false)}>关闭</button>
</div>
</Presence>
</>
)
}7. 键盘导航测试清单
每个组件开发完成后,手动验证以下键盘操作:
Tab/Shift+Tab:焦点按预期顺序移动Enter/Space:触发预期操作Arrow键:列表/菜单项正确导航Escape:关闭弹出层/对话框Home/End:跳转到列表首尾(如适用)
技术局限与边界
已知问题
- 社区规模较小:相比 Radix UI(15k+ stars)和 Headless UI(28.6k+ stars),Ark UI(5.3k+ stars)社区规模较小,Stack Overflow 问答较少
- React 非惯用写法:状态机驱动的组件在 React 中不是最惯用的写法,React 专属项目可能有更优选择
- 样式化方式学习成本:
data-scope/data-part样式化方式相对不常见,新手需要适应 - 缺少原生 CSS 类名:不像 React Aria 提供原生 class 可直接覆写,Ark UI 依赖 data 属性选择器
- Zag.js 额外依赖:状态机引擎增加了额外的依赖层和理解成本
设计边界
- 无样式限制:需要自行实现所有样式,初始开发成本较高(搭配 Park UI 可缓解)
- 状态机复杂度:简单组件使用状态机可能过度设计,增加理解成本
- 框架适配延迟:新组件或功能在四个框架间的适配可能有时间差
- Panda CSS 绑定较深:官方推荐 Panda CSS 作为样式方案,非 Panda CSS 用户可能需要额外配置
何时不该使用
- 纯 React 项目:如果只用 React 且不需要跨框架,Radix UI 或 Base UI 更合适(社区更大、生态更成熟)
- 快速原型:需要开箱即用的样式,选择 MUI、Ant Design 或 shadcn/ui
- 简单项目:无需复杂交互,直接使用 HTML + CSS 或轻量组件库
- Tailwind 优先:Headless UI 与 Tailwind 集成更自然,shadcn/ui 基于 Radix + Tailwind
- 最严格无障碍合规:React Aria 提供更严格的 WAI-ARIA 合规性
- 最小 bundle 体积:Ariakit 的 bundle 更小
迁移风险
- Zag.js 耦合:Ark UI 深度依赖 Zag.js,如果 Zag.js 维护出现问题,Ark UI 会受影响
- 框架生态变化:Svelte 支持刚于 2025年6月推出,成熟度低于 React/Vue/Solid
- Park UI 依赖 Panda CSS:如果团队选择 Tailwind CSS,需要额外的样式化工作
学习资源
官方资源
- Ark UI 官网 — 完整文档、组件示例和样式指南
- Ark UI GitHub — 源码、Issue、Changelog
- Ark UI Changelog — 版本更新日志
- Ark UI Showcase — 使用案例展示
- Zag.js 官网 — 底层状态机引擎文档
- Ark UI LLM 文档 — 供 AI 模型参考的文档
视频教程
- Using Ark UI as the Headless UI for your Design System(2025年1月)— 深度讲解 Ark UI 设计系统实践
- Building Svelte Apps with Ark UI Headless Components(2025年7月)— Svelte + Ark UI 实战
- Vue.js Forge 2024: Leveraging Ark UI — Vue + Ark UI 构建组件库
- Park UI: The Shadcn Alternative Built with Panda CSS & Ark UI — Park UI 实战
在线 Playground
博客文章
- Ark UI Design System Series — 官方设计系统系列文章
- Building a Design System with Ark UI - Menu component — 实战教程
- Building a Design System with Ark UI - Avatar component — 实战教程
- What is the difference between Ark UI and zag? — 架构讨论
生态项目
- Park UI 官网 — 基于 Ark UI + Panda CSS 的设计系统
- Park UI GitHub — 源码和 Issue
- Park UI Figma Kit — 设计稿
- Nuxt Ark UI 模块 — Nuxt 集成
对比文章
- Headless UI alternatives: Radix Primitives vs. React Aria vs. Ark UI — LogRocket
- Top Headless UI libraries for React in 2026 — GreatFrontEnd
- shadcn/ui vs Park UI vs Melt UI in 2026 — PkgPulse
中文资源
2026 年现状
版本与发布
- 当前版本:v5.37.x(2026年6月),保持每月多次发布的活跃节奏
- 近期重大更新:
- v5.36.0(2026年4月):新增 DateInput 组件、多触发器支持、Splitter 嵌套
- v5.35.0(2026年3月):Drawer 滑动手势、Pin Input 重构、Toast 优先级队列
- v5.37.0(2026年5月):浮动组件
data-side属性、Splitter CSS 单位支持
- Svelte 支持:2025年6月正式推出
@ark-ui/svelte,支持 Svelte 5 响应式原语
生态发展
- Park UI 成熟:作为 shadcn/ui 的 Panda CSS 替代品,Park UI 持续增长
- @ark-ui/mcp:推出 Model Context Protocol 服务器,支持 AI 辅助开发工作流
- shadcn/ui 竞争:shadcn/ui 默认使用 Radix UI / Base UI,Park UI 作为 Panda CSS 生态的对标方案
- Base UI 崛起:原 Radix 核心成员打造的 Base UI 于 2025年12月发布 v1.0,成为 Ark UI 的新竞争者
维护状态
- Chakra UI 团队全力维护:Segun Adebayo、Christian Schröter、Esther Agbaje 持续投入
- 开源治理规范:使用 Changesets 管理版本、Biome 统一代码风格、Renovate 自动更新依赖
- 社区增长稳定:GitHub stars 5.3k+,npm 周下载量 630k+,社区活跃度稳步提升
推荐策略
现有项目:
- 如果已使用 Ark UI,继续使用,维护活跃且 API 稳定
- 如果使用 Chakra UI v2,可以考虑迁移到 Ark UI + Panda CSS 架构
新项目:
- 多框架设计系统(React + Vue + Solid + Svelte):Ark UI 是唯一选择
- 单 React 项目 + Tailwind CSS:选择 Radix UI / shadcn/ui
- 单 React 项目 + Panda CSS:选择 Ark UI + Park UI
- 需要复杂组件(Date Picker、Color Picker):Ark UI 组件最丰富
长期展望
Ark UI 是当前唯一真正跨框架的 headless 组件库,其状态机驱动的架构保证了行为的可预测性和跨框架一致性。尽管社区规模不及 Radix UI 和 Headless UI,但在以下领域具有独特优势:
- 多框架设计系统的统一行为层
- 复杂交互组件的状态管理
- Panda CSS 生态的核心组件提供者
随着 Svelte 支持的推出和 MCP 工具的发布,Ark UI 正朝着更全面的方向发展。对于需要跨框架一致性或 Panda CSS 集成的团队,Ark UI 是不可替代的选择。
文档版本:v1.0 最后更新:2026-07-07 适用版本:Ark UI v5.37.x 参考资料: