@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의 리졸브된 토큰 모델을 받아, 프로젝트의 빌드가 import하는 단일 스타일시트를 산출합니다: 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 프로파일 레벨에서는, 기본 text-*/font-*/leading-*/tracking-* 스케일을 재설정하여 프로파일 자체의 부분 타이포그래피 유틸리티만 존재하도록 하는 @theme 블록도 함께); 내장된 토큰 테마; 후보 클래스당 하나의 @utility 블록; @source inline(...) 어휘 폐쇄; 별도로 이어 붙는 @layer utilities 블록 안의 선언된 변형 체인 규칙(buildChainLayer()를 통해, 모델이 어떤 체인도 선언하지 않으면 생략됨); 그리고 자체 레이어에 담긴 프로즈 격리(prose-quarantine) 제외 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;주변의 import/theme/source-inline 뼈대 없이, @utility <class> { … } 블록만 — 모델의 순위 순서로 후보당 하나씩 — 렌더링합니다. 각 선언은 후보의 리터럴 value 대신 var로 간접 참조되는 emitted 형태를 우선하므로, 방출된 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 규칙이며, 그 declaration 본문은 기본 유틸리티의 것과 바이트 단위로 동일합니다(buildUtilities()와 같은 렌더링 경로). 규칙은 전역 (effectiveRank, class) 순서로 방출되며, 같은 미디어 조건을 공유하는 연속된 규칙은 하나의 @media 블록으로 묶이므로, 문서 순서가 병합의 전순서를 정확히 실현합니다. 모델이 어떤 체인도 선언하지 않으면 빈 문자열을 반환합니다. 체인 규칙은 의도적으로 @source inline에 나열되지 않습니다 — 거기 나열된 체인은 프로파일의 (0,1,0) 형태와 모순되는 순정 (0,2,0) 형태의 중복을 Tailwind가 생성하게 만들 것이기 때문입니다; 리터럴 규칙으로 방출하는 것이 구성상 올바른 캐스케이드 순서를 강제하는 방법입니다.
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 중괄호-전개(brace-expansion) 스펙 집합으로 접습니다 — 정렬되어 있으므로 스펙 목록은 클래스 집합의 순수 함수입니다 — 반면 접기에 안전하지 않은 것(-m-md 같은 부정 클래스, 단일 세그먼트 이름)은 있는 그대로 나열됩니다. expandSpec()/expandSpecs()는 정확히 그 역(inverse)으로, 실제 Tailwind 빌드 없이도 왕복(round-trip) 속성 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를 방출하지 않도록 만드는 이유.