@animus-ui/system

中文文档
0.1.87发布于 2026/10/24MITTypeScriptES Module构建来源已验证近 30 天下载 6931

Animus 设计系统构建器 — 符号, 道具组, 全球风格

中文使用文档
文档目录17 节
  1. 安装
  2. 快速启动
  3. 1. 确定主题
  4. 2. 与道具组创建系统
  5. 3. 构筑组件
  6. 构建链
  7. 扩展组件
  8. 倒计时值
  9. 锁定位置后退
  10. 查看过渡
  11. 相对状态选择器
  12. 颜色模式
  13. "系统"是没有属性
  14. 外表记录
  15. 融合合同
  16. 出口
  17. 许可证

@animus-ui/系统

React的设计系统构建器. 由零运行时间驱动的CSS-in-JS——样式提取到建设时间的静态CSS.

安装

npm install @animus-ui/system

与提取驱动程序对等 : @animus-ui/vite-plugin, (中文(简体) ), @animus-ui/next-plugin, (中文(简体) ), @animus-ui/unplugin,或 @animus-ui/cli。 。 。 .

快速启动

1. 确定主题

import { createTheme } from '@animus-ui/system';

const theme = createTheme()
  .addBreakpoints({ sm: 480, md: 768, lg: 1024 })
  .addColors({
    gray: { 100: '#f0f0f0', 800: '#1a1a1a' },
    blue: { 400: '#3d94ff', 700: '#003d99' },
    red: { 500: '#e63946' },
  })
  .addColorModes('dark', {
    dark: {
      primary: 'blue.400',
      danger: 'red.500',
      bg: 'gray.800',
      text: 'gray.100',
    },
    light: {
      primary: 'blue.700',
      danger: 'red.500',
      bg: 'gray.100',
      text: 'gray.800',
    },
  })
  .addScale({
    name: 'space',
    values: { sm: '0.5rem', md: '1rem', lg: '1.5rem' },
  })
  .build();

type AppTheme = typeof theme;

declare module '@animus-ui/system' {
  interface Theme extends AppTheme {}
}

2. 与道具组创建系统

预先建造的团体船 与包裹。 把它们编成自己的语义组:

import { createSystem } from '@animus-ui/system';
import {
  background,
  border,
  color,
  flex,
  layout,
  shadows,
  space,
  typography,
} from '@animus-ui/system/groups';

const bundle = createSystem()
  .addGroup('surface', { ...color, ...border, ...shadows, ...background })
  .addGroup('space', space)
  .addGroup('text', typography)
  .addGroup('arrange', { ...flex, ...layout })
  .build();

export const { createGlobalStyles, createKeyframes } = bundle;

build() 返回捆绑,而不是系统。 关键框架收集和 全局风格的块在模块镜下的捆绑上注册 导出名称,以及 seal() 生产系统组件的构建方式如下:

export const motion = createKeyframes({
  pulse: { '0%, 100%': { opacity: 1 }, '50%': { opacity: 0.6 } },
});

export const globalStyles = createGlobalStyles({
  body: { margin: 0, bg: 'bg', color: 'text' },
});

export const ds = bundle
  .registerKeyframes({ motion })
  .registerGlobalStyles({ globalStyles })
  .seal();

注册密钥必须等于导出名称, 因为提取符 决定 motion.pulse 引用该名称。 关键框架和全球风格 块共享一个词汇名空间.

