若要功夫深,铁杵磨成针。——曹学《蜀中广记・上川南道彭山县》

八年,一万三千个应用,然后开源:Astryx 想让人和 agent 用同一套工具

设计系统开源时的两种命运

一个公司内部的设计系统拿出来开源,通常只有两种结局。

第一种是变成考古现场:文档停在两年前,组件里全是只有内部人才懂的耦合,示例代码引用的包早就改名了。你 clone 下来,跑不起来,然后关掉。

第二种是被重新打磨成一件产品:依赖理清,文档重写,边界划明白,然后老老实实标上「Beta」。

Astryx 属于第二种。它甚至在第一屏就把这两件事都写清楚了:它是 Meta 内部长了八年的东西,现在开源,目前是 Beta。

项目地址:https://github.com/meta/astryx

它的自我介绍是:一个完全可定制的开源设计系统,为「我们现在的构建方式」而生——由人,以及和他们并肩工作的 agent 一起构建。

这句话里最值得琢磨的是后半段。设计系统这个词已经被说了十几年,而「为 agent 而生」是个新命题。

来路:一万三千个应用

文档里有一句话,比任何功能列表都有说服力:

它在 Meta 内部生长了八年,成了公司里使用最广、规模最大的设计系统,支撑着 13,000 多个应用,由每天依赖它的工程师、设计师和产品团队共同塑造。

八年,13000+ 应用。

这两个数字解释了很多事。为什么它一上来就有 150 多个组件;为什么它敢说自己有「品牌级主题」和「暗色模式」;为什么它的组件不是几个漂亮按钮,而是有表格页、详情页、表单向导、导航模式、数据录入流程这些「模式」。

因为这些东西不是设计出来的,是磨出来的。一个内部系统要服务一万多个应用,它必须长出那些能被反复套用的形状。

而这也带来一个天然的门槛:内部系统的默认假设是「你和我在同一栋楼里」。开源要做的第一件事,就是把这些默认假设一条条拆掉。

「agent ready」到底指什么

这是我最想认真讲的一节,因为它很容易被做成噱头。

大多数产品说「支持 AI」,指的是加了一个 MCP 服务器,或者提供一个 llms.txt,或者写了一篇「如何让 Cursor 用我们的库」的教程。

Astryx 的做法不一样。它把这件事拆成了三个同时设计的东西:

API。 每个组件遵循同一套命名、prop 和组合规则。学会几个,剩下的就能猜。

文档。 每个组件都被彻底写清楚,不是「有这个 prop」,而是「这个 prop 在这个场景下该给什么值」。

CLI。 组件文档、模板、脚手架、主题、codemod,全都能从命令行拿到。

三者一起设计的结果是:人和 AI 拿到的是同一份参考。

注意这个顺序。它不是「先做给人用的东西,再想办法让 AI 也能用」。它是从一开始就把「可预测」当成设计目标——而可预测这件事,恰好同时服务于两种使用者。

那句题眼

整份文档里我最喜欢的一句话,是一条原则的结尾:

每一个让 Astryx 对 AI 更容易的改动,也让人用起来更容易。

这句话值得抄在本子上。

它其实是在反驳一种流行的做法:为了迁就模型的上下文长度和推理能力,专门做一套「给 AI 用的简化接口」。那套接口往往更笨、更受限、更容易出错——然后人类用户被留在原地,看着一个为机器降级的产品。

Astryx 的判断相反:如果一个改动只让 AI 变好,让人变差,那它大概是错的。因为 AI 的困难和人 的困难高度重叠——记不住 API、猜不到默认值、看不懂文档、被隐式行为坑到。

模型遇到的「上下文不够」,和人遇到的「我没时间读完这三百页」,本质是同一种匮乏。

四个「不一样」,逐条拆

文档列了四条差异,每一条都指向一个具体的设计决策。

第一,内部是敞开的。 组件被设计成可以在任何层级组合,而不是锁在一个封闭的顶层 API 后面。你会伸手去拿的那些积木,都直接导出。想再往下走,swizzle 会把组件的完整源码弹到你的项目里,归你所有。

「弹出来归你所有」这个动作,在设计系统里叫 eject,是一个很有分量的承诺。它意味着这个库承认:总有一天你会想改一个我没想到的地方,那我给你一把钥匙,而不是一堵墙。

当然,代价也很清楚——弹出来的源码从此由你维护,上游的更新不会再流回来。这是标准的「逃生舱悖论」:门开着,但走出去的人得自己走路。

第二,不锁样式。 它自己用 StyleX 写样式,但这对使用者是隐形的。你可以用 className 覆盖,用 Tailwind、CSS Modules 或者纯 CSS 都行,用你项目本来就有的那套。

这一条很务实。样式的选型在真实项目里往往早于组件库,一个库如果要求你换掉整套样式方案,那它在评审会上活不过十分钟。

第三,换主题不用包一层。 一个主题就是一串 CSS 自定义属性的覆盖。所以设计师可以让 Astryx 变得「一眼就是他们的」,而不需要 fork,也不需要写一堆 wrapper 组件。

这句话的技术含量在于「不用包一层」。包装是设计系统最常见的腐化方式:为了改一个按钮的颜色,你在外面套一个 MyButton,然后 MyButton 再套 MySpecialButton,三年后没人知道哪个是真的。

第四,人和 agent 用同一套东西。 也就是上一节讲的那件事。

一个诚实的张力

这里得说一句文档没明说、但摆在那里的事。

它说「不锁样式库」,但你安装的时候,仍然要装 @stylexjs/stylex。

这不是矛盾,但需要说清楚:它不锁定你的写法,但它锁定了一个运行时依赖。

