CSS Formatter
Format messy or minified CSS, SCSS or LESS to editor-consistent Prettier output — entirely on this device.
This tool formats — it does not lint or validate. Unknown at-rules and vendor hacks pass through unchanged.
Press ? for shortcuts
Do it offline:$ npx prettier --parser=css styles.css
How to use it
- Paste a stylesheet or drop a .css, .scss or .less file — dropped files never touch the network, and past 2 MB an explicit button names the size first.
- Check the detected-dialect badge — one click overrides a wrong guess — then pick print width, spaces or tabs, double or single quotes.
- Fix any syntax error from the verbatim message, caret frame and Jump to error — your bytes stay exactly as pasted.
- Copy the result, download it under the matching extension, or share the tool state as a permalink.
Paste minified or hand-mangled styles and get back what Prettier itself would print: this page runs the real Prettier engine with its postcss plugin for CSS, SCSS and LESS, inside a Web Worker on this device, so nothing you format is uploaded. A detector proposes the dialect, and print width, indentation and quotes stay yours to set.
How it works
The engine is Prettier's standalone build with its postcss plugin, fetched once as a separate chunk and run inside a Web Worker, so a multi-megabyte bundle formats without freezing the page. One plugin carries the css, scss and less parsers — that is why a single tool covers all three dialects.
Formatting rewrites presentation only: indentation, line breaks against the print width, spacing inside values, quote style, stray semicolons. Selectors, property names, values and declaration order are never edited, and unknown at-rules such as @apply or vendor hacks like *zoom and _height pass through byte-for-byte instead of raising errors.
Editor extensions, pre-commit hooks and the prettier CLI embed this same core, so equal input, version and options print equal bytes everywhere — what you copy here is what your repository's format check expects to see.
Examples
.card{display:flex;flex-direction:column;gap:12px;padding:16px;}.card:hover{box-shadow:0 2px 8px rgba(0,0,0,0.2);}.card {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px;
}
.card:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
A minified one-liner expands to one declaration per line at two-space indentation. Strip the whitespace back out and the two texts are identical character for character — nothing but formatting moved.
$accent: #0af;
.nav{.item{color:$accent;&:hover{color:darken($accent,10%)}}}$accent: #0af;
.nav {
.item {
color: $accent;
&:hover {
color: darken($accent, 10%);
}
}
}
The $accent variable, nested .item block, & parent reference and darken() call all survive untouched. The leading $accent: declaration is also what makes auto-detection answer SCSS here.
a{color:red;;}a {
color: red;
}
A doubled semicolon is noise, not a syntax error: the extra one disappears and the rule formats normally instead of failing.
a{background:url(data:image/png;base64,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)}a {
background: url(data:image/png;base64,AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA);
}
At print width 80 the background line comes back 162 characters long and intact — a break inside url() would corrupt the encoded image, so that token is never wrapped.
Every example on this page is executed against the same engine that powers the tool, on every build.
Gotchas worth knowing
Formatting is not linting
Nothing here judges correctness: duplicate properties, unknown property names and suspect values format without comment; only a true syntax error stops the run. A dedicated CSS Linter is a separate planned tool — this page rewrites presentation, nothing else.
Comments survive, /*! banners included
A formatter deletes no comments, and the /*! convention that tells minifiers “keep this license text” rides through with its bang intact — safe to format before a minify step.
Invisible bytes are handled out loud
One leading byte-order mark is stripped with the engine note “Leading byte-order mark removed” — Prettier alone would let it through. Dropped files decode as UTF-8 without aborting: undecodable bytes and unpaired surrogates become the U+FFFD replacement character, always with a visible warning.
Big bundles: worker, elapsed time, Cancel
Past 200 KB the status line counts elapsed seconds beside a Cancel button; past 2 MB the run waits for an explicit press naming the payload size. The ceiling is 5 MB — beyond it the tool refuses, states the limit and hands you the CLI line below.
Data URIs never wrap
Inlined images push a single background declaration past any sane print width, and splitting the base64 run would break the image — so url() contents stay on one line at any width setting.
Common errors
Unclosed block starting at line 1, column 1.- A { opened but its } never arrived; postcss anchors the report where the doomed rule begins, not at end of input. Truncated copy-pastes are the usual source.
Unknown word color at line 1, column 3.- With the colon missing, color red cannot be read as a declaration, so the parser names the first token it could not classify. Restore the colon and the rule parses.
Unclosed string at line 1, column 11.- The column points at the opening quote whose partner never appears — a closing brace inside a quoted value does not end the rule.
FAQ
Does formatting change how my CSS works?
No. Whitespace, indentation, quote style and stray semicolons are rewritten; selectors, property names, values and their order come back token for token. The build re-executes every example on this page through the engine — the first is also checked character-equal to its input once whitespace is stripped.
Can it format SCSS and LESS?
Yes — the postcss plugin ships parsers for all three dialects. Detection scans the first 64 KB for evidence such as $width: declarations, @mixin blocks or @{...} interpolation and shows its guess as a badge; one click pins your choice, and downloads use the matching extension.
Why does the output match my editor?
Prettier is deterministic: one canonical print per input, version and option set, whether the caller is a VS Code extension, a Git hook or this page. If results differ, compare versions first — the defaults here are Prettier's own: width 80, two spaces, double quotes.
Keyboard shortcuts
- ⌘/Ctrl + Enter
- Format the current buffer — required above 2 MB.
- ⌘/Ctrl + S
- Download the result with the dialect's extension.
- ⌘/Ctrl + C
- Copy the whole output while nothing is selected.
- ⌘/Ctrl + Shift + C
- Copy the full output even with a selection active.
- t
- Toggle the color theme (ignored while typing).
- ? or ⌘/Ctrl + /
- List this page's shortcuts.
- Esc
- Close the topmost dialog without losing input.