要利用已公布的设计系统工具包,请从其中任一环节开始 .extend() 它将工具包的注册和信物合并到你的(kit as 基地,你后来的呼声赢得了冲突, 和提取管道 在同一边上发现工具包 :

import { system as kitSystem, theme as kitTheme } from '@acme/kit';

const theme = createTheme().extend(kitTheme).build();
const bundle = createSystem()
  .extend(kitSystem)
  .addProps({ cursor: { property: 'cursor' } })
  .build();

createSystem({ includes: [...] }) 财务报告和审定财务报表 .from() 已贬值:它们添加 在不合并登记册的情况下加入调查组.

每个组成为一组可选入的道具,组件可以使用 .system()数字 :

const Box = ds
  .styles({})
  .system({ surface: true, space: true })
  .asElement('div');

// Box now accepts: color, bg, border, shadow, p, m, gap, etc.
<Box bg="bg" p="md" borderBottom="1px solid" />;

3. 构筑组件

export const Alert = ds
  .styles({
    display: 'flex',
    alignItems: 'flex-start',
    p: 'md',
    borderRadius: '4px',
    fontSize: '14px',
    lineHeight: '1.5',
  })
  .variant({
    prop: 'variant',
    variants: {
      filled: { color: 'bg' },
      outline: { bg: 'transparent', borderWidth: '1px', borderStyle: 'solid' },
    },
  })
  .variant({
    prop: 'intent',
    variants: {
      info: { bg: 'primary' },
      danger: { bg: 'danger' },
    },
  })
  .compound(
    { variant: 'outline', intent: 'info' },
    { borderColor: 'primary', color: 'primary' }
  )
  .compound(
    { variant: 'outline', intent: 'danger' },
    { borderColor: 'danger', color: 'danger' }
  )
  .states({
    disabled: { opacity: 0.5, pointerEvents: 'none' },
  })
  .system({ space: true })
  .asElement('div');

<Alert variant="filled" intent="info" m="sm" disabled />;

构建链

链条执行级联命令——每个方法映射到CSS @layer数字 :

ds.styles()    → @layer anm-base
  .variant()   → @layer anm-variants
  .compound()  → @layer anm-compounds
  .states()    → @layer anm-states
  .system()    → @layer anm-system
  .props()     → @layer anm-custom
  .asElement() → typed React component

类型系统防止调用方法出轨.

扩展组件

.extend() 在组件上启动一个继承所有 组件声明、自定义道具及其召回。 其方法 可以在任意顺序下调用; 自定义道克重新声明在 .props() 替换 仅用于此扩展及其自身的扩展.

const Card = ds
  .props({
    inset: { property: 'padding', transform: (v) => `${Number(v) * 4}px` },
  })
  .asElement('div');

export const Panel = Card.extend()
  .styles({ display: 'grid' })
  .asElement('section');

<Panel inset={3} />; // padding: 12px, computed by Card's callback

扩展读取从父组件继承的回调, 当其 模块运行,就像作者一样 Card.extend() 已经 有两个后果:

  • 父模块必须在扩展前完成初始化 模块:无法在导入周期内宣布扩展 首先评价扩展模块.
  • 客户端模块中定义的自定义道具召回的父程序 (单位:千美元)'use client'无法从服务器模块中扩展; 声明 客户端模块中的扩展名.

倒计时值

firstThatWorks() 列出一个属性的值,优先. 提取器声明为倒置, 所以浏览器支持的最后一个 胜出,折出一跑赤裸的 var() 值变为一个嵌入式倒计时 :

import { firstThatWorks } from '@animus-ui/system';

const Header = ds
  .styles({
    position: firstThatWorks('sticky', '-webkit-sticky', 'fixed'),
    scrollMarginTop: firstThatWorks(
      'var(--header-height)',
      'var(--bar-height)',
      '4rem'
    ),
  })
  .asElement('header');
.animus-Header-… {
  position: fixed;
  position: -webkit-sticky;
  position: sticky;
  scroll-margin-top: var(--header-height, var(--bar-height, 4rem));
}

每个成员按财产打字,按其规模解决。 这个 以组件样式、全局样式和键框来工作。 写成 调用站点道具它只在运行时读取,运行时应用首选值 独自一人,且加以警告animus.first-that-works.unextracted) (中文(简体) ). 降低修饰符 用于浏览器目标, 如在Vite 或 Animus 自己的闪电 CSS targets,将每个已经支持的目标降回倒计时.

锁定位置后退

createPositionTry,在已建的捆绑上 锚定位. 以出口名称登记收藏,截至 键,并使用 positionTryFallbacks数字 :

export const { createPositionTry } = bundle;
export const placements = createPositionTry({
  above: { positionArea: 'top', marginBottom: '{space.8}' },
  flipStart: { top: 'anchor(bottom)', insetInlineStart: 'anchor(start)' },
});
export const ds = bundle.registerPositionTry({ placements }).seal();

const Tooltip = ds
  .styles({
    positionAnchor: '--trigger',
    positionTryFallbacks: `${placements.above}, flip-block`,
  })
  .asElement('div');
