UXDSL · CSS with a theme
Breakpoints belong to the theme, not to your components.
Spacing, type and color — and how each of them steps at a breakpoint — are defined once in a theme JSON. Components name them. The compiler writes the media queries and refuses any reference it cannot resolve.
The theme uxdsl.theme.json
{
"densities": {
"4": "xs(space(4)) md(space(5)) xl(space(6))"
}
}UXDSL you write card.uxdsl
.card {
@ds-surface(contained);
display: grid;
padding: density(4);
gap: xs(0.5rem) md(1rem);
}
.card h2 { @ds-typo(h2); }CSS you get compiled by uxdsl
:root { --uxdsl__typography__h2-font-size: var(--uxdsl__space__6); }
@media (min-width: 480px) { :root { --uxdsl__typography__h2-font-size: var(--uxdsl__space__7); } }
@media (min-width: 1024px) { :root { --uxdsl__typography__h2-font-size: var(--uxdsl__space__8); } }
@media (min-width: 1280px) { :root { --uxdsl__typography__h2-font-size: var(--uxdsl__space__9); } }
:root { --uxdsl__density__4: var(--uxdsl__space__4); }
@media (min-width: 768px) { :root { --uxdsl__density__4: var(--uxdsl__space__5); } }
@media (min-width: 1280px) { :root { --uxdsl__density__4: var(--uxdsl__space__6); } }
.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: grid;
padding: var(--uxdsl__density__4);
gap: 0.5rem;
}
@media (min-width: 768px) { .card { gap: 1rem; } }
.card h2 {
font-family: var(--uxdsl__typography__h2-font-family);
font-size: var(--uxdsl__typography__h2-font-size);
line-height: var(--uxdsl__typography__h2-line-height);
font-weight: var(--uxdsl__typography__h2-font-weight);
letter-spacing: var(--uxdsl__typography__h2-letter-spacing);
margin-block-start: var(--uxdsl__typography__h2-margin-block-start);
margin-block-end: var(--uxdsl__typography__h2-margin-block-end);
}Drag the frame's right edge (or the slider). The card has no breakpoint in it; its padding and title step at the theme's.
- frame
- 390px
- breakpoint
- …
--uxdsl__density__4- …
h2font-size- …
What the theme decides
Tokens that step
Spacing, density, type, radii and shadows are defined once with their breakpoint progression. A component names the token.
Roles, not recipes
@ds-surface, @ds-button and @ds-input apply a role from the theme: padding, corners, colors, border, shadow and states.
Nothing silent
An unknown token, breakpoint or role stops the build with a UXD_* code, the file, line and column, and what exists instead.
One JSON, three uses
The theme the build compiles is the one applyTheme changes in the browser and uxdsl theme --contrast audits.
Experiment: a theme from a name
Type a name and a language model writes a theme override for this site; it is validated and applied with applyTheme, like the header's theme buttons. It changes values only — the components are not rebuilt.