Skip to content

@tabula-css/core

Tabula 的“契约即代码”层:所有其他 Tabula 包都认可的冻结表、纯函数、错误码,以及 JSON Schema。零运行时依赖。

安装

大多数项目从不直接安装 @tabula-css/core——它是 @tabula-css/tokens@tabula-css/registry@tabula-css/merge@tabula-css/preset 的传递依赖,这些包都从中导入自己的不变量。只有当你在编写需要诊断目录、冻结表或 JSON Schema、但又不想引入整条流水线其余部分的自定义工具时,才需要直接安装它:

bash
npm install @tabula-css/core

概览

@tabula-css/core 是唯一定义该配置档案词汇表及其诊断信息的地方,这样 CLI、注册表生成器、linter 和 MCP 服务器就不会彼此脱节。它没有 I/O,也不依赖 Tailwind:它导出的一切要么是冻结的数据表(规范 CSS 槽位、命名空间 → 工具族映射、变体表、封禁表),要么是纯函数(rank()checkLaminarity()computeSpecificity()),要么是 JSON Schema(用于 tabula.config.json、令牌文件、registry.jsonmanifest.json),要么是稳定的 TAB-Exxx/TAB-Wxxx 错误目录。下游的一切——@tabula-css/tokens 的校验器、@tabula-css/registry 的生成器、@tabula-css/merge 的运行时——都读取这些表,而不是自行重新推导。

导出

诊断

系统中每一个 TAB-Exxx/TAB-Wxxx 代码都取自这个稳定的诊断目录——有关这些代码的上下文,参见概念 § 注册表即真相快速上手

ts
export type Severity = 'error' | 'warning';

export interface Diagnostic {
  readonly code: string;
  readonly severity: Severity;
  readonly message: string;
  readonly subject?: string;
  readonly hint?: string;
}

export type ErrorCode = keyof typeof ERROR_CATALOG; // e.g. 'TAB-E109' | 'TAB-E300' | …
export const ERROR_CATALOG: Readonly<Record<ErrorCode, { severity: Severity; summary: string; teach: string }>>;
export const ERROR_CODES: readonly ErrorCode[];

export function formatMessage(
  template: string,
  params?: Readonly<Record<string, string | number>>,
): string;

export function makeDiagnostic(
  code: ErrorCode,
  params?: Readonly<Record<string, string | number>>,
  opts?: { readonly subject?: string; readonly hint?: string },
): Diagnostic;

ERROR_CATALOG 将每个代码映射到其严重级别、一段 {name} 模板化的 summary,以及一行 teach 字符串(即 tabula explain <code> 打印的内容)。makeDiagnostic() 是系统中每个包用来构建 Diagnostic 的唯一构造函数:它查找该代码的模板,通过 formatMessage() 填入 params,并附加可选的 subject(该诊断所涉及的令牌路径或类名)和 hint(一步可执行的下一步操作)。

ts
import { makeDiagnostic } from '@tabula-css/core';

makeDiagnostic('TAB-E109', { path: 'color.surface' });
// → { code: 'TAB-E109', severity: 'error',
//     message: "Token 'color.surface' requires a $description ≥ 20 chars, not byte-equal to its value.", … }

该目录涵盖结构性令牌文件错误(TAB-E1xx)、值语义(TAB-E15xE16x)、变体封闭性(TAB-E170E172 为配置错误,TAB-E230 为扫描错误)、构建不变量(TAB-E21x)、CSS 治理(TAB-E22x)、弹出(eject)前置条件(TAB-E240/TAB-W402)、合并运行时(TAB-E3xx)、生成产物完整性(TAB-E601),以及 CLI 用法(TAB-E9xx)——关于 E22x 家族的详情,尤其参见 CSS 治理

冻结表

@tabula-css/core 为该配置档案的每一项不变量所依据的检查表都冻结了一份规范副本。按主题分组:

