CSS Formatting Best Practices: Readable Stylesheets That Scale
CSS is uniquely easy to write and uniquely hard to maintain. A 200-line stylesheet is manageable in any format. A 20,000-line stylesheet becomes a nightmare without consistent formatting. This guide covers the formatting practices that keep stylesheets readable at scale.
Indentation and spacing
Two spaces is the most common indentation in CSS, matching the JavaScript ecosystem's convention. Tabs work too -- the key is consistency across the team.
/* Consistent 2-space indentation */
.card {
display: flex;
flex-direction: column;
padding: 16px;
border-radius: 8px;
}
/* One blank line between rulesets */
.card-title {
font-size: 1.25rem;
font-weight: 600;
}
Always include a space after the colon and before the opening brace. Always put the closing brace on its own line. These small details add up to readability across thousands of lines.
Property ordering
Random property order is the most common source of CSS messiness. There are two main schools of thought.
Grouped by type (recommended)
Group related properties together in a consistent order:
.element {
/* 1. Positioning */
position: absolute;
top: 0;
right: 0;
z-index: 10;
/* 2. Display & Box Model */
display: flex;
align-items: center;
width: 100%;
padding: 16px;
margin: 0 auto;
/* 3. Typography */
font-family: Inter, sans-serif;
font-size: 14px;
line-height: 1.5;
color: #2c3e50;
/* 4. Visual */
background-color: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
/* 5. Misc */
cursor: pointer;
transition: background-color 0.2s ease;
}
This ordering is logical: you define where the element is, how big it is, what the text looks like, and then visual decoration. When scanning a ruleset, you know exactly where to look.
Alphabetical
Some teams prefer strict alphabetical ordering. It is easier to enforce with linters but harder to scan visually because related properties (like width and height) end up far apart.
.element {
align-items: center;
background-color: #ffffff;
border: 1px solid #e2e8f0;
border-radius: 8px;
color: #2c3e50;
cursor: pointer;
display: flex;
font-size: 14px;
padding: 16px;
position: absolute;
width: 100%;
}
Both approaches work. The worst option is no ordering at all.
Nesting depth limits
CSS preprocessors and native CSS nesting make it tempting to deeply nest selectors. Keep nesting to a maximum of 3 levels.
/* Good: shallow nesting */
.nav {
display: flex;
.nav-item {
padding: 8px 16px;
&:hover {
background-color: #f1f5f9;
}
}
}
/* Bad: too deep */
.page {
.content {
.sidebar {
.nav {
.nav-item {
.nav-link {
color: blue; /* 6 levels deep -- specificity nightmare */
}
}
}
}
}
}
Deep nesting produces highly specific selectors that are hard to override and signals that your CSS architecture needs restructuring. If you need more than 3 levels, consider flattening with BEM-style class names.
Shorthand vs longhand
Use shorthand properties when setting all related values. Use longhand when setting one specific value to avoid accidentally resetting others.
/* Good: setting all sides */
.card {
margin: 16px 24px;
padding: 12px 16px 12px 16px;
}
/* Good: overriding one side */
.card-featured {
margin-top: 32px; /* Only change top, leave others alone */
}
/* Risky: shorthand resets unset values */
.element {
background: url('bg.png'); /* This resets background-color, background-size, etc. */
}
Formatting tools
Prettier
Prettier is the most popular opinionated formatter. It handles CSS, SCSS, and Less with zero configuration:
{
"singleQuote": true,
"tabWidth": 2,
"printWidth": 80
}
Prettier makes formatting decisions for you: spacing, line breaks, quote style. The benefit is that your team never debates formatting. The trade-off is that you cannot customize every detail.
Stylelint
Stylelint is a linter, not a formatter. It catches errors and enforces conventions that formatters cannot:
{
"rules": {
"declaration-block-no-duplicate-properties": true,
"max-nesting-depth": 3,
"selector-max-id": 0,
"color-named": "never",
"order/properties-order": ["position", "display", "width"]
}
}
The best setup is Prettier for formatting and Stylelint for catching logical issues. They complement each other.
Using both together
npm install -D prettier stylelint stylelint-config-standard stylelint-config-prettier
The stylelint-config-prettier package disables Stylelint rules that conflict with Prettier, so they work together without fighting.
Minification for production
Formatted CSS is for development. Production CSS should be minified. Modern build tools handle this automatically:
// PostCSS with cssnano (most common)
// postcss.config.js
module.exports = {
plugins: [
require('autoprefixer'),
require('cssnano')({ preset: 'default' }),
],
};
Minification removes whitespace, comments, and shortens values. A 50 KB formatted stylesheet might minify to 35 KB, and further compress to 8 KB with gzip.
/* Before: 847 bytes */
.card {
display: flex;
flex-direction: column;
padding: 16px;
background-color: #ffffff;
border-radius: 8px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
/* After minification: 134 bytes */
.card{display:flex;flex-direction:column;padding:16px;background-color:#fff;border-radius:8px;box-shadow:0 1px 3px rgba(0,0,0,.1)}
Notice that #ffffff became #fff and 0.1 became .1. Good minifiers optimize values, not just whitespace.
Summary
Consistent CSS formatting is not about aesthetics -- it is about maintainability. Choose a property ordering convention, limit nesting depth, automate formatting with Prettier, catch issues with Stylelint, and minify for production. These practices cost nothing to set up and pay dividends as your stylesheets grow.
Try our CSS Beautifier to format and clean up your CSS instantly -- right in your browser, no upload required.