# @cssints/reactive 中文文档

规范地址：https://npmcn.com/package/%40cssints/reactive/

版本：0\.35\.1
npm 元数据更新时间：2026-11-07T14:55:38.673Z
中文译文更新时间：2026-11-11T04:06:34.930Z
软件包许可：MIT

本文由上游 README 翻译，可能存在偏差，请结合原文核对 API、命令与配置。版权与许可归原作者所有。

原始 npm 页面：https://www.npmjs.com/package/%40cssints/reactive
原文对照：https://npmcn.com/package/%40cssints/reactive/?lang=original
项目主页：https://cssints.dev/
源码仓库：https://github.com/alexeiaccio/css-in-ts-compiler

## 关键信息（来自 npm 元数据）

- 包体积（解压后）：52.4 kB，7 个文件
- 模块格式：ES Module
- TypeScript 类型：包内自带
- Node.js 要求：\>=22
- 运行依赖：0 个
- peer 依赖：cssints ^0\.35\.0
- 首次发布：2026-10-31T08:43:15.004Z
- 最新版本发布：2026-11-07T14:55:38.341Z
- 近 30 天下载量：3283（2026-10-11 至 2026-11-09，npm 官方下载统计）
- 已发布版本数：19（近 12 个月 19 个）

## 中文简介

Cssints 的 CSS- first 交互: 状态为自定义属性, 仅在事件、 条件和 Plain CSS 的 DOM 输出中写入, 由 ~1 kB 运行时连接

## 安装

```sh
npm install @cssints/reactive
```

也可以使用：`pnpm add @cssints/reactive`、`yarn add @cssints/reactive`、`bun add @cssints/reactive`

## README

# @ cssints/反应

