SCSS Best Practices
When to Use
Use this when writing SCSS for Bootstrap customization. Apply these patterns to prevent technical debt and maintain upgrade compatibility.
Philosophy
Work WITH Bootstrap's architecture, not against it. These practices prevent technical debt, maintain upgrade compatibility, and ensure predictable CSS output.
Core SCSS Principles
Variable Scoping
- Global variables - Define at root level for design system tokens
- Local variables - Define within mixins/functions for calculations
- WHY: Proper scoping prevents naming collisions and makes refactoring safer
Nesting Limits (CRITICAL)
Maximum 2-3 nesting levels:
// ❌ WRONG: Excessive nesting
.navbar {
.nav {
.nav-item {
.nav-link {
&:hover {
color: $primary; // 5 levels = disaster
}
}
}
}
}
// ✅ CORRECT: Limited nesting
.navbar {
.nav-link {
&:hover {
color: $primary; // 3 levels = manageable
}
}
}
WHY excessive nesting fails:
- Overly specific CSS (specificity 0,0,4,1)
- Performance issues (browser selector matching overhead)
- Impossible to override without !important
- SCSS compile performance degradation
Module System (Modern SCSS)
// ❌ OLD: @import (deprecated)
@import "bootstrap/scss/functions";
@import "bootstrap/scss/variables";
// ✅ NEW: @use with namespacing
@use "bootstrap/scss/functions" as bs-fn;
@use "bootstrap/scss/variables" as bs-var;
.component {
padding: bs-fn.map-get(bs-var.$spacers, 3);
}
WHY @use is required: - No global scope pollution - Performance improvement (faster compilation) - Prevents naming conflicts - @import is deprecated (will be removed)
Mixin Usage
// ❌ WRONG: Mixin without arguments
@mixin button-base {
padding: 0.5rem 1rem;
border: none;
}
// ✅ CORRECT: Mixin WITH arguments
@mixin button-variant($bg, $color, $hover-bg) {
background: $bg;
color: $color;
&:hover {
background: $hover-bg;
}
}
// ✅ CORRECT: Use %placeholder for static styles
%button-base {
padding: 0.5rem 1rem;
border: none;
}
.btn-primary { @extend %button-base; }
WHY: Mixins without arguments duplicate CSS in every output location. Use mixins ONLY when arguments provide dynamic functionality.
Bootstrap Integration Patterns
✅ DO
| Pattern | Code |
|---|---|
| Use Bootstrap variables | padding: $btn-padding-y $btn-padding-x; |
| Use Bootstrap mixins | @include button-variant($bg, $border, $color); |
| Extend Bootstrap maps | $spacers: map-merge($spacers, ("3xs": 2px)); |
❌ DON'T
| Anti-Pattern | Why It Fails |
|---|---|
@extend .btn |
Selector explosion, specificity chaos |
!important everywhere |
Specificity wars, maintenance nightmare |
| Hardcode Bootstrap values | Breaks with Bootstrap updates |
| Nesting >3 levels | Specificity issues, performance overhead |
Critical Anti-Patterns
Never @extend Bootstrap Classes
// ❌ WRONG
.custom-button {
@extend .btn;
@extend .btn-primary;
}
// ✅ CORRECT
.custom-button {
padding: $btn-padding-y $btn-padding-x;
background: $primary;
border-radius: $btn-border-radius;
}
WHY @extend is dangerous: 1. Selector explosion (combinatorial nightmare) 2. Unpredictable specificity changes 3. Bootstrap class pollution 4. Maintenance nightmare 5. Performance issues 6. Debugging hell
If you MUST use @extend: Limit to %placeholder selectors ONLY, never actual CSS classes.
Never Use !important
// ❌ WRONG
.component {
color: #0066cc !important;
padding: 1rem !important;
}
// ✅ CORRECT
.component {
color: $primary;
padding: $spacer;
}
WHY !important is dangerous: 1. Specificity wars 2. Maintenance nightmare 3. Impossible to override 4. Code smell 5. Team collaboration issues
ONLY acceptable use: Utility classes designed to override everything (proactive, not reactive).
Never Hardcode Bootstrap Values
// ❌ WRONG
.component {
padding: 0.375rem 0.75rem;
color: #0d6efd;
font-weight: 700;
}
// ✅ CORRECT
.component {
padding: $btn-padding-y $btn-padding-x;
color: $primary;
font-weight: $font-weight-bold;
}
Common Mistakes
- Wrong: Using @extend on Bootstrap classes → Right: Use Bootstrap variables and mixins
- Wrong: 5+ nesting levels → Right: Maximum 2-3 nesting levels
- Wrong: @import for Bootstrap modules → Right: @use with namespacing
- Wrong: Mixins without arguments → Right: Use %placeholders for static styles