@layer anm-global {
  @position-try --animus-pt-… {
    position-area: top;
    margin-bottom: 0.5rem;
  }
}
.animus-Tooltip-… {
  position-anchor: --trigger;
  position-try-fallbacks: --animus-pt-…, flip-block;
}

每个职位都取自其描述词。 只不过什么 @position-try 接受的属性包括:设置、边距、大小和自配对属性, positionAnchor 财务报告和审定财务报表 positionArea; 其他任何东西都是类型错误,以及 掷出. 由于数组是 反应性价值。 每个注册位置都发出,是否使用.

查看过渡

createViewTransitions,在已建的捆绑上,名称视图-过渡 课程。 每个过渡 group, (中文(简体) ), imagePair, (中文(简体) ), old 财务报告和审定财务报表 new 槽样式 其伪要素,并将关键框架的参考、标志和条件作为 一个全局风格块。 以出口名称登记收藏:

export const { createKeyframes, createViewTransitions } = bundle;
export const motion = createKeyframes({
  slideIn: { from: { translate: '100% 0' } },
  slideOut: { to: { translate: '-100% 0' } },
});
export const transitions = createViewTransitions({
  slide: {
    group: { animationDuration: '250ms', _motionReduce: { animation: 'none' } },
    old: { animationName: motion.slideOut },
    new: { animationName: motion.slideIn },
  },
});
export const ds = bundle
  .registerKeyframes({ motion })
  .registerViewTransitions({ transitions })
  .seal();

const Card = ds
  .styles({
    viewTransitionName: 'match-element',
    viewTransitionClass: transitions.slide,
  })
  .asElement('article');
@layer anm-global {
  ::view-transition-group(*.animus-vt-…) {
    animation-duration: 250ms;
  }
  @media (prefers-reduced-motion: reduce) {
    ::view-transition-group(*.animus-vt-…) {
      animation: none;
    }
  }
  ::view-transition-old(*.animus-vt-…) {
    animation-name: animus-kf-…;
  }
  ::view-transition-new(*.animus-vt-…) {
    animation-name: animus-kf-…;
  }
}

校对的指纹是其类,所以 String(transitions.slide) 还有名字 如果一个框架需要一个类,例如React's <ViewTransition>每一个 已注册的转换输出,是否使用.

相对状态选择器

marker(name) 命名要设置一个状态样式的元素的类 另一种:先祖,后人或兄弟姐妹. 将其关系登记为 选择者别名 :

import { createSystem, marker } from '@animus-ui/system';

export const row = marker('row'); // row.className === 'animus-marker-row'

const bundle = createSystem()
  .addSelectors({
    _rowHover: row.ancestor(':hover'),
    _rowHasFocus: row.descendant(':focus-visible'),
  })
  .build();

<tr className={row.className}>
  <Cell _rowHover={{ opacity: 1 }} />
</tr>;
关系选择器
ancestor(':hover')&:where(.m:hover *)
descendant(':hover')&:where(:has(.m:hover))
siblingBefore(...)&:where(.m:hover ~ *)
siblingAfter(...)&:where(:has(~ .m:hover))
anySibling(...)&:where(.m:hover ~ *, :has(~ .m:hover))

状态是伪类或属性选择器([data-open]) (中文(简体) ). 这个 标记坐在里面 :where(),因此,关系除了 组件的类,和元素本身的状态,例如 _hover, (中文(简体) ), 胜于相亲. 没有 _group* 或者说 _peer* 化名由内建而来.

颜色模式

addColorModes(initialMode, modeConfig) 输出 a [data-color-mode="…"] 块 每个模式。 可选的第三个参数选择主题为 OS 参与 :

const theme = createTheme()
  .addColors({ gray: { 100: '#f0f0f0', 800: '#1a1a1a' } })
  .addColorModes(
    'dark',
    {
      dark: { bg: 'gray.800', text: 'gray.100' },
      light: { bg: 'gray.100', text: 'gray.800' },
    },
    {
      // OS preference → declared mode name.
      systemPreference: { light: 'light', dark: 'dark' },
      // CSS `color-scheme` per mode. The two mapped modes default to
      // 'light'/'dark', so `{}` is the whole opt-in here; classify any
      // additional modes explicitly.
      browserColorScheme: {},
    }
  )
  .build();

