@tabula-css/preset
组装项目的 Tailwind v4 入口样式表:@import "tailwindcss" source(none) 加上 @theme 加上面向整个封闭词汇表的 @source inline()。
安装
@tabula-css/preset 是一个运行时依赖——是根 README 快速上手部分的一部分:
npm install @tabula-css/merge @tabula-css/preset概览
@tabula-css/preset 是一个纯库:每一个导出都是一个 ResolvedModel → string 函数,不涉及任何文件系统访问——把结果写入 .tabula/source.css 是 @tabula-css/cli 的职责,不是这个包的。它接收来自 @tabula-css/tokens 的已解析令牌模型,产出项目构建流程所导入的那一份单一样式表:关闭 Tailwind 的扫描(source(none))、令牌主题(委托给 @tabula-css/tokens 的 emitThemeCss())、每个已注册类对应一个 @utility 代码块(只使用逻辑长写属性,因此每个生成的属性都保持为一个规范槽位),以及枚举出确切的封闭词汇表的 @source inline(...) 声明——这正是使封闭性(T3,参见概念)得以成立的原因:一个未注册的类根本无法产生 CSS,因为 source(none) 已经告诉 Tailwind 不要扫描其他任何东西。
导出
buildPresetCss()
export function buildPresetCss(model: ResolvedModel): string;为一个已解析模型组装完整的 Tailwind v4 入口样式表——正是 tabula build 写入 .tabula/source.css 的那段确切字符串。按顺序依次是:@import "tailwindcss" source(none); 这一行(外加,在 base 配置档案层级下,一个 @theme 代码块,重置标准的 text-*/font-*/leading-*/tracking-* 比例尺,使得只存在该配置档案自身的局部排版工具类);内嵌的令牌主题;每个候选类对应一个 @utility 代码块;@source inline(...) 词汇表闭包;被追加在一个 @layer utilities 代码块中的已声明变体链规则(通过 buildChainLayer(),当模型没有声明任何链时会被省略);以及位于自己独立层(layer)中的散文隔离区排除 CSS。
import { writeFileSync } from 'node:fs';
import { buildProfile } from '@tabula-css/tokens';
import { buildPresetCss } from '@tabula-css/preset';
const { model } = buildProfile(tokensJson, configJson);
writeFileSync('.tabula/source.css', buildPresetCss(model));buildUtilities()
export function buildUtilities(model: ResolvedModel): string;只渲染 @utility <class> { … } 代码块——按模型的排名顺序,每个候选项一个——不包含周围的 import/theme/source-inline 脚手架。每一条声明都优先使用候选项的、经由变量间接引用的 emitted 形式,而非其字面量 value,因此生成的 CSS 保持可主题化。
buildSourceInline()
export function buildSourceInline(model: ResolvedModel): string[];通过压缩候选类列表(compressClasses())并渲染它(renderSourceInline()),渲染出覆盖整个词汇表的 @source inline(...) 各行。返回值是一个行数组,而非单一字符串,与 buildPresetCss() 将其拼接进更大文档的方式相匹配。
buildChainLayer()
export function buildChainLayer(model: ResolvedModel): string;把已声明的变体产物(v0.2 的变体封闭性修复)渲染为一个被追加的、单一的 @layer utilities { … } 代码块——model.chainCandidates 中每一条都对应一条字面量 CSS 规则,其声明体与该基础工具类的声明体逐字节相同(与 buildUtilities() 相同的渲染路径)。规则按全局的 (effectiveRank, class) 顺序发出,共享同一个媒体条件的相邻规则会被归并进同一个 @media 代码块,因此文档顺序精确地体现了合并的全序。当模型没有声明任何链时返回空字符串。链规则被刻意排除在 @source inline 之外——被列在那里的一条链会让 Tailwind 生成一个形如标准 (0,2,0) 的重复项,与该配置档案的 (0,1,0) 形态相矛盾;把它们发成字面量规则,才是通过构造本身来强制正确级联顺序的方式。
compressClasses() / expandSpec() / expandSpecs() / renderSourceInline()
export function compressClasses(classes: readonly string[]): string[];
export function expandSpec(spec: string): string[];
export function expandSpecs(specs: readonly string[]): string[];
export function renderSourceInline(specs: readonly string[], maxLineLength?: number): string[];这个包的封闭性保证所依赖的 @source inline 规格合成逻辑。compressClasses() 会将一个共享第一段的类列表(bg-surface、bg-surface-raised → bg-{surface,surface-raised})花括号折叠为一个更小的 Tailwind 花括号展开规格集合——经过排序,因此该规格列表是类集合的纯函数——同时将任何不适合折叠的内容(如 -m-md 这样带负号的类、单段名称)原样逐一枚举。expandSpec()/expandSpecs() 是精确的逆操作,专门导出以便在不需要真实 Tailwind 构建的情况下,测试往返性质 expandSpecs(compressClasses(v)) === v(作为一个集合)成立。renderSourceInline() 将一个规格列表包裹为 @source inline("…"); 各行,在 maxLineLength(默认 96)处换行,使一个庞大的词汇表在生成的文件中仍然保持可读。
import { compressClasses, renderSourceInline } from '@tabula-css/preset';
renderSourceInline(compressClasses(['bg-surface', 'bg-surface-raised', 'p-md']));
// → ['@source inline("bg-{surface,surface-raised} p-md");']参见
@tabula-css/tokens— 产出这个包所渲染的ResolvedModel。@tabula-css/registry— 从这份样式表所产出的同一次 Tailwind 构建中推导registry.json。@tabula-css/cli— 在tabula build中把buildPresetCss()的输出写入.tabula/source.css。- 概念 § 封闭性(T3) — 为什么
source(none)加上@source inline能使一个未注册的类不产生任何 CSS。