Skip to content

CSS Compiler

The CSS compiler is the first-party reference compiler. It reads visual tokens from a xtyle definition and produces CSS custom properties.

Terminal window
npm install @xtylejs/compiler-css
Terminal window
npx xtyle-css brand.xtyle.json # print to stdout
npx xtyle-css brand.xtyle.json -o tokens.css # write to file
import { compile } from "@xtylejs/compiler-css";
const result = compile({
xtyle: "1.0",
name: "my-brand",
visual: {
colors: {
palette: { ocean: "#0077B6", sand: "#F4E8C1" },
semantic: { primary: "ocean", surface: "sand" },
},
typography: {
families: { heading: "Georgia, serif", body: "system-ui, sans-serif" },
scale: { base: "16px", ratio: 1.25 },
},
},
});
console.log(result.css);
// :root {
// --xtyle-color-ocean: #0077B6;
// --xtyle-color-sand: #F4E8C1;
// --xtyle-color-primary: #0077B6;
// --xtyle-color-surface: #F4E8C1;
// --xtyle-font-heading: Georgia, serif;
// --xtyle-font-body: system-ui, sans-serif;
// --xtyle-font-size-base: 16px;
// --xtyle-font-scale-ratio: 1.25;
// }
console.log(result.tokenCount); // 8
SourceCSS Custom Property
visual.colors.palette.<name>--xtyle-color-<name>
visual.colors.semantic.<name>--xtyle-color-<name> (resolved against palette)
visual.typography.families.<name>--xtyle-font-<name>
visual.typography.scale.base--xtyle-font-size-base
visual.typography.scale.ratio--xtyle-font-scale-ratio
visual.typography.weights.<name>--xtyle-font-weight-<name>
visual.spacing.scale.<name>--xtyle-spacing-<name>
visual.layout.borderRadius.<name>--xtyle-radius-<name>
visual.layout.shadows.<name>--xtyle-shadow-<name>
visual.layout.breakpoints.<name>--xtyle-breakpoint-<name>

Semantic color values are resolved against the palette. If "primary": "ocean" and the palette contains "ocean": "#0077B6", the output is --xtyle-color-primary: #0077B6. If the value isn’t found in the palette, it passes through as-is (assumed to be a raw CSS color).

When a definition includes visual.components, the CSS compiler generates class-based rulesets alongside the :root custom properties.

Component StructureCSS Selector
Root element.xtyle-button
Child part.xtyle-button-label
Hover state.xtyle-button:hover
Focus state.xtyle-button:focus-visible
Disabled state.xtyle-button:disabled, .xtyle-button--disabled
Size variant.xtyle-button--sm
Visual variant.xtyle-button--secondary

State selectors on child parts scope through the root: .xtyle-button:hover .xtyle-button-label.

Variant selectors on child parts also scope through the root: .xtyle-button--secondary .xtyle-button-label.

Token references in anatomy definitions resolve to var(--xtyle-*) references in the CSS output. The compiler matches values against every token pool in the definition:

{ "background": "amber", "border-radius": "md", "font-family": "heading" }

Produces:

background-color: var(--xtyle-color-amber);
border-radius: var(--xtyle-radius-md);
font-family: var(--xtyle-font-heading);

Values that don’t match any token pass through as literals.

Some shorthand property names map to their CSS equivalents:

Definition PropertyCSS Property
backgroundbackground-color
radiusborder-radius
fontfont-family
weightfont-weight
colorcolor
elevationbox-shadow

Standard CSS properties (padding, border, opacity, etc.) pass through unchanged.

Given a button component with anatomy, states, sizes, and variants, the compiler produces:

.xtyle-button {
background-color: var(--xtyle-color-amber);
border-radius: var(--xtyle-radius-md);
padding-x: var(--xtyle-spacing-md);
padding-y: var(--xtyle-spacing-sm);
font-family: var(--xtyle-font-heading);
font-weight: 700;
}
.xtyle-button-label {
color: var(--xtyle-palette-coal);
font-size: 1rem;
}
.xtyle-button:hover {
background-color: #D4900F;
box-shadow: var(--xtyle-shadow-mid);
}
.xtyle-button--sm {
padding-x: var(--xtyle-spacing-sm);
padding-y: var(--xtyle-spacing-xs);
font-size: 0.875rem;
}
.xtyle-button--secondary {
background-color: transparent;
border: 1px solid var(--xtyle-color-amber);
}
.xtyle-button--secondary .xtyle-button-label {
color: var(--xtyle-color-amber);
}