@tabula-css/react
可选的 Tabula “铺好的路径”原语:<Text>、<Separator>、<Prose>。基于注册表的合并;导入图中不含 Tailwind。
安装
npm install @tabula-css/react运行时依赖——与那些开发侧工具包不同,@tabula-css/react 发布的是你的应用会渲染的组件。它只依赖 @tabula-css/merge(react 是一个对等依赖,^18.0.0 || ^19.0.0),因此——与运行时的其余部分一样——Tailwind 永远不会出现在它的导入图中。
概览
这三个组件都是可选的:这个配置档案中没有任何东西要求使用 React,每一个组件都只是对你原本可以手写的标记的一种便利封装。每一个组件之所以存在,都是因为该配置档案禁止了某种机制,而它欠这种机制一个替代方案——<Separator> 用子元素之间一个真实的元素替代了被禁止的 divide-* 族,<Text> 是封闭的 type-*/ink-* 文本叶子节点,用来取代未注册的局部排版工具类(text-sm、font-medium),而 <Prose> 则是该配置档案明确拒绝管理的、未经编写的 HTML(渲染后的 markdown、CMS 输出)的隔离区边界。每一个原语都会转发其 ref,并且总是最后才通过 cn() 合并调用方的 className,因此调用方的覆盖值总能确定性地胜出。
Text
渲染一个单一的文本叶子节点,恰好携带一个 type-* 组合与一个 ink-* 颜色——这是唯一被允许写入可继承排版属性的类。as 刻意是一份封闭的文本叶子标签列表:允许任意容器会重新引入这个组件本应消除的“祖先继承”问题。
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
as | 'p' | 'span' | 'label' | 'strong' | 'em' | 'small' | 'h1'..'h6' | 'legend' | 'figcaption' | 'p' | 渲染出的标签。 |
variant | 'body' | 'label' | 'small' | 'heading' | 'body' | 映射到 type-body / type-label / type-small / type-heading。 |
tone | 'text' | 'muted' | 'accent' | 'danger' | 'on-accent' | 'text' | 映射到 ink-text / ink-text-muted / ink-accent / ink-danger / ink-on-accent。 |
htmlFor | string | — | 当 as="label" 时转发给 for。 |
className | ClassName | — | 直通类,最后通过 cn() 合并。 |
外加每一个 React.HTMLAttributes<HTMLElement> 属性。
要求你的项目中存在这些令牌:type.body、type.label、type.small、type.heading、color.text、color.text-muted、color.accent、color.danger、color.on-accent。
import { Text } from '@tabula-css/react';
<Text as="h2" variant="heading" tone="text">Section title</Text>
<Text variant="small" tone="muted">Last updated 3 days ago</Text>Separator
在子元素之间渲染一个真实的元素,取代 divide-*(后者从父元素给兄弟元素设置样式,是一种被禁止的非局部机制)。这样一来,每个子元素自身的间距就归属于该子元素自己的标记之中。
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
orientation | 'horizontal' | 'vertical' | 'horizontal' | 映射到 bg-border w-full h-hairline / bg-border w-hairline h-full;同时设置 aria-orientation。 |
decorative | boolean | false | 渲染 role="none",不带 aria-orientation,用于一条纯视觉性的分隔线,其分组语义已在别处传达。 |
className | ClassName | — | 直通类,最后通过 cn() 合并。 |
外加每一个 React.HTMLAttributes<HTMLDivElement> 属性。
需要这些令牌:color.border、size.hairline(1px)、size.full(100%)。
import { Separator } from '@tabula-css/react';
<Separator />
<Separator orientation="vertical" decorative />Prose
标记出该配置档案不予管理的一个子树——一次诚实的“不作声明”,而不是一种样式。在这个边界内部,局部性被明确暂停:resolve_element(参见 @tabula-css/mcp)会对其内部的任何内容回答 { managed: false, reason: "prose-quarantine" }。每一个元素子节点都会被自动加上注册过的 not-prose 标记工具类进行围栏隔离,使生成的重置样式能够通过其唯一受认可的祖先组合选择器排除这个子树;一个组件子节点必须将 className 转发给它的根节点,这道围栏才能生效。
| 属性 | 类型 | 默认值 | 描述 |
|---|---|---|---|
html | string | — | 要在该边界内渲染的未经编写的 HTML(markdown/CMS 场景)。与 children 互斥。 |
children | ReactNode | — | 手写的子元素;每一个元素子节点都会被自动围栏。与 html 互斥。 |
as | 'article' | 'div' | 'section' | 'aside' | 'div' | 渲染出的标签。 |
className | ClassName | — | 仅限布局/盒模型工具类(w-、max-w-、m*、p*)——最后合并。在开发环境中,任何其他工具类都会抛出错误,因为一个边界不应设置任何会继承进它明确拒绝管理的内容里的属性。 |
import { Prose } from '@tabula-css/react';
<Prose html={renderedMarkdownHtml} className="max-w-prose" />
<Prose>
<p>Authored paragraph, auto-fenced with <code>not-prose</code>.</p>
</Prose>同时导出:useInQuarantine()——一个返回调用组件是否被渲染在某个 <Prose> 边界内部的 hook——以及该边界所设置的 QUARANTINE_ATTRIBUTE/QUARANTINE_KIND 常量,这些常量从 @tabula-css/core 重新导出,使得该组件与任何工具读取的都是同一套值。
在开发环境中,<Prose> 宁可抛出错误,也不会静默地渲染出错误的标记:当它嵌套在另一个 <Prose> 边界内部时(一个隔离区不能被重新嵌套)、当 html 与 children 同时被传入时,或当 className 携带了盒模型/布局白名单之外的任何内容时。
参见
@tabula-css/merge— 这些组件所构建于其上的cn()/ClassName。- 概念 — 禁用机制 — 为什么
divide-*与局部排版工具类会被禁止。 - 智能体接口 —
resolve_element的managed: false隔离区报告。