导出项类型用途
CANONICAL_SLOT_PROPERTIESreadonly string[]规范化的长写 CSS 属性有序列表;一个槽位的 id 就是它的下标。
CANONICAL_SLOTSReadonly<Record<string, string>>slotId → property,与 registry.jsonslots 映射一致。
SLOT_ALIASESReadonly<Record<string, string>>逻辑块级 → 物理块级的规范化映射(padding-block-start → padding-top)。
PHYSICAL_INLINE_TWINSReadonly<Record<string, string>>逻辑行内属性 → 其(未注册的)物理行内孪生属性,供“二者不可兼得”的单元测试使用。
slotIdOf(property)(string) => SlotId | undefined某个规范属性对应的槽位 id,若没有则为 undefined
SHORTHAND_EXPANSIONSReadonly<Record<string, readonly string[]>>CSS 简写属性 → 长写叶子属性的展开(padding → 其四个方向),仅展开一层。
INHERITED_PROPERTIES{ tier1, tier2 }两层的可继承属性表:第一层(要求闭包:仅 type-*/ink-*)与第二层(环境性,受限)。
INHERITABLE_PROPERTIESreadonly string[]第一层 ∪ 第二层,按稳定顺序排列——正是 base 层级的环境基线必须在 :root 一次性设置的确切集合。
AMBIENT_BASELINE_DEFAULTSReadonly<Record<string, string>>每个第二层属性冻结的字面量 :root 默认值。
TYPE_COMPOSITE_FIELDS8 个字符串组成的元组type 复合体的字段名(fontFamilyfontSize 等)。
TYPE_FIELD_TO_PROPERTYReadonly<Record<string, string>>将每个 type 字段映射到它所写入的 CSS 属性。
inheritanceTier(property)(string) => 0 | 1 | 2某个属性所属的继承层级,若不可继承则为 0
NAMESPACE_FAMILY_TABLEReadonly<Record<string, NamespaceEntry>>命名空间 → 其合成出的工具族,以命名空间为键(colorspacingsizeradius 等)。
ALL_FAMILIESreadonly FamilyDef[]所有命名空间下的所有工具族,按 familyIndex 顺序排列。
NAMESPACESreadonly string[]合法命名空间名称的集合。
isKnownNamespace(ns)(string) => boolean某个命名空间名称是否合法。
familyInLevel(family, level) / familiesForLevel(entry, level)profileLevelbasestrict)过滤某个工具族/命名空间的条目。
familyBreadth(family)(FamilyDef) => number非复合槽位数量加上写入的自定义属性数量——rank() 的主要排序键。
STATIC_UTILITIESReadonly<Record<string, StaticUtility>>该配置档案所允许的无值工具类(flexsr-onlycursor-pointer 等),以类名为键。
STATIC_UTILITY_NAMESreadonly string[]每个静态工具类的类名。
staticUtilityNamesForLevel(level)(ProfileLevel) => readonly string[]在给定配置档案层级下注册的静态工具类名称。
VARIANT_TABLEReadonly<Record<string, VariantDef>>每个命名变体(hovermdgroup-hover/* 等)及其选择器/条件与排名区间。
CONDITION_RANK_SCALEnumber1_000_000应用于条件排名总和的乘数,使条件形成外层区间。
BANLISTreadonly BanPattern[]与注册表无关的封禁下限(space-*divide-**:in-*rtl:/ltr: 等)。
matchBan(token)(string) => string | undefined将某个类令牌与封禁下限进行匹配;返回触犯的封禁 id。
betterTailwindcssBanlist()() => { restrict: … }eslint-plugin-better-tailwindcssno-restricted-classes 选项定型的封禁下限。
QUARANTINE_ATTRIBUTEQUARANTINE_KINDQUARANTINE_FENCE_CLASSQUARANTINE_SAFE_FAMILIESQUARANTINE_SELECTORQUARANTINE_CSS字符串 / readonly string[]散文隔离区(prose-quarantine)边界标记、围栏类、安全工具族,以及生成的排除 CSS——参见概念
isQuarantineSafe(className)(string) => boolean某个类在隔离区边界元素上是否合法。

纯函数

ts
export function expandShorthand(property: string): readonly string[];
export function canonicalizeProperty(property: string): string;
export function isComposedValue(value: string): boolean;

export interface ClassMeta {
  readonly breadth: number;
  readonly familyIndex: number;
  readonly valueIndex: number;
}
export function rank(meta: ClassMeta): number; // throws RankError out of range
export class RankError extends Error {}
export const MAX_BREADTH: number; // 32
export const VALUE_SPAN: number; // 100_000
export const FAMILY_SPAN: number; // 10_000

export interface SlotBearing {
  readonly class: string;
  readonly slots: Iterable<number>;
  readonly atomic?: boolean;
}
export interface LaminarityViolation {
  readonly a: string;
  readonly b: string;
  readonly shared: readonly number[];
  readonly onlyA: readonly number[];
  readonly onlyB: readonly number[];
}
export function checkLaminarity(classes: readonly SlotBearing[]): LaminarityViolation[];

export type Specificity = readonly [number, number, number];
export function computeSpecificity(selector: string): Specificity;
export function compareSpecificity(a: Specificity, b: Specificity): number;
export function formatSpecificity(s: Specificity): string; // "(0,1,0)"
export function hasPseudoElement(selector: string): boolean;

rank() 将一个类的 (breadth, familyIndex, valueIndex) 三元组归约为对整个词汇表施加全序的单一整数(草案 A §5.2:lex(−breadth, familyIndex, valueIndex))——参见概念 § 合并:全序,而非级联模拟。若任一分量超出其受支持的范围,它会抛出 RankError,因为静默溢出会破坏排序。

ts
import { rank } from '@tabula-css/core';

rank({ breadth: 4, familyIndex: 1, valueIndex: 0 }); // a p-* class: writes 4 slots

checkLaminarity() 对应不变量 I2(草案 A §5.3):对于共享同一条件的多个类,它们的槽位集合必须相互嵌套或互不相交——绝不能部分重叠。它返回每一对违规的类(返回空表示层状结构成立);原子重置工具类(sr-only)豁免,会被跳过。

computeSpecificity() 在不引入任何运行时依赖的情况下实现了 CSS Selectors Level 4 的特异性算法,能理解 :where()(始终为 (0,0,0))、:is()/:not()/:has()(取其最具特异性的参数的特异性),以及伪元素。它支撑着不变量 I4——每条工具类规则都必须归一化为 (0,1,0)(若含伪元素则为 (0,1,1))。

配置 Schema 与类型

tabula.config.json 的 Schema 及与之对应的 TypeScript 类型——参见快速上手 § 3. 声明你的轴

ts
export type ProfileLevel = 'base' | 'strict';
export const PROFILE_LEVELS: readonly ProfileLevel[];
export const DEFAULT_PROFILE_LEVEL: ProfileLevel; // 'base'

export interface AxisConfig {
  readonly values: readonly string[];
  readonly default: string;
  readonly attribute?: string;
  readonly media?: Readonly<Record<string, string>>;
}
export interface TabulaConfig {
  readonly profileId: string;
  readonly profileLevel?: ProfileLevel;
  readonly axes: Readonly<Record<string, AxisConfig>>;
  readonly baseline?: BaselineConfig;
  readonly groups?: readonly string[];
  readonly variantChainMaxLength?: number;
  readonly dynamicProperties?: Readonly<Record<string, DynamicPropertyConfig>>;
  readonly foreignClasses?: readonly string[];
  readonly textLeafComponents?: readonly string[];
  readonly gamut?: 'srgb' | 'p3' | 'rec2020';
  readonly budgets?: BudgetsConfig;
  readonly scan?: ScanConfig;
  readonly variants?: VariantsConfig; // declared variant products (contract J22)
}

export const configSchema: JsonSchemaObject; // JSON Schema (2020-12) for tabula.config.json

variants 小节声明了哪些变体链会产生 CSS——这正是 v0.2 中修复的那个封闭性缺陷:

ts
export type VariantPreset = 'interaction' | 'all-len1' | 'none';
export const VARIANT_PRESETS: readonly VariantPreset[];

export interface VariantsConfig {
  /** Seeds the product map before `products` are merged over it. Absent ⇒ `interaction`. */
  readonly preset?: VariantPreset;
  /** Canonical chain prefix → the families it may prefix (`"*"` = every non-atomic family). */
  readonly products?: Readonly<Record<string, '*' | readonly string[]>>;
  /** Hard cap on materialized chain candidates. Absent ⇒ DEFAULT_MAX_CHAIN_CANDIDATES. */
  readonly maxChainCandidates?: number;
}

