Skip to content

Add a CSS engine to experiments/css - #17

Open
natemoo-re wants to merge 9 commits into
dom-integrationsfrom
experiments/css
Open

natemoo-re wants to merge 9 commits into
dom-integrationsfrom
experiments/css

Conversation

@natemoo-re

@natemoo-re natemoo-re commented Oct 22, 2026 •

Copy link
Copy Markdown
Member

Adds the Bombshell CSS engine into experiments/css/ and adds @bomb.sh/css.
Walk a @bomb.sh/dom tree, snapshot it, and get computed styles per node with real multi-stylesheet aggregation and inline styles.

Rust workspace

CSS text ──▶ bombshell-css-compiler ──▶ typed IR (v2, postcard bytes)
              (vendored lexbor drives a ~800-line C shim that emits a flat
               event stream of byte spans; everything semantic is Rust:
               nesting expansion, condition parsing, canonicalization)
                         │
                         ▼
             bombshell-css-runtime ──▶ computed declarations
              (parcel_selectors matcher, cascade + inheritance,
               custom properties / var(), media/container/supports eval)
                         │
                         ▼
             bombshell-css-wasm ──▶ freestanding component,
              WIT world `bombshell:css@0.4.0`, zero imports

JS package (@bomb.sh/css)

import { createEngine, compile } from '@bomb.sh/css';

// Host many stylesheets; @layer and document order are handled natively.
const engine = createEngine();
const id = engine.addSheet(baseCss);
engine.addSheet(themeCss);
engine.removeSheet(id);

// Snapshot a DOM tree and styles it:
const nodes = [...root].map((node) => ({
  tag: node.localName,
  id: node.id || undefined,
  classes: (node.getAttribute('class') ?? '').split(/\s+/).filter(Boolean),
  state: stateFlags(node.states),            // focus/hover/active bits
  width, height,                             // for container/media queries
  style: node.getAttribute('style'),         // inline styles, real cascade slot
}));
const computed = engine.computeTree(nodes, parents, env);  // per-frame entry
  • compile(css) → disposable Sheet (compiles + prepares; throws CompileError with .diagnostics when no IR; warnings-only compiles succeed). Sheet carries ir, diagnostics, lazily decoded version/rules
  • Engine — multi-sheet host: addSheet/removeSheet/clear, compute/computeTree, diagnostics, disposable
  • Free functions compute/computeTree/release, decodeIr(sheet|bytes), NO_PARENT
  • src/wasm.js — the runtime embedded: brotli+Z85-encoded optimized module (219 KB → 96 KB source) that decodes and WebAssembly.compiles itself on import; regenerable via scripts/bundle-wasm.mjs
  • loadCssComponent/loadCssComponentFromModule for hosts shipping their own artifact; the embedded artifact decodes via node:zlib (Node-like runtimes)

@socket-security

socket-security Bot commented Oct 22, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedcargo/​cssparser@​0.37.01001009310070
Addedcargo/​parcel_selectors@​0.28.3821009310070
Addedcargo/​wit-bindgen@​0.62.07910093100100
Addedcargo/​serde@​1.0.2298110093100100
Addedcargo/​cc@​1.5.19910093100100
Addedcargo/​postcard@​1.1.310010093100100
Addedcargo/​rustc-hash@​2.1.310010093100100

View full report

@natemoo-re natemoo-re changed the title Move the CSS engine into experiments/css Add a CSS engine to experiments/css Oct 22, 2026
@natemoo-re
natemoo-re changed the base branch from main to dom-integrations October 22, 2026 02:37
…stitution

`light-dark(a, b)` no longer folds to its light branch at compile time.
Both branches are validated and canonicalized as standalone colors, the
call is preserved through the IR, and the runtime resolves it at
computed-value time against the environment's color scheme: after var()
substitution (a custom property may carry the call verbatim into the
using declaration) and before descendants inherit the value. A malformed
call — anything other than exactly two colors — is invalid at
computed-value time and drops the declaration, like a failed var()
resolution. Resolution is scoped to the color properties the compiler
canonicalizes via a shared `is_color_property` predicate in css-core.

The compiler's sheet-level path rejects malformed calls with the same
"invalid color value" diagnostic the plain color path reports.

Also fix var() substitution to always re-serialize through the token
writer: custom-property values arrive verbatim, and the old early return
leaked uncanonicalized numbers (`oklch(70% 0.2 40)` stayed `0.2` where
the conformance corpus pins `.2`). Drop the unused
`Stylesheet::layer_rank` method.

Rebuild the embedded wasm artifact; extend the compiler and runtime
tests and the smoke test with light-dark coverage under both schemes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant