Preprocessor Best Practices — Complete Guide
In this tutorial, you will learn about Preprocessor Best Practices. We cover key concepts, practical examples, and best practices to help you master this topic.
CSS preprocessor best practices cover naming conventions, file organization, nesting depth limits, mixin usage, performance optimization, and team conventions.
What You'll Learn
- Naming conventions for variables, mixins, and files
- Nesting depth and selector specificity
- When to use mixins vs extends
- File organization patterns
- Performance considerations
- Migration strategies
- Code review guidelines
Why It Matters
- Consistent practices improve team productivity
- Poor practices create unmaintainable code
- Performance issues from excessive CSS
- Standards enable automation and tooling
Real-World Use
- A team adopts a Sass style guide for consistency
- A code review checklist catches common issues
- A migration plan moves from Less to Sass
- A performance budget prevents bloat
flowchart LR A[Best Practices] --> B[Naming] A --> C[Structure] A --> D[Nesting] A --> E[Performance] B --> F[kebab-case, BEM] C --> G[7-1 pattern] D --> H[Max 3 levels] E --> I[Avoid over-abstraction]
Naming Conventions
Code Example: Variable and Mixin Naming
// GOOD: Descriptive, hierarchical naming
// Pattern: [context]-[property]-[variant]
$color-primary: #0066CC;
$color-primary-hover: #0052a3;
$color-secondary: #6c757d;
$color-success: #28a745;
$color-danger: #dc3545;
$spacing-xs: 4px;
$spacing-sm: 8px;
$spacing-md: 16px;
$spacing-lg: 24px;
$spacing-xl: 32px;
$font-size-xs: 0.75rem;
$font-size-sm: 0.875rem;
$font-size-base: 1rem;
$font-size-lg: 1.25rem;
$font-size-xl: 1.5rem;
$breakpoint-sm: 480px;
$breakpoint-md: 768px;
$breakpoint-lg: 1024px;
$breakpoint-xl: 1280px;
// BAD: Unclear, inconsistent
$blue: #0066CC;
$sp1: 4px;
$sp2: 8px;
$big: 1.25rem;
$bp1: 480px;
// GOOD: Mixin naming (verb + context)
@mixin respond-to($breakpoint) { }
@mixin button-variant($bg, $color) { }
@mixin visually-hidden() { }
@mixin truncate($max-width) { }
// BAD: Vague mixin names
@mixin stuff() { }
@mixin do-thing() { }
Expected output: Consistent naming conventions make code self-documenting. kebab-case for variables, verb-noun for mixins, hierarchical names that show context.
Nesting and Specificity
Code Example: Nesting Guidelines
// GOOD: Max 3 levels deep
.card {
background: #fff;
&__title {
font-size: 1.25rem;
}
&__text {
color: #666;
}
@media (min-width: 768px) {
display: flex;
}
}
// BAD: Deep nesting (6 levels)
.header {
.nav {
ul {
li {
a {
color: #fff; // .header .nav ul li a
// Specificity: 0,5,0 - very hard to override
}
}
}
}
}
// GOOD: Shallow nesting with BEM or utility classes
.header-nav {
a {
color: #fff;
}
}
// OR using utility classes
<nav class="header-nav">
<ul class="header-nav__list">
<li class="header-nav__item">
<a class="header-nav__link" href="#">Link</a>
</li>
</ul>
</nav>
// Specificity guidelines:
// - Max specificity: 0,3,0 (3 classes)
// - Avoid IDs (#id) in selectors
// - Avoid !important
// - Use BEM to keep specificity flat
Expected output: Nesting depth should not exceed 3 levels. BEM keeps specificity flat (0,1,0 per block). Deep nesting creates overly specific selectors that are hard to override.
Mixins vs Extends
Code Example: When to Use Each
// USE @EXTEND when:
// - Same static styles repeated in multiple places
// - No parameters needed
// - Styles do not change between uses
%flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.header { @extend %flex-center; }
.hero { @extend %flex-center; }
.footer { @extend %flex-center; }
// USE @MIXIN when:
// - Parameters customize the output
// - Content blocks needed (@content)
// - Same styles with variations
@mixin button-variant($bg, $color: #fff) {
background: $bg;
color: $color;
border: none;
border-radius: 4px;
&:hover {
background: darken($bg, 10%);
}
}
.btn-primary { @include button-variant(#0066CC); }
.btn-danger { @include button-variant(#dc3545); }
// USE PLACEHOLDER (%) for:
// - Base classes that should never render in output
// - Shared patterns without semantic meaning
// USE A CLASS for:
// - Styles that have semantic meaning
// - Patterns used in HTML directly
// AVOID:
// - @extend with deeply nested selectors (creates long selectors)
// - Mixins that output large amounts of static CSS (duplicates output)
// - Both when a simple utility class would work
Expected output: @extend for static shared styles (smaller output). @mixin for parameterized styles (flexible). Placeholder selectors (%) for abstract base patterns. Classes for semantically meaningful patterns used in HTML.
File Organization
Code Example: Recommended Structure
src/
styles/
abstracts/
_variables.scss // Design tokens
_mixins.scss // Reusable mixins
_functions.scss // Custom functions
_placeholders.scss // % placeholder selectors
base/
_reset.scss // CSS reset
_typography.scss // Fonts and type
_animations.scss // @keyframes
components/
_button.scss // Button component
_card.scss // Card component
_form.scss // Form elements
_nav.scss // Navigation
_modal.scss // Modal dialog
layout/
_header.scss // Header
_footer.scss // Footer
_grid.scss // Grid system
_sidebar.scss // Sidebar
pages/
_home.scss // Homepage-specific
_about.scss // About page
vendors/
_external.scss // Third-party overrides
main.scss // Entry point (only @use and @forward)
// main.scss - Entry point rules
// 1. Only @use statements (no actual CSS)
// 2. Order: abstracts -> vendors -> base -> layout -> components -> pages
@use 'abstracts/variables';
@use 'abstracts/mixins';
@use 'abstracts/functions';
@use 'vendors/external';
@use 'base/reset';
@use 'base/typography';
@use 'layout/grid';
@use 'layout/header';
@use 'layout/footer';
@use 'components/button';
@use 'components/card';
@use 'components/form';
@use 'pages/home';
// NO CSS rules in main.scss
// NO @import (use @use)
// Each partial exports its own styles
Expected output: Clear Separation Of Concerns. Abstracts (no CSS output), base (element styles), components (reusable), layout (page structure), pages (page-specific), vendors (third-party). Import order reflects the cascade.
Performance Considerations
Code Example: Performance Checklist
// 1. AVOID: Too many nested selectors
// BAD - 0,5,0 specificity:
.main .sidebar .widget .title a { }
// GOOD - 0,2,0 specificity:
.widget-title a { }
// 2. AVOID: Excessive mixin use (duplicates CSS)
// BAD - @mixin causes duplication:
@mixin hidden {
position: absolute;
overflow: hidden;
clip: rect(0,0,0,0);
width: 1px;
height: 1px;
}
// GOOD - @extend groups selectors:
%hidden {
position: absolute;
overflow: hidden;
clip: rect(0,0,0,0);
width: 1px;
height: 1px;
}
// 3. AVOID: Over-abstraction
// BAD - a mixin for EVERYTHING:
@mixin color($c) { color: $c; }
@mixin bg($c) { background: $c; }
@mixin p($v) { padding: $v; }
// GOOD - use variables and write CSS directly:
.element {
color: $color-primary;
background: $color-bg;
padding: $spacing-md;
}
// 4. AVOID: Unnecessary nesting
// BAD:
.card { &__title { & span { color: red; } } }
// GOOD:
.card__title span { color: red; }
// 5. AVOID: Large generated files from loops
// BAD: generates 1000 classes
@for $i from 1 through 1000 {
.mt-#{$i} { margin-top: #{$i}px; }
}
// GOOD: generate only what is needed
$spacing-scale: (0, 4px, 8px, 16px, 24px, 32px, 48px, 64px);
@each $value in $spacing-scale {
// ...
}
Expected output: Performance best practices reduce compiled CSS size, improve selector performance, and prevent bloat from over-abstraction.
Common Mistakes
- Over-abstraction — Creating a mixin/variable for everything makes code harder to read. Only abstract when you have 3+ repetitions.
- Deep nesting — More than 3 levels creates overly specific selectors and fragile CSS.
- Using @import instead of @use — @import is deprecated and causes variable conflicts. Use @use and @forward.
- No style guide — Every developer writes different code without conventions. Document naming and patterns.
- Not using Stylelint — Linting catches errors and enforces conventions automatically.
- Mixing naming conventions — camelCase in one file, kebab-case in another. Pick one and enforce it.
- Not reviewing compiled output — The compiled CSS may differ from expectations. Check it periodically.
Practice Questions
- What is the recommended maximum nesting depth in Sass? 3 levels. Deeper nesting creates overly specific selectors.
- When should you use @extend instead of @mixin? When you have static (non-parameterized) styles that are used in multiple places.
- What is the benefit of the 7-1 file organization pattern? Clear separation of concerns by layer (abstracts, base, components, layout, pages, themes, vendors).
- Name 3 things to check in a CSS preprocessor code review. Nesting depth, mixin vs extend usage, naming conventions, file organization, selector specificity.
FAQ
Mini Project
Audit an existing (or create a deliberately messy) Sass codebase against the best practices in this lesson. Create a checklist covering: (1) nesting depth under 3, (2) BEM or consistent naming, (3) @use over @import, (4) mixin vs extend usage is correct, (5) no over-abstraction, (6) specificity under 0,3,0, (7) file organization follows a pattern, (8) no unused variables or mixins, (9) compiled output size is reasonable, (10) Stylelint config is in place. Fix all issues found. Document the Refactoring decisions.
What's Next
Continue with Lesson 30: Preprocessor Project the final project for this module.
Built by the developers of DodaTech
Doda Browser, DodaZIP & Durga Antivirus Pro