systemPreference 允许注意后退排放:

@media (prefers-color-scheme: dark) {
  :root:not([data-color-mode]) {
    /* the dark mode's declarations */
  }
}

这个 :not([data-color-mode]) 守护是使一个明确的属性获胜的 CSS独自一人,没有剧本. 这两个值必须命名已声明的模式 .

browserColorScheme 增加中央支助系统 color-scheme 土产 滚动条,窗体控件,以及 UA 样式跟踪活动模式. 当供应时 必须分类每个声明模式,否则模式将默默继承 先前的本地方案——有一个例外: 两种模式由 systemPreference 强迫 light编号 :dark 由验证,所以他们 默认并可能被省略。 在地图绘制模式上有一个明确条目被尊重 仍然有冲突.

选择两个字节都无法给出它之前释放的字节的主题 .

"系统"是没有属性

没有 data-color-mode="system" ——OS跟随状态是属性 状态 缺席,这是上面的媒体卫士唯一能通过的价值. system 是一个保留模式名称并被拒绝.

外表记录

模糊的外表生活在一个版本的密钥下, animus:appearance数字 :

{ "v": 1, "mode": "system" | "<mode name>", "theme": "default" }

这个 theme 轴被保留,目前被忽略——只拥有 模式轴必须保存它不拥有的字段。 包裹船 写纪律,以免手卷: @animus-ui/system/appearance 是一个 微小, 只存储的运行时间子路径( 没有反应, 没有听众, 没有 DOM —— 应用) data-color-mode 留在你的身边:

import {
  SYSTEM_MODE,
  migrateLegacyModeKey,
  persistColorMode,
} from '@animus-ui/system/appearance';

persistColorMode('midnight'); // read-modify-write; unowned fields survive
persistColorMode(SYSTEM_MODE); // "follow the OS" — bootstrap restores absence

// One-shot: move YOUR app's old key into the record, then delete it.
migrateLegacyModeKey('my-app-color-mode', ['midnight', 'paper']);

它拒绝降级一个新版本的唱片,并拒绝 移动共享 color-mode 密钥(一个可能属于另一个应用程序) 来源; 拖曳已读取, 只读)。 一个失败的写入被吞噬 而不是抛出,所以一个已经应用的呼叫者 data-color-mode 保留 即使存储拒绝写入,用户对该会话的选择.

打电话 migrateLegacyModeKey 后油漆,从应用程序条目中返回 迁移模式名称以申请一次访问,或 null 当什么都没有的时候 迁移。 之前在OS解析模式下 修正自己——一个接受的闪光,一次——以后的每个负载都恢复 模式预绘通过生成的靴子.

为了在第一次涂料之前恢复它, 生成一个内线片段 已建成 主题:

import { createAppearanceBootstrap } from '@animus-ui/system/bootstrap';

const { code, cspHash } = createAppearanceBootstrap(theme, {
  storageKey: 'animus:appearance', // default
});

code 是文档头的无依赖性IIFE:它读了记录, 验证 mode 针对主题宣布的名称, 集 data-color-mode (单位:千美元) 有效的明确模式,以及 删除 属性 "system",一个缺失 记录,或未识别值——将控件交回媒体查询. 这个 从不写存储,从不打电话 matchMedia (实现操作系统答案) 输入属性后,它会被冻结,以适应以后的操作系统变化。 预录 纯字符串 color-mode 键被读取一次,仅在记录缺失时 从来没有写.

cspHash 授权准确的脚本。 从文物中取出信头 构建时间和 单引号 数值 - script-src 'sha256-…'。没有引用它 解析为主机源并默默地屏蔽脚本;手抄取后,便去 暂停已宣布的模式名称或存储密钥更改的时刻,并暂停 散列是一个被屏蔽的脚本和错误模式的闪光.

这个子路径正在构建工具. 它从未被组件或运行时间导入 条目,因此无法到达提取的应用程序捆绑 —— 生成 装入您捆绑配置的文物并将其交给插件 (单位:千美元)@animus-ui/vite-plugin 接受 appearanceBootstrap; 在 Next.js 中,应用程序的位置 code 本身, 这样它就可以控制 CSP 不ce和订购.

融合合同

出版自定义别名. 从已建成的系统中增加两个登记册:

