A style language that tells you what you got wrong.
In CSS, a mistyped property is quietly ignored. In DSSL it is an error with the line number and a suggested fix. You also get design tokens as the vocabulary, a cascade you can explain in one sentence, and motion in four words.
It needs JavaScript, and the section below is honest about what that costs.
83 kB gzipped, no dependencies, works with any HTML and any JavaScript.
One line each, running on this page
None of this is a video or an image. Every effect below is a few words of DSSL applied by the same engine that styles the rest of the site. Move your mouse over the glass.
.torch { use fire }Fire is a recipe you write once with aura-glow and aura-particle, then reuse by name. Nothing is preset, so every number is yours.
glass: 18; tilt: 10 glareglass blurs what is behind it and survives a CSS build. tilt turns toward the pointer and stays in the engine.
.sofa { texture: wood scale 6 }Forty textures, ten of them wood species, or build your own from a generator and a few settings. They lay on top of whatever background the element already has.
Edit these texturesslider: graph 20 45 70 90 ...Range inputs are styled through four vendor pseudo-elements. slider: writes them all from one description, and can turn the track into a histogram.
Want to see a whole page? Heartwood is a field guide to wood, built with these: generated growth rings, glass, tilt, CSS counters, a fire, and styles compiled to real CSS with dssl build.
Four things CSS leaves to you
The cascade is arithmetic
CSSAn id beats a class beats a tag, then !important beats everyone.
DSSLLater wins, inside layers. That is the whole cascade, and there is no !important to reach for.
Typos fail silently
CSSMisspell background or a variable and the browser quietly does nothing.
DSSLYou get the file, the line and a did-you-mean. The editor above shows exactly that.
Motion needs a paragraph
CSS@keyframes plus an animation shorthand with five values in a fixed order.
DSSLDeclare a motion once, then write animate: rise-in 400 snap.
Theming from JavaScript is a hack
CSSYou poke data attributes or inline styles by hand and hope the cascade agrees.
DSSLDSSL.signal('theme', 'dark') restyles the page, and you can ask the engine why.
The same card, written twice
The DSSL version is about half the characters (141 against 304, ignoring whitespace). It is not fewer lines. It is shorter because the vocabulary does the work: tokens for colour and spacing, one word for a shadow, one number for a transition.
styles.css
.card {
background-color: var(--card);
border-radius: 14px;
padding: 16px;
box-shadow: 0 2px 4px rgb(0 0 0 / .1),
0 4px 14px rgb(0 0 0 / .08);
transition: all 160ms cubic-bezier(.2,.9,.3,1);
}
.card:hover {
transform: translateY(-4px);
box-shadow: 0 4px 8px rgb(0 0 0 / .12),
0 14px 36px rgb(0 0 0 / .12);
}
.card:hover .chip { background: var(--accent-soft); }card.dssl
.card {
background: color.card
radius: md
padding: space.4
shadow: md
transition: 160 snap
hover {
translate: 0 -4
shadow: lg
.chip { background: color.accent/12 }
}
}This page has no CSS file
The engine matches selectors against the live DOM, runs its own cascade and drives motion through the Web Animations API. Pseudo-elements are the one exception: they compile to a generated stylesheet, because inline styles cannot reach them.
- Its own cascade
- Later wins. Layers when you care. No specificity score, and no !important, ever.
- Tokens as vocabulary
- color.ink, space.4, radius.md. References compile to custom properties you can inspect.
- Readable states
- hover, focus-visible and active are blocks. Rules nested under a state just work.
- One-word conditions
- @media dark, @media 768, @media 480..960. The same shape for @container and @signal.
- Motion in four words
- animate: rise-in 400 snap both. Add seen and stagger to reveal a grid on scroll. Reduced motion is respected by default.
- Behaviors
- Name a pattern once, like a focus ring or a chip, and use it anywhere with one line.
- Loud errors
- Unknown property, unknown token, bad value. Each one names the file and line and suggests a fix.
- Texture, aura and sliders
- Forty textures, ten of them real wood species, light and particles with no presets (you build the fire), and one-line range sliders. The VS Code extension has a designer with sliders and live previews.
- A JavaScript bridge
- DSSL.signal restyles live, DSSL.replay reruns motion, DSSL.compile powers tools like the VS Code extension.
Where DSSL is the wrong tool
DSSL is experimental and moving fast. It suits creative sites, prototypes, docs, playgrounds and internal tools. Here is what to weigh before you pick it.
- It needs JavaScript. With no JavaScript there are no styles, because there is no CSS to fall back to.
- Styles arrive after the engine loads. A big page can flash unstyled. This site avoids it by putting its stylesheets inside the HTML.
- Strict CSP blocks it. A policy that forbids inline styles stops the engine from working.
- Inline styles are heavy. In our test, 3000 small cards came to about 4.5 times the HTML of the same page with a CSS class.
If a page has to work without JavaScript, build the part of DSSL that can be plain CSS. dssl build turns tokens, states, media queries and layers into a real .css file. @signal rules and animate: stay in the engine, and the report counts them so you know exactly what was left behind.
node cli/dssl.mjs build styles.dssl -o styles.css
node cli/dssl.mjs check styles.dssl --strict # for CIInstall
The engine is a folder of plain JavaScript files. No package manager, no build step, nothing installed globally. Any of these gives you a dssl-engine folder with the engine, a starter token set, an example stylesheet and a usage guide.
macOS, Linux and BSD
curl -fsSL https://dssl-sable.vercel.app/install.sh | shPlain sh is enough. It uses curl or wget, whichever you have, and unpacks with tar, falling back to unzip or python3.
Windows
irm https://dssl-sable.vercel.app/install.ps1 | iexPowerShell 5.1 or newer, no admin rights needed.
No terminal
The exact package the one-liners fetch. Unzip it anywhere and read USAGE.md.
Then wire it into a page
<script type="module" src="/dssl/boot.js"></script>
<link rel="stylesheet/dssl" href="/styles/tokens.dssl">
<link rel="stylesheet/dssl" href="/styles/hello.dssl">The engine fetches your .dssl files, walks the DOM and keeps restyling as elements come and go. Inline sheets work too, with <style type="text/dssl">. The docs cover the rest.