StyleX 在编译期把样式抽成原子 CSS,所以产出的东西对消费者是透明的——你写 Tailwind,它照样工作。但那个包仍然在你的依赖树里。

这个区别很重要。它意味着你不是「可以完全不用 StyleX」,而是「不必用 StyleX 的写法」。前者是自由,后者是便利。

文档在这一点上算是坦白的:安装命令里明明白白写着三个包,@stylexjs/stylex 就在其中。它没有假装这件事不存在。

七个主题,名字很会起

@astryxdesign/theme-* 提供七套现成主题:neutral、butter、chocolate、matcha、stone、gothic、y2k。

这七个词放在一起,有一种奇怪的和谐:三个是食物,两个是材质,一个是风格,一个是年代。

命名这件事看着不重要,其实很能说明团队的性格。一个只会起 light、dark、brand-primary 的设计系统,通常也只会做很安全的设计。而敢把主题叫「抹茶」和「千禧年」的团队,大概率真的在意外观。

当然,七套主题全都「完全可定制」——因为主题本身就是 CSS 变量,改它不需要动组件。

四条原则,前两条最见水平

第一条:引导,而不是强制。 组件给你能力,而不是给你一堆跟你作对的护栏。设计的意见放在文档和示例里;只要你传了值,组件就渲染它。

这条原则在工程上是有风险的。它意味着组件不会替你拦住错误用法,也意味着文档要承担更大的责任。但它换来的是可组合性——一个总在替你决定的组件,是没法被组合的。

第二条:强而可查的约定。 每个组件遵循同一套命名、prop 和组合规则,而且每一个都被彻底记录。于是学会几个之后,剩下的会显得眼熟;人和 AI 都能预测一个没见过的组件怎么用。

这条和第一条是一对。第一条放开控制,第二条就必须用一致性来补偿。放开而不一致,是混乱;放开而高度一致,才是自由。

第四条也有意思:靠测量赢得,而不是靠断言。 他们说会去测试这些约定,而不是宣称它们;把结论握得松一点,遇到反例就重新审视。

设计系统最容易变成教条。愿意写「我们的结论可能是错的」,是一种很少见的自我约束。

安装:三个包,加一个 CLI

前置条件是 React 19 或更高——react 和 react-dom 是 @astryxdesign/core 的 peer 依赖。

1
2
npm install @astryxdesign/core @astryxdesign/theme-neutral @stylexjs/stylex
npm install -D @astryxdesign/cli

然后是那句关键的话:不需要构建插件,不需要 PostCSS 或 Babel 配置。

一个设计系统能做到「几个 CSS 引入加一个主题 Provider 就完事」,说明它在打包上做了很多工作。而这些工作,用户是看不见的——这正是好基础设施的样子。

CLI 还可以全局装一次,这样 astryx 在任何目录下都能用,而且「每个编码 agent 都知道 Astryx」;在具体项目里,它仍然跑项目自己的那个版本。

这句话里藏着一个新问题:全局工具和项目版本的冲突。而它给出的答案和大多数人一样——项目优先,全局只是入口。

文档还推荐「两个都装」,并让你跑 astryx docs global/project-global-or-both 去看对比。用 CLI 解释 CLI 该怎么装,这个设计挺自洽。

那个 package.json 脚本,是全篇最真实的细节

有一个小节,短到容易略过,但我认为它是整份文档里最「2026 年」的一段。

它建议你在 package.json 里加一条脚本:

1
2
3
"scripts": {
"astryx": "node node_modules/@astryxdesign/cli/clients/cli/bin/astryx.mjs"
}

然后这样用:npm run astryx -- component --list。

理由是:这样可以避免 AI 助手或新来的开发者直接调用 CLI 时出现路径错误。

这句话的信息量很大。它承认了一个现实:现在调用你工具的第一个人,很可能不是一个读过文档的人。

一个 agent 会打开项目,看到有个叫 astryx 的命令,然后试图运行它。如果这个命令只在某个特定的 shell 环境里存在,它就会失败,然后 agent 会开始瞎猜,然后你的终端里会出现一串没人看得懂的报错。

用一个 npm script 把入口固定下来,是一件很小的事。但它是「为 agent 设计」这句话落到实处的样子——不是加一个接口,而是把已有的接口做得更不容易走错。

顺带说,这也是那句题眼的又一个例子:这条脚本对人同样有用。新同事第一次 clone 项目,也不会再问「astryx 命令怎么找不到」。

成熟度:它标得很清楚

一个 Beta 项目最容易犯的错,是把还没做完的东西说得像做完了一样。

这份文档没有。

它明确说:@astryxdesign/lab 是实验组件,只在内部用于 Storybook 和 sandbox,不发布到 npm。@astryxdesign/vega 和 @astryxdesign/charts 只以 @canary 标签发布,还没有稳定版本。

三句话,划清了三条线:什么是稳定的,什么是内部的,什么是只能尝鲜的。

在一个「什么都想要」的生态里,愿意把自己的半成品标出来,是一种很稀缺的自制力。

最后

回到开头那两种命运。

Astryx 选了第二种,但它做的事情比「打磨一下再发布」更多一点。

它把一个在大公司内部长了八年、支撑过一万多个应用的设计系统,重新回答了一个新问题:当一半的使用者是 agent 时,一个设计系统该长什么样?

它的答案不是加接口,而是三个同时做的决定:让 API 可预测,让文档能被机器读,让 CLI 成为一等公民。然后立了一条原则来约束自己——每一个让 AI 变容易的改动,也必须让人变容易。

这条原则如果真能守住,它就不只是一个设计系统。它会是那种少见的、没有为机器降级的产品。

至于那七个主题里的「抹茶」和「千禧年」——一个愿意这样起名字的团队,大概也知道自己在做什么。