CSS - 第一次互动 [点缀](https://npmcn.com/package/README.md): state 是一个所有者元素上的自定义属性集, 一个写入 仅发生于一事件,一个条件或一块DOM输出是平坦 CSS. 一个小剧本代表了活动 写入属性;浏览器中没有其他操作.

这个模型是 [反应CSS](https://reactivecss.com/):在样式中声明为自定义属性,事件和条件. 内容 严格来自 [Solid 2.0的规则 - 游戏](https://github.com/solidjs/solid/blob/next/packages/signals/docs/RULES-FUNDAMENTALS.md) 财务报告和审定财务报表 [反应脚本](https://github.com/ryansolid/reactivescript):来源是唯一可写的东西,写在 绝对范围(一个事件),状态属于所有者元素,一个冲出涂上一个整帧.

反应 CSS 写入 `:on(click)` 在选择器中。 这不是有效的CSS, 而csints的船编译了类串,所以 事件程序作为数据在非继承自定义属性中行走,被授权的收听者读取.

```ts
import { cn, display, p } from "cssints" with { type: "cssints" };
import {
  bind,
  either,
  equals,
  inside,
  on,
  set,
  state,
  toggle,
  when,
} from "@cssints/reactive" with { type: "cssints" };

const ui = state({ open: false, tab: "one" }); // { owner, open, tab }
const card = cn(ui.owner, p(4)); // the owner element
const button = cn(
  on({ click: toggle(ui.open) }), // a write, only in an event
  bind({ "aria-expanded": ui.open }), // DOM output
);
const tab = cn(
  bind({
    "aria-selected": equals(ui.tab, "one"), // a comparison: "true" or "false"
    "aria-controls": "panel-one", // a literal, written as is
    tabindex: either(equals(ui.tab, "one"), "0", "-1"), // one of two literals
  }),
);
const panel = inside(ui.open)(display("block")); // a descendant of the owner
const tabPanel = inside(ui.tab, "two")(display("block"));
const own = when(ui.open)(display("block")); // the element itself
```

## 模型

-   **一个来源是唯一可以写的东西.** `state()` 唯一的来源; 来源是 CSS本身 (单位:千美元)`calc(var(--reactive-count) * 2)`,一个条件). 没有 JavaScript 的衍生词可以写入,所以错误是 无相相相.
-   **写作只在某种情况下发生.** `toggle()` 财务报告和审定财务报表 `set()` 返回不透明 `Action`; 仅 `on()` 一个,所以写在 模块级别无法写入(类型错误).
-   **国家属于所有者要素.** `state()` 给一个主人的风格和每个状态一个来源。 一个事件写道: 该元素的最近拥有者 它的程序在(`closest`就像Solid的主人树.
-   **一个事件,一个冲浪.** 运行时间应用事件的每一动作,然后改写每个所有者的每个约束 触动。 CSS涂装一次;没有中间状态可见.
-   **V1中无同音.** 没有取取取, 所以没有装入或出错的界限; Solid 2 的合成基础是参考 为后一版本.
-   **一手执相.** `emit()` 让一个事件离开 CSS 世界用于 JavaScript(`listen()` 在应用中,它没有写作状态, 没有东西会回来的, 州仍然没有从JS读取.

协议的昆特模型 [`packages/apg-specs/specs/patterns/reactive.qnt`](https://npmcn.com/package/%40cssints/apg-specs/specs/patterns/reactive.qnt) (豆色克辛-tm9j):两个 拥有者,一个巢穴,每个巢穴有四种捆绑, 最近的拥有者读取,一个冲出 作为无变化者. `test/scenarios.mjs` 重放其情景 和两个种子的差分行走 `test/fixtures/model.js` 在Chrome、Firefox和WebKit中,并检查 每个步骤之后的一页与模型相对,包括一个事件画了一次(一页在一个框前后读取) 不得改变.

## API 密码

| 成员 | 它会做什么 |
| --- | --- |
| `state(states)` | 车主: `{ owner, ...sources }`名称为 idents; 一次两次通话 `cn()` 是一个主人. |
| `toggle(source)` | 翻转布尔源的动作 。 仅 `on()` 拿着. |
| `set(source, value)` | 将源设定为值或字段的动作(`value()`, (中文(简体) ), `checked()`) (中文(简体) ). 仅 `on()` 拿着. |
| `value()` | 活动所发射的字段的价值(`target.value`,一个字符串). 仅 `set()` 拿着. |
| `checked()` | 是否检查字段( E)`target.checked`) (中文(简体) ). 仅 `set()` 拿着. |
| `persist(value, key)` | 重载后保存在 `"reactive:" + key`。仅限 `state()` 拿着. |
| `equals(source, value)` | 比较: `"true"` 在一个 `bind()` 属性,而状态有该值, `"false"` 否则. |
| `either(comparison, a, b)` | 取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取出取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取取出. 仅 `bind()` 拿着. |
| `on({ event: action | action[] })` | 该元素的程序:点击,输入,更改,提交,按键,按键,指向,指向,聚焦,聚焦. |
| `emit(name)` | 交接:发送 `reactive:<name>` 以便应用程序听到。 仅 `on()` 拿走它,它没有写状态. |
| `bind({ attribute: binding })` | 将每个属性写在它所在的元素上:一个源,一个比较,一个选择,或者一个字. |
| `when(source, value?)` | 元素本身的条件(`data-reactive-<name>`, (中文(简体) ), `"true"` 当数值被忽略时. |
| `inside(source, value?)` | 房主后裔的条件(`within()` 超过 `data-reactive-<name>`) (中文(简体) ). |

两通电话 `on()`, (中文(简体) ), `bind()` 或者说 `state()` 在一个 `cn()` 附加而不是替换: 元素的程序是 `click toggle open,keydown set tab two`,其属性 `aria-expanded open,data-current tab`及其所有者的国家 `open,tab`A级 `<select>` 驱动字符串状态 `set(ui.tab, value())`,一个复选框 一个布尔的,有一个 `set(ui.open, checked())`。 。 。 .

绑定是四件事之一: 源值(`bind({ "aria-expanded": ui.open })`),比较 (单位:千美元)`equals(ui.tab, "one")`, (中文(简体) ), `"true"` 或者说 `"false"`),可选取两字`either(equals(ui.tab, "one"), "0", "-1")`, (中文(简体) ), 狂欢 `tabindex`),或写作正文`"aria-controls": "panel-one"`) (中文(简体) ). 比较就是一种表达方式 运行时间评估, 以及它可能的唯一地点: 来源仍然是唯一可写作的东西, a `Comparison` 或一个 `Choice` 无法是 `set()`, (中文(简体) ), `toggle()`ed, 用于 `when()`编号 :`inside()` 或存储于 `persist()`这是一个方程式,不是 状态 —— 它没有自己的价值, 所以运行时间读取它 约束 和州的价值 离最近的所有人 在同一个冲浪上。 问题本身就是为什么: `attr()` 是一个条件,读取一个属性, 它不计算一个属性 `aria-selected` 财务报告和审定财务报表 `tabindex` 坐在元素上, CSS 无法写入. `aria-selected="one"` 从字符串状态会 无效的 HTML, 因此比较就是使一个字符串状态足够制表符 。 字面内容在建筑中检查 时间( 无逗号, 无空格, 也没有线索) `=`, (中文(简体) ), `?` 或者说 `!`,协议的标记),其属性和值在 呼叫站点出错 .