import type { ConditionsOf, SelectorsOf } from '@animus-ui/system';

declare module '@animus-ui/system' {
  interface Selectors extends Record<SelectorsOf<typeof ds>, true> {}
  interface Conditions extends Record<ConditionsOf<typeof ds>, true> {}
}

虽然两个接口都是空的, _- 前置的块键保持允许. 任何一种增量都会使整个 _ 命名空间验证, 所以另一个 登记处的别名被拒绝,直到其扩大。 已注册的选择器 别名也作为组件调用道具类型(<Box _hoverChild={{ p: 8 }} />(三); 条件化名只是样式-块键.

关键框架. 框架接受 CSS 属性名称( mamlCase,转换为 排放时的kebab-case),原始CSS值,以及 {scale.key} 符号引用 作为 {shadows.glow-text},主题解析器可替换。 赤度键 没有牙套是逐字发射的,所以总是写出被划定的表格.

className 点菜. 在正常的路由 消费者提供 className 在生成的类后合并, 道具转发跳过 className 因为它的合并拥有它。 就是这样 className="group" 因此,祖先模式,如 .group:hover & 工作时.

出口

路径里面有什么东西
@animus-ui/systemFull API — 构建器,主题,运行时间,类型
@animus-ui/system/groups预建道具组: space, (中文(简体) ), color, (中文(简体) ), typography, (中文(简体) ), layout, (中文(简体) ), flex, (中文(简体) ), grid, (中文(简体) ), border, (中文(简体) ), shadows, (中文(简体) ), background, (中文(简体) ), positioning, (中文(简体) ), transitions, (中文(简体) ), mode, (中文(简体) ), vars
@animus-ui/system/composecompose - 共用变种传播的插槽家庭
@animus-ui/system/compose-with-contextcomposeWithContext 共用道具的家庭通过反应环境旅行 requireRoot: true,其根外的成员投向发展
@animus-ui/system/runtimecreateComponent 和组成家庭的运行时间
@animus-ui/system/class-resolvercreateClassResolver ——无反应运行时间的类解析,面向非反应消费者
@animus-ui/system/bootstrapcreateAppearanceBootstrap ——仅构建时间,从未通过应用程序代码导入
@animus-ui/system/appearancepersistColorMode, (中文(简体) ), migrateLegacyModeKey, (中文(简体) ), SYSTEM_MODE ——仅存储外观记录写入路径(运行时间,客户端安全)

许可证

麻省理工学院

版本与发布记录

@animus-ui/system 共发布 112 个版本,首次发布于 2026/03/26;过去 12 个月发布了 112 个版本。最新版本附带 npm provenance 构建来源证明,可追溯到公开的 CI 构建记录。

版本发布日期距今
0.1.87latest2026/10/2414 天前
0.1.862026/10/2315 天前
0.1.852026/10/2315 天前
0.1.842026/10/2216 天前
0.1.832026/10/2217 天前
0.1.822026/10/2217 天前

其他发布通道:next → 0.1.1-beta.2。

关于 @animus-ui/system 的常见问题

@animus-ui/system 是什么?

Animus 设计系统构建器 — 符号, 道具组, 全球风格

如何安装 @animus-ui/system?

在项目目录运行 npm install @animus-ui/system,或使用 pnpm add / yarn add / bun add。

@animus-ui/system 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@animus-ui/system 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@animus-ui/system 需要什么版本的 Node.js?

package.json 的 engines 字段要求 Node.js >=22.12.0。版本不符时 npm 默认只给出警告,开启 engine-strict 后会拒绝安装。

@animus-ui/system 可以商用吗?

@animus-ui/system 使用 MIT 许可。宽松许可:允许商业使用、修改和再分发,通常只需在分发时保留原版权与许可声明。以上为概括,不构成法律意见,请以许可原文为准。

@animus-ui/system 的下载量有多少?

根据 npm 官方下载统计,@animus-ui/system 在 2026/10/06 – 2026/11/04 这 30 天内被下载 6,931 次,日均约 231 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@animus-ui/system 还在维护吗?

最新版本 0.1.87 发布于 2026/10/24(14 天前);至今共发布 112 个版本,其中过去 12 个月发布了 112 个;首次发布于 2026/03/26。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。