Each panel is the real stdout, stderr and exit status of the uxdsl CLI in this repository, run against a small project by scripts/capture-capabilities.js; npm test runs it again and fails when the CLI prints something else. Paths are shown as <project>.
$ uxdsl
Usage: uxdsl <command> [options]
Commands:
init First-run setup: uxdsl.config.cjs, src/styles.uxdsl (the file you
edit), the generated src/uxdsl-entry.uxdsl that imports it and,
in a Next.js project, postcss.config.js. Never overwrites a file.
generate-entry Rewrite the entry file's @import list from the .uxdsl files
found under a directory. Run it after adding or removing files.
build Compile the entry (or every entry in "builds") to CSS, once;
--watch keeps rebuilding as files change.
theme Print the effective theme as JSON: the same discovery and
resolution as build, no CSS written.
Global options:
--help, -h This help. "uxdsl <command> --help" describes one command.
--version, -v Print the installed uxdsl version.
Set UXDSL_DEBUG=1 to log which config and theme files were discovered.
An unrecognized flag, or a flag used on the wrong command, fails with a
suggestion instead of being ignored.uxdsl init --multi scaffolds a theme entry plus one component entry instead of one entry. uxdsl generate-entry --src <dir> --out <file> --exclude <a,b> rewrites the entry's @import list from the .uxdsl files under a directory. uxdsl <command> --help prints one command's options, uxdsl --version the installed version. An unknown command or flag fails before anything runs, with a suggestion.
The build config, uxdsl.config.cjs (or .js), says what to compile and where. The theme file, uxdsl.theme.json (or uxdsl.theme.js/.cjs exporting the object), holds the theme and nothing else; it is discovered next to the config, or in the working directory, by the CLI and by the PostCSS plugin, the Vite plugin and the Webpack loader alike. A config that carries theme or breakpoints is refused, naming where each belongs.
| Config key | What it does |
|---|---|
entry, outFile | One entry and its output. |
builds | Several entries, [{ entry, outFile, includeTheme? }], compiled from one theme. |
includeTheme | Emit the theme's :root definitions (default true); per entry in builds. |
watch | Extra paths for --watch, beyond the entries, their imports and the config and theme files. |
references | Tokens defined outside the theme (externalTokens, css) and how strictly a missing one fails. |
strictTheme | Families the project must declare completely, such as ['palette']. |
sourcemap | false, 'inline' or 'external'. |
The project every command below runs in — a config made with defineConfig from uxdsl/config, a theme file whose $schema points at the packaged schema, three sources:
uxdsl.config.cjs// The build config of the small project whose real CLI output the playground's /docs/tooling
// page shows (captured by scripts/capture-capabilities.js). `defineConfig` is the typed
// helper from `uxdsl/config`. The theme is not named here: `uxdsl build` discovers
// `uxdsl.theme.json` next to this file.
const path = require('path');
const { defineConfig } = require('uxdsl/config');
module.exports = defineConfig({
entry: path.join(__dirname, 'src/main.uxdsl'),
outFile: path.join(__dirname, 'dist/app.css'),
});uxdsl.theme.json{
"$schema": "../../node_modules/uxdsl/schema/theme.schema.json",
"breakpoints": { "xs": 0, "sm": 480, "md": 768, "lg": 1024, "xl": 1280 },
"palette": {
"primary": { "main": "#0f766e" }
}
}src/card.uxdsl.card {
@ds-surface(contained);
display: flex;
flex-direction: xs(column) md(row);
gap: density(4);
}src/button.uxdsl.save {
@ds-button(contained primary 2);
}src/legacy.uxdsl/* Left out of the entry with --exclude: an old stylesheet nothing imports any more. */
.old-banner {
color: palette(primary.main);
}--config, --entry$ uxdsl build
[uxdsl] Theme config detected
[uxdsl] built dist/app.css (49478 bytes)49478169$ uxdsl build --config uxdsl.config.cjs
[uxdsl] Theme config detected
[uxdsl] unchanged dist/app.css (compiled output identical to the file on disk; not rewritten)$ uxdsl build --entry src/card.uxdsl --out dist/card.css
[uxdsl] Theme config detected
[uxdsl] built dist/card.css (47671 bytes)4767116$ uxdsl build --help
Usage: uxdsl build [options]
Compiles the entry from uxdsl.config.cjs (or --config, or --entry/--out) to
one CSS file. The theme comes from the theme file next to the config
(uxdsl.theme.json, or uxdsl.theme.js/.cjs exporting a computed theme).
Options:
--config, -c Path to the build config (default: uxdsl.config.cjs or
uxdsl.config.js in the current directory).
--entry, -e Entry .uxdsl file; with --out, compiles it without a config.
--out, -o Output CSS file path (with --entry).
--watch, -w Rebuild on file changes. Survives a broken edit: the last
good CSS stays on disk until the next valid save.
--include-theme, --no-include-theme
Emit (or skip) the global :root token definitions.
Default true. Pass --no-include-theme for a component or
CSS-Module entry that consumes tokens another entry
defines. Overrides "includeTheme" in the config.
--sourcemap, --sourcemap=inline, --no-sourcemap
Emit a source map. Bare --sourcemap means "external":
writes <outFile>.map and a sourceMappingURL comment;
=inline embeds it as a data URI. Default off. Overrides
"sourcemap" in the config.
--strict-theme=<family,...>, --no-strict-theme
Fail the build, before writing anything, when one of the
named theme families is only partly declared by the
project and the rest was filled from the base theme
(e.g. --strict-theme=palette,breakpoints). A scope is
required: the theme model is partial overrides, so an
unscoped check flagged the recommended usage as
incomplete. --no-strict-theme turns off "strictTheme"
from the config for one run.The first entry defines every token in :root, once (--include-theme, the default). Every other entry — a component sheet, a CSS Module — compiles with --no-include-theme (or includeTheme: false in builds, or in the PostCSS plugin's options) and only consumes those tokens: no :root, so a CSS Modules loader accepts it, and nothing is repeated. This site does exactly that: every component's .uxdsl is a CSS Module compiled with includeTheme: false.
$ uxdsl build --entry src/card.uxdsl --out dist/card-with-theme.css --include-theme
[uxdsl] Theme config detected
[uxdsl] built dist/card-with-theme.css (47671 bytes)4767116$ uxdsl build --entry src/card.uxdsl --out dist/card-only.css --no-include-theme
[uxdsl] built dist/card-only.css (464 bytes)It wrote dist/card-only.css:
.card {
padding: var(--uxdsl__surface__contained-padding);
border-radius: var(--uxdsl__surface__contained-radius);
background: var(--uxdsl__surface__contained-bg);
color: var(--uxdsl__surface__contained-color);
border: var(--uxdsl__surface__contained-border);
box-shadow: var(--uxdsl__surface__contained-shadow);
display: flex;
flex-direction: column;
gap: var(--uxdsl__density__4);
}@media (min-width: 768px) {.card {
flex-direction: row;
}
}4640--sourcemap writes <outFile>.map and a sourceMappingURL comment, so devtools point at the .uxdsl line that produced a rule; --sourcemap=inline embeds the map. The Vite plugin and the Webpack loader pass their maps on to the next loader.
$ uxdsl build --entry src/card.uxdsl --out dist/card-mapped.css --no-include-theme --sourcemap
[uxdsl] built dist/card-mapped.css (508 bytes) + card-mapped.css.map (408 bytes)It wrote dist/card-mapped.css:
.card {
padding: var(--uxdsl__surface__contained-padding);
border-radius: var(--uxdsl__surface__contained-radius);
background: var(--uxdsl__surface__contained-bg);
color: var(--uxdsl__surface__contained-color);
border: var(--uxdsl__surface__contained-border);
box-shadow: var(--uxdsl__surface__contained-shadow);
display: flex;
flex-direction: column;
gap: var(--uxdsl__density__4);
}@media (min-width: 768px) {.card {
flex-direction: row;
}
}
/*# sourceMappingURL=card-mapped.css.map */["../src/card.uxdsl","<no source>"]card-mapped.csscard-mapped.css.mapuxdsl build --watch rebuilds when a source, an import, the config or the theme file changes. A broken edit is reported and the last good CSS stays on disk; the next valid save recovers. A session recorded by the capture script as it edited a file:
start: uxdsl build --watch
[uxdsl] Theme config detected
[uxdsl] built dist/app.css (49478 bytes)
[uxdsl] watching for changes...
edit src/card.uxdsl: add border-radius: radius(3);
[uxdsl] change src/card.uxdsl
[uxdsl] Theme config detected
[uxdsl] built dist/app.css (49520 bytes)
edit src/card.uxdsl: add xxl(2rem), a breakpoint that is not configured
[uxdsl] change src/card.uxdsl
[uxdsl] Theme config detected
[uxdsl] build failed: uxdsl: src/card.uxdsl:5:3: UXD_BREAKPOINT_UNKNOWN: xxl(...) is not a configured breakpoint or a known CSS function; configured breakpoints: xs, sm, md, lg, xl.
undo the edit
[uxdsl] change src/card.uxdsl
[uxdsl] Theme config detected
[uxdsl] built dist/app.css (49478 bytes)
--diff, --contrast, --strict-themeuxdsl theme prints the effective theme (no CSS written; it works from a theme file alone). --diff prints only what the theme file mentions, each leaf labeled project or default. --contrast runs the WCAG gate of Accessibility. --strict-theme=<families> fails when a named family was partly inherited — on build too; a scope is required.
$ uxdsl theme --diff
[uxdsl] palette.primary mixes your values (main) with base values (light, dark, contrast)stdout is a JSON document; an excerpt:
[
{
"path": "palette.primary.main",
"value": "#0f766e",
"source": "project"
},
{
"path": "palette.primary.light",
"value": "color(purple.500)",
"source": "default"
},
{
"path": "palette.primary.dark",
"value": "color(purple.900)",
"source": "default"
},
{
"path": "palette.primary.contrast",
"value": "color(white)",
"source": "default"
}
]$ uxdsl theme --contrast
[uxdsl] --contrast: no blocking failure. 111 more pairs fail and are covered by an exception (listed under "excepted", not passing).stdout is a JSON document of 377744 bytes; an excerpt:
[]$ uxdsl build --strict-theme=palette
[uxdsl] Error: --strict-theme (scoped to: palette): the following theme families you declared are partially filled from defaults: palette. Provide every key of these families explicitly, or drop --strict-theme/strictTheme if inheriting some of them is intentional.$ uxdsl build --strict-theme=breakpoints
[uxdsl] Theme config detected
[uxdsl] unchanged dist/app.css (compiled output identical to the file on disk; not rewritten)$ uxdsl build --strict-theme
[uxdsl] Error: --strict-theme needs a scope: name the families that must be completely declared, e.g. --strict-theme=palette,breakpoints. An unscoped check treats every partial override — the theme model itself — as incomplete, so it is not accepted.$ uxdsl generate-entry --src src --out src/main.uxdsl --exclude legacy.uxdsl
[uxdsl] Scanning src for .uxdsl files...
[uxdsl] Generated entry file at src/main.uxdsl with 2 imports.It wrote src/main.uxdsl:
/* AUTO-GENERATED FILE - DO NOT EDIT MANUALLY */
/* Generated by uxdsl generate-entry */
/* --- Application & Component Imports --- */
@import './button.uxdsl';
@import './card.uxdsl';$ uxdsl watch
[uxdsl] Unknown command "watch": use "uxdsl build --watch"uxdsl-vscode extension highlights .uxdsl files and completes the token functions, breakpoints, directives, @uxdsl theme;, and the roles, tones and sizes of the base theme (not yet a project's own roles). It does not compile or validate; the build does."$schema": "./node_modules/uxdsl/schema/theme.schema.json" gives any JSON editor validation and completion of every family.init writes // @ts-check and a JSDoc type on the config object, so the editor flags a mistyped key; nothing is loaded at build time for it.uxdsl/engine, for toolsThe per-family engines the compiler is built from, for editors, inspectors and generators. It is exempt from semantic versioning — do not build an application on it. Two of them, run on the active theme:
buttonDeclarations · inputDeclarations · resolveTypographyRoleWhat @ds-button(contained) emits for the active theme:
padding | var(--uxdsl__surface__contained-padding) |
border-radius | var(--uxdsl__surface__contained-radius) |
background | var(--uxdsl__surface__contained-bg) |
color | var(--uxdsl__surface__contained-color) |
border | var(--uxdsl__surface__contained-border) |
box-shadow | var(--uxdsl__surface__contained-shadow) |
background | var(--uxdsl__button__contained-hover-bg) |
color | var(--uxdsl__button__contained-hover-color) |
outline | var(--uxdsl__button__contained-focusvisible-outline) |
outline-offset | var(--uxdsl__button__contained-focusvisible-outline-offset) |
background | var(--uxdsl__button__contained-selected-bg) |
color | var(--uxdsl__button__contained-selected-color) |
opacity | var(--uxdsl__button__contained-disabled-opacity) |
cursor | var(--uxdsl__button__contained-disabled-cursor) |
inspectReferencesLoading PostCSS…