@tabula-css/core
Tabula의 코드형 계약(contract-as-code) 계층: 다른 모든 Tabula 패키지가 합의하는 고정 테이블, 순수 함수, 오류 코드, JSON 스키마. 런타임 의존성 없음.
설치
대부분의 프로젝트는 @tabula-css/core를 직접 설치하지 않습니다 — 이는 @tabula-css/tokens, @tabula-css/registry, @tabula-css/merge, @tabula-css/preset의 전이 의존성(transitive dependency)이며, 이들 모두 여기서 불변식(invariant)을 가져옵니다. 나머지 파이프라인을 끌어들이지 않고 진단 카탈로그, 고정 테이블, JSON 스키마만 필요한 커스텀 도구를 작성하는 경우에만 직접 설치하십시오.
npm install @tabula-css/core개요
@tabula-css/core는 프로파일의 어휘와 그 진단이 정의되는 단 하나의 장소이며, 이 덕분에 CLI, 레지스트리 생성기, 린터, MCP 서버가 서로 어긋나지 않습니다. I/O도 Tailwind 의존성도 없습니다: 이 패키지가 내보내는 모든 것은 고정 데이터 테이블(정규 CSS 슬롯, 네임스페이스 → 유틸리티 패밀리 매핑, 변형 테이블, 금지 목록), 순수 함수(rank(), checkLaminarity(), computeSpecificity()), JSON 스키마(tabula.config.json, 토큰 파일, registry.json, manifest.json용), 또는 안정된 TAB-Exxx/TAB-Wxxx 오류 카탈로그 중 하나입니다. 하위의 모든 것 — @tabula-css/tokens의 검증기, @tabula-css/registry의 생성기, @tabula-css/merge의 런타임 — 은 이 테이블들을 다시 유도하는 대신 그대로 읽습니다.
내보내기
진단
시스템 안의 모든 TAB-Exxx/TAB-Wxxx 코드가 끌어오는 안정된 진단 카탈로그입니다 — 코드가 실제 맥락에서 쓰이는 예시는 핵심 개념 § 레지스트리는 근거 있는 사실이다와 시작하기를 참고하십시오.
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는 모든 코드를 그 심각도(severity), {name} 템플릿이 적용된 summary, 그리고 (tabula explain <code>가 출력하는) 한 줄짜리 teach 문자열에 매핑합니다. makeDiagnostic()은 시스템의 모든 패키지가 Diagnostic을 만들 때 사용하는 단일 생성자입니다: 코드의 템플릿을 조회하고, formatMessage()를 통해 params를 채운 뒤, 선택적인 subject(진단이 다루는 토큰 경로 또는 클래스 이름)와 hint(실행 가능한 다음 단계 하나)를 붙입니다.
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-E15x–E16x), 변형 폐쇄성(TAB-E170–E172 설정, 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 copy)을 고정(freeze)합니다. 주제별로 묶으면 다음과 같습니다.
| 내보내기 | 타입 | 목적 |
|---|---|---|
CANONICAL_SLOT_PROPERTIES | readonly string[] | 정규 롱핸드(longhand) CSS 속성의 순서 목록; 슬롯의 id는 그 인덱스입니다. |
CANONICAL_SLOTS | Readonly<Record<string, string>> | slotId → property, registry.json의 slots 맵과 일치합니다. |
SLOT_ALIASES | Readonly<Record<string, string>> | 블록-논리(block-logical) → 물리-블록(physical-block) 정규화(padding-block-start → padding-top). |
PHYSICAL_INLINE_TWINS | Readonly<Record<string, string>> | 논리-인라인 속성 → (등록되지 않은) 그 물리-인라인 짝, "둘 다는 안 됨" 단위 테스트용. |
slotIdOf(property) | (string) => SlotId | undefined | 정규 속성에 대한 슬롯 id, 없으면 undefined. |
SHORTHAND_EXPANSIONS | Readonly<Record<string, readonly string[]>> | CSS 숏핸드(shorthand) → 롱핸드-리프(leaf) 전개(padding → 네 방향), 한 단계만. |
INHERITED_PROPERTIES | { tier1, tier2 } | 2단계 상속 속성 테이블: Tier 1(폐쇄성 요구 — type-*/ink-*만) 대 Tier 2(주변적, 유계). |
INHERITABLE_PROPERTIES | readonly string[] | Tier 1 ∪ Tier 2, 안정된 순서로 — base 레벨의 주변 기준선(ambient baseline)이 :root에서 한 번 설정해야 하는 정확한 집합. |
AMBIENT_BASELINE_DEFAULTS | Readonly<Record<string, string>> | 모든 Tier-2 속성에 대한 고정된 리터럴 :root 기본값. |
TYPE_COMPOSITE_FIELDS | 8개 문자열 튜플 | type 합성 값의 필드 이름들(fontFamily, fontSize 등). |
TYPE_FIELD_TO_PROPERTY | Readonly<Record<string, string>> | 각 type 필드를 그것이 기록하는 CSS 속성에 매핑합니다. |
inheritanceTier(property) | (string) => 0 | 1 | 2 | 속성의 상속 계층, 상속하지 않으면 0. |
NAMESPACE_FAMILY_TABLE | Readonly<Record<string, NamespaceEntry>> | 네임스페이스 → 그것이 합성하는 유틸리티 패밀리, 네임스페이스(color, spacing, size, radius 등)로 키가 지정됩니다. |
ALL_FAMILIES | readonly FamilyDef[] | familyIndex 순서로, 모든 네임스페이스에 걸친 모든 패밀리. |
NAMESPACES | readonly string[] | 합법적인 네임스페이스 이름의 집합. |
isKnownNamespace(ns) | (string) => boolean | 네임스페이스 이름이 합법적인지 여부. |
familyInLevel(family, level) / familiesForLevel(entry, level) | — | profileLevel(base 대 strict)로 패밀리/네임스페이스의 항목을 필터링합니다. |
familyBreadth(family) | (FamilyDef) => number | 비합성 슬롯 개수 + 기록된 커스텀 속성 개수 — rank()의 주요 키. |
STATIC_UTILITIES | Readonly<Record<string, StaticUtility>> | 프로파일이 허용하는 값 없는 유틸리티(flex, sr-only, cursor-pointer 등), 클래스 이름으로 키가 지정됩니다. |
STATIC_UTILITY_NAMES | readonly string[] | 모든 정적 유틸리티의 클래스 이름. |
staticUtilityNamesForLevel(level) | (ProfileLevel) => readonly string[] | 특정 프로파일 레벨 아래 등록된 정적 유틸리티 이름. |
VARIANT_TABLE | Readonly<Record<string, VariantDef>> | 이름 있는 모든 변형(hover, md, group-hover/* 등)과 그 선택자/조건, 순위 대역(rank band). |
CONDITION_RANK_SCALE | number (1_000_000) | 조건이 외부 대역을 형성하도록 합산된 조건 순위에 곱해지는 승수. |
BANLIST | readonly BanPattern[] | 레지스트리와 무관한 금지 하한선(space-*, divide-*, *:, in-*, rtl:/ltr: 등). |
matchBan(token) | (string) => string | undefined | 클래스 토큰을 금지 하한선에 대조하고, 위반된 금지 id를 반환합니다. |
betterTailwindcssBanlist() | () => { restrict: … } | eslint-plugin-better-tailwindcss의 no-restricted-classes 옵션에 맞게 정형화된 금지 하한선. |
QUARANTINE_ATTRIBUTE, QUARANTINE_KIND, QUARANTINE_FENCE_CLASS, QUARANTINE_SAFE_FAMILIES, QUARANTINE_SELECTOR, QUARANTINE_CSS | strings / readonly string[] | 프로즈 격리(prose-quarantine) 경계 마커, 펜스 클래스, 안전한 유틸리티 패밀리, 생성된 제외 CSS — 핵심 개념을 참고하십시오. |
isQuarantineSafe(className) | (string) => boolean | 격리 경계 요소에서 클래스가 합법적인지 여부. |
순수 함수
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) 트리플을 어휘 전체를 완전히 정렬하는 단일 정수로 축약합니다(Draft A §5.2: lex(−breadth, familyIndex, valueIndex)) — 핵심 개념 § 병합: 캐스케이드 시뮬레이션이 아닌 전순서를 참고하십시오. 어느 성분이든 지원 범위를 벗어나면 RankError를 던지는데, 조용한 오버플로가 순서를 망가뜨릴 것이기 때문입니다.
import { rank } from '@tabula-css/core';
rank({ breadth: 4, familyIndex: 1, valueIndex: 0 }); // a p-* class: writes 4 slotscheckLaminarity()는 불변식 I2(Draft A §5.3)입니다: 조건을 공유하는 클래스들의 슬롯 집합은 서로 내포되거나(nest) 서로소(disjoint)여야 하며, 부분적으로 겹쳐서는 안 됩니다. 위반하는 모든 쌍을 반환하며(비어 있으면 라미나(laminar) 조건 충족), 원자적 리셋 유틸리티(sr-only)는 예외로 건너뜁니다.
computeSpecificity()는 런타임 의존성 없이 CSS Selectors Level 4의 명시도(specificity)를 구현하며, :where()(항상 (0,0,0)), :is()/:not()/:has()(가장 명시도가 높은 인자의 명시도), 의사 요소(pseudo-element)를 이해합니다. 이는 불변식 I4를 뒷받침합니다 — 모든 유틸리티 규칙은 (0,1,0)(의사 요소의 경우 (0,1,1))으로 정규화되어야 합니다.
설정 스키마와 타입
tabula.config.json 스키마와 그에 대응하는 TypeScript 타입입니다 — 시작하기 § 3. 축을 선언하기를 참고하십시오.
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.jsonvariants 섹션은 어떤 변형 체인이 CSS를 내는지 선언합니다 — v0.2에서 나온 폐쇄성 수정입니다:
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;
}선언된 변형 프로덕트는 등록된 집합(기본 어휘 ∪ 프로덕트)을 넓혀서, 방출되는 스타일시트가 여전히 프로파일의 순수 함수로 남는 채로 변형이 접두된 클래스가 CSS를 내게 합니다 — 핵심 개념 § 변형을 참고하십시오. 구조적 형태는 configSchema가 검사합니다; 값-의미론 게이트(체인 문법, 패밀리 이름, 미디어→브레이크포인트 요구 사항 TAB-E170, 잘못된 프로덕트 검사 TAB-E171, 그리고 maxChainCandidates 예산 TAB-E172)는 @tabula-css/tokens의 validateConfig에 있습니다.
DynamicPropertyConfig.syntax는 결코 리터럴 '*'가 될 수 없습니다 — 보편적인 @property 구문을 가진 --d-* 동적 커스텀 속성(dyn()을 통해 기록됨)은 시스템 어디에서도 아무것도 검증하지 못하게 되므로, 이 스키마와 @tabula-css/tokens의 validateConfig 모두 이를 거부합니다(TAB-E127).
스키마와 함께 내보내지는 그 밖의 기본값과 상한: 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_IGNORE(tabula scan의 글롭 기본값), MAX_AXIS_CROSS_PRODUCT(16).
토큰, 레지스트리, 매니페스트 스키마
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 스키마가 표현할 수 없는 값 의미론(축의 완전성, 대비, 차원 도메인)은 @tabula-css/tokens의 커스텀 검사 단계입니다. registrySchema와 manifestSchema는 @tabula-css/registry의 리더가 registry.json과 manifest.json의 내용을 신뢰하기 전에 검증하는 데 사용됩니다. 레지스트리 스키마는 버전 3입니다(v0.2): 선언된-변형-프로덕트 필드인 variantProducts(체인 접두사 → 정렬된 패밀리 목록)와 chainCount, 그리고 선택적인 chainExceptions(정확한 단일 클래스 체인 허가)를 추가합니다. 더 이전 스키마 버전으로 작성된 레지스트리는 리더에 의해 TAB-E302로 거부됩니다.
모든 JSON 스키마는 core가 함께 내보내는 최소한의 JsonSchema/JsonSchemaObject 형태로 타입이 지정됩니다(이 네 스키마를 표현하기에 충분한 JSON Schema draft 2020-12의 부분집합이며, any 대신 unknown 타입의 인덱스 시그니처를 사용합니다).
함께 보기
@tabula-css/tokens— core의 테이블과 스키마를 사용하는 검증기와 리졸버.@tabula-css/registry— core의 스키마에 대해registry.json/manifest.json을 검증하는 리더.@tabula-css/merge— core의TAB-E3xx진단을 던지는 런타임.- 핵심 개념 — 이 테이블들이 부호화하는 지역성, 레지스트리, 병합 알고리즘, 금지된 메커니즘 카탈로그.