# DSSL Guide Everything in the repo, in one file. Written for the person who just cloned this and wants to know what exists, what works, and what to reach for. Every `.dssl` example in this guide is compiled by `scripts/test-guide-examples.mjs` on every release, so the examples cannot drift from the compiler. If an example here stops compiling, that is a release blocker, not a docs bug. Status up front: DSSL is a real, tested style engine at v0.9.0, and it is honest alpha. It needs JavaScript, it writes inline styles, and it has a first-paint story you should read before shipping anything serious (see "Honest limits"). Within those limits, it is genuinely nicer to write than hand-rolled CSS for creative pages, docs, playgrounds and internal tools. --- ## 1. What this repo is | Path | What it is | |---|---| | `public/dssl/` | The engine: plain ESM files with no dependencies, loaded by one script tag. `parser.js` (text to tree), `resolver.js` (tree to rules), `engine.js` (applies them in the browser), `index.js` (the public API), `boot.js` (auto-boot), `utils.js`, `static.js` (`dssl build`), `css-keywords.js` (every CSS keyword and property, generated from the MDN data by `scripts/gen-css-keywords.mjs`), `server.js` (the same compiler with no DOM), `texture.js`, `aura.js`, `slider.js`, `effects.js` (glass, tilt), `color.js`, `lint.js`, `import.js` (CSS to DSSL), `fonts.js`, `dev.js` (inspector, hot reload, runtime lint; loaded only when asked for). This is the whole language. | | `public/styles/*.dssl` | The stylesheets that drive the docs site itself. The site is styled entirely by its own engine, which makes it the de-facto test suite. | | `public/examples/hello.dssl` | The starter stylesheet. Small, commented, every interaction real. | | `package/` + `public/install.sh`, `install.ps1` | The downloadable engine package: `curl -fsSL https://dssl-sable.vercel.app/install.sh \| sh`. | | `public/dssl-engine.zip`, `.tar.gz` | Same package as plain archives. | | `dssl-vscode/` | The editor extension (VS Code and VSCodium). Bundles the real parser and resolver, so editor squiggles are browser-console errors. See its own README and PUBLISHING.md. | | `src/` | The Next.js docs site: playground, bench page, spec, changelog. | | `scripts/` | Tests, package builds, the extension engine sync, deploy helpers. | | `llms.txt`, `AGENTS.md`, `PARITY.md` | The machine-reviewer layer and contributor rules. PARITY.md is the honest CSS feature matrix. | | `worklog.md` | The full build history, every decision logged. | Version rule: the engine version in `public/dssl/index.js` is the source of truth. The site badge, footer, changelog and READMEs follow it on every release. ## 2. Quickstart Two tags. No build step, no npm, nothing to compile: ```html ``` Or embed DSSL directly in your HTML (this is the Tailwind-ish answer: the language lives wherever you want it): ```html ``` ` ``` ### Using it for a game (0.14.0) A review by someone building a game on DSSL turned up twelve problems and a list of wishes. This is the language now, in the order they hit. **Motion** ```dssl motion bob { from { translate: 0 0 } to { translate: 0 8 } } motion pulse { from { opacity: 1 } to { opacity: .4 } } motion boing { 0%, 100% { translate: 0 0 } 40% { translate: 0 -18 } essential } // 0% and 40% work, a list works .coin { animate: bob 1200 infinite alternate delay 100 jitter 600 } // jitter: a different delay per element .hero { animate: boing 300 iterations 3 essential } // essential for this use only .spark::before { content: ""; animate: pulse 900 infinite } // motion on a pseudo-element ``` - **Reduced motion is never silent.** A system that asks for reduced motion stops every `animate:` that is not `essential`, which for a game is all of it. The engine now says so, once, in the console, and there are three ways out: `essential` inside the motion, `essential` on one `animate:` line, or for the whole page `` (`DSSL.motion('full')`; `'reduced'` holds everything still, `'auto'` goes back to following the system). - **Motions are shared like tokens.** `motion waddle { }` in one file is usable by `animate: waddle` in any file loaded after it (`dssl check` and `dssl build` fold them forward the same way). No more pasting a motion into three files. - **`animation-delay` and friends** (`-duration`, `-iteration-count`...) only tune a CSS animation. A rule using `animate:` has none, so they used to compile and do nothing. Now there is a warning that names the replacement: `delay N`, `jitter N`, `iterations N`, `infinite`, `alternate`, an easing, `essential`. And every motion is also written as a real `@keyframes` into the engine's own stylesheet, so the plain CSS form `animation: boing 300ms 2` works too. - **`DSSL.play(el, 'boing', { duration, delay, easing, iterations, hold, jitter, essential })`** runs a motion once and returns a promise that settles when it ends (or is cancelled). `hold: true` keeps the last frame. It follows reduced motion like `animate:`. This replaces add a class, set a timer, remove the class. - **Keyframes in CSS habits:** `0% { }`, `40% { }`, and `0%, 100% { }` are accepted next to `from`, `to` and `at 40%`. - **Motion on `::before` and `::after`** becomes a CSS animation, behind the reduced-motion check (and `dssl build` writes it). **Looks** ```dssl tokens { shadow { pop: 0 4 0 #000 } radius { pill: 99 } } .tomato { gradient: radial-gradient(circle, #fff6 18%, transparent 20%) tile 24 offset 4 4 + linear 180, #f55 -> #b11 } // the dots come FIRST, so they sit ON TOP of the red .title { text-stroke: 6 color.ink; font-size: 48 } // an outline OUTSIDE the letters .badge { shadow: pop; radius: pill } // your own shadow and radius tokens, by bare name .ring { background: conic-gradient(from 0deg, red, blue) } // from, at, in... all accepted .label { paint-order: stroke fill } // stroke and fill are accepted words ``` - **`gradient:` and `background-image:` in one rule** replace each other, and the first used to vanish without a word. Now it is a warning that tells you to layer them in the sentence: layers are joined with `+`, and a layer can be a plain CSS `radial-gradient(...)` or `url(...)`. Each layer takes `tile 24` (or `tile 24 12`), `offset 4 4`, `no-repeat`, `repeat-x`, `repeat-y`. - **`shadow: pop` and `radius: pill`** work for tokens you define (`tokens { shadow { pop: 0 4 0 #000 } radius { pill: 99 } }`), like the built-in `sm`, `md`, `lg`. - **`text-stroke: 6 color.ink`** is the stroke-plus-`paint-order` trick written once (`inside` keeps a centred stroke). **The engine and your scripts** - **Your inline styles win.** What the page wrote before the engine looked (a `style=""` attribute, a script placing an element) and what a script changes afterwards belongs to you: a rule with `left: 0` no longer undoes the positions your game code sets, exactly like in CSS. (The few properties the engine also moves itself, `transform`, `translate`, `transform-origin` and `grid-area`, are exempt from the after-the-fact part.) - **Static subtrees.** `