一个值可能是 `persist(value, key)`数字 : `state({ open: persist(false, "demo-open") })` 保持状态 `localStorage` 下级 `"reactive:" + key`中,当运行时间 通知所有者时,重新加载会恢复它——模块运行于 文档已解析,在第一次涂料之前,页面可以显示,但与初始值相同的警告(条件是 它可能画出一个框架后期, 浏览器在脚本运行前画。 存储值胜过构建值 声明 每一个州文库都存储它,所以点击不需要额外的呼叫. 两个国家可故意共享一个密钥: 存储槽,由他们的写作保持同步. 应用程序的密钥前缀其名( P)`demo-open`没有 `open`),所以两个 一个来源的申请不满足.

**对JavaScript的一次交接是 `emit()`。 。 。 .** 一种形式最终必须到达应用程序,状态从不离开CSS 事件确实如此. `emit()` 是一种动作 `on()` 喜欢 `toggle()` 财务报告和审定财务报表 `set()`,但它没有写: 运行时间调度 `reactive:<name>` 从原生事件射出 `listen()` 听见了吗

```ts
// the style
const form = cn(ui.owner, on({ submit: emit("save") }));
```

```ts
// the app: listen from the runtime module, not the build-time one
import { listen } from "@cssints/reactive/runtime";

listen(document.querySelector("form")!, "save", ({ detail }) => {
  detail.event.preventDefault(); // detail.event is the native submit
  save(detail.owner); // the nearest owner of the element the event fired on
});
```

`listen()` 从 `@cssints/reactive/runtime` 将引擎船装入浏览器的模块; `@cssints/reactive` 它本身只是建设时间。 它返回一个处置者。 名称是相同的字符串 `emit()` 和一个凹痕(小写) 字母、数字和 `-`与呼叫站点一起在建设时检查。 活动会喷出气泡 所以听众可以坐在表格上, 页面上,或元素上方的任何地方; `detail` 实值 `{ event, owner }`,本地事件和最近的被动所有者 (或 `null` 在任何业主之外). A 类 `reactive:` 事件不是一个运行时间 听,所以 程序无法回答, 并且没有重复 .

以上例子的 CSS( 类名为散列) :

```css
@layer _.k, _.a, _.b, _.c, _.d, _.e;
@property --reactive-owner {
  syntax: "*";
  inherits: false;
}
@property --reactive-on {
  syntax: "*";
  inherits: false;
}
@property --reactive-binding {
  syntax: "*";
  inherits: false;
}
@layer _.k {
  ._reactive-events {
  }
  ._reactive-binding {
  }
  ._reactive-owner {
  }
}
@layer _.a {
  ._1un0p5l {
    --reactive-owner: open, tab;
  }
  ._1gy1wuu {
    --reactive-open: false;
  }
  ._3z64sy {
    --reactive-tab: one;
  }
  ._1u5v0k0 {
    --reactive-on: click toggle open;
  }
  ._2qzz6u {
    --reactive-binding: aria-expanded open;
  }
  ._4h1t7s {
    --reactive-binding: aria-selected ?tab one, aria-controls =panel-one, tabindex ?tab one 0 -1;
  }
  ._lwugp7 {
    padding: 1rem;
  }
}
@layer _.b {
  :where(._g-reactive[data-reactive-tab="two"]) ._1x9gkmf {
    display: block;
  }
  :where(._g-reactive[data-reactive-open="true"]) ._edztjc {
    display: block;
  }
  ._23ymfi[data-reactive-open="true"] {
    display: block;
  }
}
```

`--reactive-open` 财务报告和审定财务报表 `--reactive-tab` 继承,所以任何后代都可以在CSS中读取 (单位:千美元)`color: var(--reactive-tab)`); 协议清单(`--reactive-owner`, (中文(简体) ), `--reactive-on`, (中文(简体) ), `--reactive-binding`, (中文(简体) ), `--reactive-persist`)不继承,因此一个元素只看到自己的程序,绑定和存储.

## 议定书

什么建筑发出,什么 `./runtime` 读取( 对于调试):