一个已声明的变体产物会扩大已注册的集合(基础词汇表 ∪ 产物),使一个带变体前缀的 class 得以产生 CSS,同时生成 的样式表依然保持为配置档案的纯函数——参见概念 § 变体。结构形状由 configSchema 校验;值语义门禁(链的语法、族群名称、media→breakpoint 的要求 TAB-E170、非法产物检查 TAB-E171,以及 maxChainCandidates 预算 TAB-E172)位于 @tabula-css/tokensvalidateConfig 中。

DynamicPropertyConfig.syntax 绝不能是字面量 '*'——一个 --d-* 动态自定义属性(通过 dyn() 写入)如果使用通配的 @property 语法,就无法在系统的任何地方进行有效校验,因此这份 Schema 与 @tabula-css/tokensvalidateConfig 都会拒绝它(TAB-E127)。

随 Schema 一并导出的其他默认值与上限:DEFAULT_VARIANT_CHAIN_MAX_LENGTH(2,v0.2 中从 3 下调,以匹配生成器实际产出的链长度)、DEFAULT_VARIANT_PRESET'interaction')、DEFAULT_MAX_CHAIN_CANDIDATES(20,000——variants.maxChainCandidates 预算)、DEFAULT_GAMUT'srgb')、DEFAULT_MAX_ESCAPES(25)、DEFAULT_MAX_SUPPRESSIONS(0)、DEFAULT_SCAN_SOURCES/DEFAULT_SCAN_IGNOREtabula scan 的 glob 默认值),以及 MAX_AXIS_CROSS_PRODUCT(16)。

