lax-css
Lax CSS, SCSS, and Less formatter, usable as a Rust library or as a dprint plugin.
Philosophy
This formatter is deliberately lax. It is built on the generic, semantics-free parsing model from CSS Syntax Level 3: the parser does not know what any at-rule, property, or value means, and the printer only ever adjusts whitespace, indentation, and line breaks.
Concretely, that means:
- Casing is never changed.
prefersDark: trueinside an unknown at-rule stays exactly as written. - Values are never rewritten, reordered, or normalized.
- Unknown or future syntax formats fine, because nothing is special-cased. Tailwind directives, vendor hacks, and tomorrow's at-rules all pass through.
- Custom property values are preserved verbatim, including inner whitespace.
SCSS and Less are supported through the same generic model. #{...} and
@{...} interpolations are treated as opaque tokens, and // line comments
are recognized everywhere. The indented Sass syntax (.sass files) is not
supported and there are no plans to support it; Prettier and Biome do not
support it either, and SCSS is the dominant syntax in practice.
Status
Experimental.
Long values and at-rule preludes wrap at the configured lineWidth, but a
line break is only ever introduced where the author already had whitespace,
so constructs where a space is meaningful (Tailwind arbitrary values, unicode
ranges, unquoted urls) are never broken apart. Author newlines inside values
are preserved, so hand formatted multi line font stacks, grid-template-areas
blocks, and SCSS maps keep their shape. Selectors are never wrapped.
Configuration
| Key | Default | Description |
|---|---|---|
lineWidth |
120 |
Target maximum line width. |
indentWidth |
2 |
Number of spaces per indent. |
useTabs |
false |
Use tabs instead of spaces. |
newLineKind |
lf |
Kind of newline to use. |
Development
Spec tests live in tests/specs. Each file contains one or more cases with
the input followed by an [expect] block.