| 自定义属性 | 打开 | 数值 |
| --- | --- | --- |
| `--reactive-owner` | `._reactive-owner` | 各州, `open,tab` |
| `--reactive-<name>` | 拥有者 | 状态值( n)`false`, (中文(简体) ), `one`, (中文(简体) ), `2`(中文(简体) ) |
| `--reactive-on` | `._reactive-events` | `click toggle open,change set tab @value,submit emit save` |
| `--reactive-binding` | `._reactive-binding` | `aria-expanded open,aria-controls =panel-one,tabindex ?tab one 0 -1` |
| `--reactive-persist` | `._reactive-persist` | 存储状态, `open:demo-open,tab:demo-tab` |

运行时间写 `data-reactive-<name>="<value>"` 拥有者——什么 `when()` 财务报告和审定财务报表 `inside()` 改为——和同值 作为内线 `--reactive-<name>`,CSS衍生的读取。 装入时列出每个所有者( E)`data-reactive-<name>` 从 或从 `localStorage` 并写出其中的律例 目标向上, 将它发现的每个程序应用到该元素最近的所有者, 然后更新每个所有者的绑定 一次 一次吗 稍后添加的节点也关闭(a) `MutationObserver`) (中文(简体) ). 捆绑对是四种类型之一:源 (单位:千美元)`aria-expanded open`),为一字`aria-controls =panel-one`),比较(`aria-selected ?tab one`, (中文(简体) ), `"true"` 当 主人的状态 `one`并择取两字`tabindex ?tab one 0 -1`, (中文(简体) ), `0` 虽然它做到了, `-1` 绝不然; 比较有其清单的三个符号,一个选择5。 无标记前缀选择的字元:自定义属性值 带有 `!` 不解析( 它读作破碎 ) `!important`),故后行的两个符所携带. 设定部分的值 是一个字面,或 `@value`编号 :`@checked`: 运行时间读取 `target.value` (a) 字符串)或 `target.checked` (单位:千美元)`"true"`, (中文(简体) ), `"false"`(中文(简体) ) 被击中的元素。 写成的相同部分两次应用一次,所以两个程序仍然是一个 切换。 A 类 `submit emit save` 片段没有写入状态: 它发送 `reactive:save` 从事件所发射的元素起, 弹出,与 `{ event, owner }` 其细节——一种交接方式,即 `listen()` 附属于且没有程序 答话 持续状态被存储在每封信上,其存储值在初始化时取代已声明状态. 数值仅通过 `setAttribute` 财务报告和审定财务报表 `style.setProperty`,从不作为 HTML 或 CSS 的字符串,所以存储 值不能注入任何东西.

## 限制

