These pages document uxdsl@0.5.0-beta.9. It is a pre-release: an API can still change before 1.0.0, and every change is in the package CHANGELOG.
npm i -D uxdsl
postcss (^8.4.31) is a peer dependency, which npm 7 and later install for you.
npx uxdsl init
init writes a typed uxdsl.config.cjs, src/styles.uxdsl (the file you edit) and src/uxdsl-entry.uxdsl (the entry, regenerated by uxdsl generate-entry; do not edit it), and adds uxdsl:build and uxdsl:watch scripts. It writes no theme file: the base theme is built in. What it printed in an empty project:
$ uxdsl init
[uxdsl] Initializing...
-> Created uxdsl.config.cjs
-> Created src/styles.uxdsl (edit this one)
[uxdsl] Scanning ./src for .uxdsl files...
[uxdsl] Generated entry file at src/uxdsl-entry.uxdsl with 1 @use rule.
-> Added script(s) to package.json: uxdsl:build, uxdsl:watch
[uxdsl] Initialization complete.
Install it in this project: npm i -D uxdsl
Next steps:
1. Edit src/styles.uxdsl. The entry src/uxdsl-entry.uxdsl is generated: the watch below regenerates it when a .uxdsl file is added or removed; without it, run "npx uxdsl generate-entry".
2. Build: npm run uxdsl:build
Watch: npm run uxdsl:watch
3. Import the generated CSS once, from your root layout or app entry (path relative to that file):
import './src/uxdsl.css';
Editor support: uxdsl.config.cjs is type-checked by your editor (// @ts-check).
For .uxdsl highlighting and completion (VS Code extension, installed from a .vsix)
and theme JSON completion ("$schema"), see "Editor support" in the uxdsl README.npx uxdsl build --watch
Import the generated src/uxdsl.css once, from your root layout or app entry. A broken edit is reported with its UXD_* code and line; the last good CSS stays on disk until the next valid save.
src/styles.uxdsl starts with one rule. With no theme file at all, it compiles to:
UXDSL you write src/styles.uxdsl
.example {
@ds-surface(contained);
padding: density(3);
}CSS you get compiled by uxdsl; :root shows only this token
:root { --uxdsl__density__3: var(--uxdsl__space__3); }
@media (min-width: 768px) { :root { --uxdsl__density__3: var(--uxdsl__space__4); } }
@media (min-width: 1280px) { :root { --uxdsl__density__3: var(--uxdsl__space__5); } }
.example {
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);
padding: var(--uxdsl__density__3);
}To change a token, add uxdsl.theme.json next to the config with only what you change — { "densities": { "3": "xs(space(2)) md(space(4))" } } — and every density(3) follows. The Theme page lists every family.
The CLI works with any framework. Next.js, Vite and Webpack can also compile .uxdsl themselves, through the same pipeline.
In a Next.js project init also writes postcss.config.js — Next's own default plugins, which a custom file replaces, then uxdsl/postcss with includeTheme: false — so CSS Modules can use tokens while the theme comes once from the CLI's src/uxdsl.css. Run npm run uxdsl:watch next to next dev, and import src/uxdsl.css from app/layout.tsx.
postcss.config.js, as uxdsl init wrote itmodule.exports = {
plugins: {
// Next.js's own default PostCSS plugins, with Next's own options. A custom
// postcss.config.js replaces Next's defaults, so they are named here; these
// are the modules Next ships and uses for its default configuration. To
// pin your own versions instead, install postcss-flexbugs-fixes and
// postcss-preset-env and use those names.
'next/dist/compiled/postcss-flexbugs-fixes': {},
'next/dist/compiled/postcss-preset-env': {
autoprefixer: { flexbox: 'no-2009' },
stage: 3,
features: { 'custom-properties': false },
},
// The CLI-generated global CSS already contains the theme; the project's
// uxdsl.theme.json (or uxdsl.theme.js/.cjs), if any, is discovered
// automatically for everything else.
'uxdsl/postcss': { includeTheme: false },
},
};/* app/card.module.css: compiled by uxdsl/postcss, no :root of its own */
.card { padding: density(4); background: palette(surface.main); }
With no CLI at all, the PostCSS plugin compiles every stylesheet of the app. Keep includeTheme: false (the same postcss.config.js as above) and write @uxdsl theme; once, at the top level of the one global stylesheet: the theme's :root is emitted there and nowhere else, so CSS Modules stay free of :root.
/* app/globals.css, imported once from app/layout.tsx */
@uxdsl theme;
body { margin: 0; }
A misspelled, repeated or nested marker is UXD_THEME_MARKER. The theme is the project's uxdsl.theme.json, discovered from the working directory.
// vite.config.js
import { defineConfig } from 'vite'
import uxdsl from 'uxdsl/vite'
export default defineConfig({ plugins: [uxdsl()] })
// main.js: a .uxdsl import is a CSS import, handled by Vite's own CSS pipeline (HMR, build extraction)
import './styles.uxdsl'
Vite 4 and later. The plugin discovers uxdsl.theme.json the way the CLI does; import css from './panel.uxdsl?inline' returns the compiled CSS as a string.
// webpack.config.js
module.exports = {
module: {
rules: [
{ test: /\.uxdsl$/, use: ['style-loader', 'css-loader', 'uxdsl/webpack'] },
],
},
}
Webpack 5. Use MiniCssExtractPlugin.loader instead of style-loader to extract a real .css file. Every @import-ed partial and the theme file are registered as dependencies, so watch mode sees them.