令牌、注册表与清单 Schema

ts
export const TOKEN_TYPES: readonly string[]; // 'color' | 'dimension' | 'number' | … | 'type' | 'shadow'
export const tokensSchema: JsonSchemaObject; // JSON Schema for a flat-DTCG token file
export interface Token {
  readonly $type: string;
  readonly $value: TokenValue;
  readonly $description: string;
  readonly $deprecated?: Deprecation;
  readonly $extensions?: TokenExtensions;
}
export type TokensFile = Readonly<Record<string, TokenNamespace>>;
export function isLiteralEscape(meta: ExceptionMeta): boolean;

export const registrySchema: JsonSchemaObject; // JSON Schema for registry.json (schema version 3)
export interface RegistryFile { /* registrySchemaVersion (3), classes, variants, exceptions, variantProducts, chainCount, chainExceptions, … */ }
export interface RegistryClass { /* family, slots, rank, declarations, … */ }

export const manifestSchema: JsonSchemaObject; // JSON Schema for manifest.json
export const MANIFEST_FORMAT_VERSION: number; // 1
export interface ManifestFile { /* formatVersion, sourceHash, inputsHash, artifacts, … */ }

tokensSchema 覆盖令牌文件契约中的结构性部分(深度为 2 的嵌套、封闭的 $type 枚举、必需的 $description、短横线命名的令牌名);JSON Schema 无法表达的值语义(轴的完备性、对比度、维度值域)则是 @tabula-css/tokens 中的自定义校验步骤。registrySchemamanifestSchema@tabula-css/registry 的读取器用来在信任其内容之前校验 registry.jsonmanifest.json。注册表 schema 目前是版本 3(v0.2):它新增了已声明变体产物的字段 variantProducts(链前缀 → 排序后的族群列表)与 chainCount,外加可选的 chainExceptions(精确的单类链授权)。一份写入时使用更早 schema 版本的注册表会被读取器以 TAB-E302 拒绝。

每一份 JSON Schema 都通过 core 同时导出的极简 JsonSchema/JsonSchemaObject 类型来定型(一个足以表达这四份 Schema 的 JSON Schema draft 2020-12 子集,其索引签名类型为 unknown 而非 any)。

参见

  • @tabula-css/tokens — 消费 core 的表与 Schema 的校验器与解析器。
  • @tabula-css/registry — 依据 core 的 Schema 校验 registry.json/manifest.json 的读取器。
  • @tabula-css/merge — 抛出 core 的 TAB-E3xx 诊断的运行时。
  • 概念 — 这些表所编码的局部性、注册表、合并算法,以及禁用机制目录。

Released under the MIT License.