-   几个要求使用一个元素 `state()`s是一家的,两家 `on()`一个程序,两个 `bind()`一个 属性列表。 同一段两次被应用一次(运行时间取消),所以重复程序不会 切换两次.
-   字符串值没有逗号、空格和空格 `@` (协议是逗号分隔的列表,以及 `@value`编号 :`@checked` 为: 有效载荷); 名称为idents(小写字母、数字和 `-`) (中文(简体) ). 都在呼叫站点制造错误 持久关键 是字母,数字, `-`, (中文(简体) ), `_`, (中文(简体) ), `.` 财务报告和审定财务报表 `:` (逗号或空格将分割协议列表),至少一个字符,以及 呼叫站点的建设错误; `persist()` 无值是类型级别和构建时间的值.
-   事件设置是固定的:点击,输入,更改,提交,按键,按键,指向,指向,聚焦,聚焦.
-   写作必须能够从元素的程序中达到:事件泡沫到 `<document>`,所以一个关于祖先的程序运行 因为它的后人的事件, 但一个按钮在主人之外 没有主人写.
-   绑定的属性和条件读取它们所在元素的最近所有者,所以它们必须坐在所有者内部 亚树. 不过,一个条件就是CSS: `inside()` 实值 `:where(._g-reactive[data-reactive-<name>=<value>]) .el`,一个祖先 匹配,所以一个巢中所有者宣布 与外所有者相同的状态 显示其后代的条件 而外 车主也火柴 运行时间将属性写在最接近的拥有者身上, 但是 CSS 不能表达“ 最接近的 ” ; 给予拥有者 不同的状态名称( 模式在 `specs/patterns/reactive.qnt` 表示此和重播检查.
-   一个比较点是状态,而不是所有者: `equals(outer.tab, "one")` 在内部拥有者内部的元素上比较 内地主人 `tab`,因为运行时间读取最近的主人像源。 对比只是平等:否 命令和不算术. `equals(ui.count, 2)` 是一个比较,但 `count > 2` 运行时间需要一个派生表达式 无需延期或延期.
-   初始状态由脚本所写: 在运行前, `data-reactive-*` 没有,所以一个条件 一个初始 值可能会画出框架后期,一旦解析出文档,运行时间就会写出。 恢复的持久性价值有: 同样的时间.
-   状态是构建时间数据:客户端获得所有者的类字符串和源名,永远不能读取. A 类 组件不能读取 `ui.open` 在 JavaScript 中——这就是点,状态是 CSS;一个值 JavaScript 需要是 框架本身的状态.
-   `emit()` 是 JavaScript 的唯一出路: 它将事件交给应用程序(`listen()`从 `@cssints/reactive/runtime`),不是州,州仍然从未从JS读取. 没有回来,没有回报值, 没有确认, 也没有从 JavaScript 写入状态 .
-   没有计数器,手势,自动或 `event()` v1 中的调用: 状态是字符串、 数字或布尔, 由 a 编写 单击,键或窗体事件. `calc()` 超过一个状态值是 CSS, 不是源: 没有从 JavaScript 递增它 .
-   持久性是 `localStorage` 仅: 没有跨 tab 同步( 第二标签保存自己的副本直到重新装入), 没有版本 或过期(存储值是当前构建可能不再预期的字符串),存储失败——私有模式,a 配额,一个没有存储的页面——沉默:状态保持互动,只是无法活过重载.

## 浏览器

每个浏览器:运行时间是一个没有多填充的脚本,所以它将运至一个构建的每个目标(2.9 kB minfied, 1.4 kB) gzipped),并读取已注册的自定义属性 `@property` (单位:千美元)`syntax: "*"; inherits: false`; Chrome 85, 萨法里语 16.4,火狐128). 何处 `@property` 缺少列表继承,但标记类仍然保留一个元素到自己的 程序和存储。 持久性需求 `localStorage`;如果它缺失或拒绝写作,国家就起作用,而不是 幸存的重载.

## 测试

```bash
cd packages/reactive && bun run test            # the engine: class strings, the protocol, the @property rules, errors
cd packages/reactive && bun run test:browsers   # Chrome, Firefox, WebKit: two disclosures, an initial true state,
                                                # tabs, a computed tab strip (aria-selected, a literal aria-controls,
                                                # roving tabindex, aria-pressed), a nested owner, the flush of one click,
                                                # a <select> from value(), a checkbox from checked(), two programs on one
                                                # element, a repeated one, a persisted disclosure across a reload, a stored
                                                # value winning, a state that is not persisted, and a form submit heard by
                                                # listen() on the form and on the page (BROWSERS=chrome)
# the model's traces (needs the packages built: node reads their dist/):
bun run build:packages && cd packages/reactive && bun run test:scenarios
                                                # specs/patterns/reactive.qnt replayed on test/fixtures/model.js in
                                                # Chrome, Firefox and WebKit: the two scenario sets and two seeded
                                                # differential walks, every probed attribute and computed display
                                                # compared with the model after every step
```

## 常见问题

### @cssints/reactive 是什么？

Cssints 的 CSS- first 交互: 状态为自定义属性, 仅在事件、 条件和 Plain CSS 的 DOM 输出中写入, 由 ~1 kB 运行时连接

### 如何安装 @cssints/reactive？

在项目目录运行 npm install @cssints/reactive，或使用 pnpm add / yarn add / bun add。它还需要宿主项目提供 peer 依赖：cssints（^0\.35\.0）。npm 7 及以上与 pnpm 8 及以上默认会自动安装必需的 peer 依赖，Yarn 需要手动安装。

### @cssints/reactive 支持 TypeScript 吗？

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

### @cssints/reactive 支持 ESM 还是 CommonJS？

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

### @cssints/reactive 需要什么版本的 Node\.js？

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

### @cssints/reactive 可以商用吗？

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

### @cssints/reactive 的下载量有多少？

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

### @cssints/reactive 还在维护吗？

最新版本 0\.35\.1 发布于 2026/11/07（3 天前）；至今共发布 19 个版本，其中过去 12 个月发布了 19 个；首次发布于 2026/10/31。发布频率只是参考信号：功能稳定的工具可能很少需要更新，建议结合源码仓库的 issue 和提交记录判断。

