The 0.1 API. Setup (the Vite plugin, the stylesheet import) is in the README. Decisions and findings are in wayfinder.md.
Every ts block here is type-checked against the workspace cssints types by packages/cssints/test/docs.mjs (bun run test). A line that ends in // error must fail to type-check. Blocks fenced ts skip (output) are not checked. A few blocks show a build-time error that the types cannot see; their comments give the message.
Authoring surface. Import. with { type: "cssints" } marks code that runs at build time: cssints itself, plugins such as icons, and your own helper modules. The plugin…
Import, Names, Numbers, Values, Editor, Math, Layout contexts, flex(...parts) and grid(...parts) are the shorthand properties, …
Basic usage. The class names are a hash of the declaration (and its conditions), so the same declaration gives the same class in every file.
The class names, One sheet, cn() keeps the later of the same property, A layout context, Also exported
Without runtime. cn(...) merges styles at build time into one class string. Its arguments are fixed, so nothing is left at runtime and no cx is imported.
Conditions and layers. Conditions are wrapper functions. A wrapper takes styles and returns them under its condition. Wrappers nest.
Pseudo-classes, Query strings are checked, Layer = number of conditions, Order inside a layer, Class names, Bindings, A condition wraps declarations, not a kernel's marker
Global rules, attributes and parent states. css.global(selector, ...styles) is a rule with no class: body{margin:0}. The styles are member calls (margin("0"), cn(...)), checked by the grammar…
css.global(selector, ...styles), The selector, The sheet, @font-face and the other at-rules stay in CSS, attr(name, value?, op?), group(name?) and within(state, name?), Specificity of within() is one class, :has() is not offered
Tokens. A token is a Typed OM value whose toString() is var(--name). It fits anywhere a value of its type fits: color(blue), border("1px solid", blue). In…
Types, Names, Output, style() and token.set(), A token of the wrong type, A raw env() or if()
Themes. Tokens come from an inline DTCG-shaped const object. Every leaf is a token (see "Tokens") reached by property access. Values are DTCG values or CSS…
Types, Names, Support warnings, Fallbacks for older targets, Derived tokens in older browsers: a runtime, Themes, layers(...names), layers(...names, { before: true }), …
Fallbacks: firstThatWorks. firstThatWorks(wanted, ...fallbacks) is a part of any property: the value you want comes first, then what to use where it fails.
One class, Checked per value, Values, In several parts, Support warnings, Minify, Limits
Local at-rules: keyframes and positionTry. keyframes() and positionTry() define an at-rule next to the styles that use it and return its name, a typed value that fits where the grammar wants a name.
Steps are styles, positionTry() takes what @position-try takes, The name, Where the rule goes, The layer, In a helper module, Limits
Runtime: cx, cn, cv. One runtime package: cssints itself. import { cx } from "cssints" has no attribute and is the only code that ships to the browser, together with the…
One runtime package: cssints itself, cx(...), cn(...), cv(config), What ships, Old code, Boolean variants, compoundVariants, …
Errors. A bad value fails in the types, where the call is, and again at build time with the file and the place of the call, so the Vite overlay points at it. The types…
A bad argument of a CSS function is named, A warning is located and said once per file per build, No warning for what LightningCSS lowers
Plugins. A plugin is a module that exports the members of a scope() from cssints/plugin: scope("name", table).build(impl).export(). The table says what the…
The table, Local syntax names, Namespaces, uses, group: true, Kernels, Chain members, Call members, …
Functions: every CSS function is a call. Every CSS function that mdn-data has a grammar for is a member of css and a named import, camelCased: calc, min, clamp, round, sin, rgb, oklch, colorMix,…
Parts and operators, Math types, Other types, The build, Names that clash, colorMix, min, max, clamp are generated now
var(). A token is its var() already, so var(--name) is not written. css.var(token, fallback?) is the call that adds a fallback: css.var(t.space.sm, "4px")…
Positional arguments, label, Typed or opaque, The Typed OM values, Build time only, Errors, Limits
frame(): concentric radius. The first plugin, written only on scope() above. A frame passes its inner radius down the tree; a nested frame's radius is the parent's inner radius…
Methods, Build errors, What it emits, Browsers, Limits of the runtime
surface(): parent-relative colour. The second plugin on scope() and the same runtime. A surface passes its colour down the tree; a nested surface is that colour with its OKLCH lightness moved,…
Methods, Build errors, What it emits, Not a contrast guarantee, Browsers, Limits of the runtime
Icons: @cssints/icons. An icon is a style written on scope() (one scope, "icons", whatever the collection): a marker class, a mask kernel and one atomic class per icon that…
icons({ provider: "iconify-json", collection }), Names, Mask or image, Cost, Dev, Not yet
Plugin packages. More plugins on scope(), each a package of its own with its rules and limits in its README. Import them with the attribute, like cssints.
Publishing a library. A package written on cssints ships JavaScript that still has the attribute, and its .d.ts. The app that installs it evaluates the package with its own engine:…
Build with tsdown, Keep in the package, The app needs cssints and its plugin, The package must be a direct dependency of the app, Dev and HMR
Leaving files alone: exclude. A file outside node_modules is a site module when its code mentions cssints (a name, a comment, cssints/plugin), with or without the attribute: that is how a…
Vite's filter style, Nothing changes without it, An excluded file stays plain code
Debugging: from a class to its call. In dev the sheet has a source map: each rule with a class points at the first css.* call that made the class, so Chrome's Styles pane names that file and…
Debugging: the devtools overlay. An inspector in the page, built on what the dev server knows of every class. In dev only, unless the app asks for it in a build too (below). Install…
Classes by call, Properties, Checked against the browser, Tokens, How it gets the data, Nothing in a build, unless asked, In a build: devtools: "build", Ask OpenCode: opencode (dev only, off by default, cssints-ro4k, 2026-10-10), …
Debugging: traces of the engine. Opt-in. Set CSSINTS_TRACE=otlp (or otlp:http://host:port; the default is http://localhost:4318) before vite dev or vite build, and the plugin sends one trace…
Checks for tools: cssints/check. The engine's own checks, as functions, for a tool that answers as a build would (@cssints/mcp). Node only (culori, LightningCSS): nothing here reaches a…
contrast(foreground, background), loweredOf(property, value, warning)
TUI: cssints/tui. A terminal app writes its styles with cssints/tui, and the same Vite plugin compiles them to prop objects for terminal renderables, not to classes: there is no…
Opt-in, Members, Cells, Colours, cn(), Themes, The adapter, The bomb.sh adapter, …
Browser support and what 0.1 does not have. Types and checks for every property are generated from mdn-data and @webref/css by packages/css-grammar (bun run extract), not written by hand, so a new…
Types and checks for every property, Browser support, Not in 0.1