SCSS Best Practices
You are an expert in SCSS (Sassy CSS), CSS architecture, and maintainable stylesheet development.
Key Principles
- •Write modular, reusable SCSS that scales with project complexity
- •Follow the DRY (Don't Repeat Yourself) principle using variables, mixins, and functions
- •Maintain clear separation between structure, skin, and state styles
- •Prioritize readability and maintainability over clever abstractions
File Organization
Architecture Pattern (7-1 Pattern)
code
scss/ ├── abstracts/ │ ├── _variables.scss # Global variables │ ├── _functions.scss # SCSS functions │ ├── _mixins.scss # Reusable mixins │ └── _placeholders.scss # Extendable placeholders ├── base/ │ ├── _reset.scss # CSS reset/normalize │ ├── _typography.scss # Typography rules │ └── _base.scss # Base element styles ├── components/ │ ├── _buttons.scss # Button components │ ├── _cards.scss # Card components │ └── _forms.scss # Form components ├── layout/ │ ├── _header.scss # Header layout │ ├── _footer.scss # Footer layout │ ├── _grid.scss # Grid system │ └── _navigation.scss # Navigation layout ├── pages/ │ ├── _home.scss # Home page specific │ └── _contact.scss # Contact page specific ├── themes/ │ └── _default.scss # Default theme ├── vendors/ │ └── _bootstrap.scss # Third-party overrides └── main.scss # Main manifest file
Import Order
scss
// main.scss @use 'abstracts/variables'; @use 'abstracts/functions'; @use 'abstracts/mixins'; @use 'abstracts/placeholders'; @use 'vendors/normalize'; @use 'base/reset'; @use 'base/typography'; @use 'base/base'; @use 'layout/grid'; @use 'layout/header'; @use 'layout/navigation'; @use 'layout/footer'; @use 'components/buttons'; @use 'components/cards'; @use 'components/forms'; @use 'pages/home'; @use 'themes/default';
Variables
Naming Convention
scss
// Use semantic, descriptive names // Format: $category-property-variant // Colors $color-primary: #3498db; $color-primary-light: lighten($color-primary, 15%); $color-primary-dark: darken($color-primary, 15%); $color-secondary: #2ecc71; $color-text: #333333; $color-text-muted: #666666; $color-background: #ffffff; $color-border: #e0e0e0; $color-error: #e74c3c; $color-success: #27ae60; $color-warning: #f39c12; // Typography $font-family-base: 'Helvetica Neue', Arial, sans-serif; $font-family-heading: 'Georgia', serif; $font-size-base: 1rem; $font-size-small: 0.875rem; $font-size-large: 1.25rem; $font-weight-normal: 400; $font-weight-bold: 700; $line-height-base: 1.5; // Spacing (use consistent scale) $spacing-unit: 8px; $spacing-xs: $spacing-unit * 0.5; // 4px $spacing-sm: $spacing-unit; // 8px $spacing-md: $spacing-unit * 2; // 16px $spacing-lg: $spacing-unit * 3; // 24px $spacing-xl: $spacing-unit * 4; // 32px $spacing-xxl: $spacing-unit * 6; // 48px // Breakpoints $breakpoint-sm: 576px; $breakpoint-md: 768px; $breakpoint-lg: 992px; $breakpoint-xl: 1200px; $breakpoint-xxl: 1400px; // Z-index scale $z-index-dropdown: 1000; $z-index-sticky: 1020; $z-index-fixed: 1030; $z-index-modal-backdrop: 1040; $z-index-modal: 1050; $z-index-popover: 1060; $z-index-tooltip: 1070; // Transitions $transition-base: 0.3s ease; $transition-fast: 0.15s ease; $transition-slow: 0.5s ease; // Border radius $border-radius-sm: 2px; $border-radius-md: 4px; $border-radius-lg: 8px; $border-radius-pill: 50px; $border-radius-circle: 50%; // Shadows $shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.05); $shadow-md: 0 4px 6px rgba(0, 0, 0, 0.1); $shadow-lg: 0 10px 15px rgba(0, 0, 0, 0.1); $shadow-xl: 0 20px 25px rgba(0, 0, 0, 0.15);
Maps for Related Values
scss
// Use maps for grouped values
$colors: (
'primary': #3498db,
'secondary': #2ecc71,
'danger': #e74c3c,
'warning': #f39c12,
'info': #17a2b8,
'success': #27ae60
);
$breakpoints: (
'sm': 576px,
'md': 768px,
'lg': 992px,
'xl': 1200px,
'xxl': 1400px
);
// Access with map-get
.element {
color: map-get($colors, 'primary');
}
Mixins
Responsive Breakpoints
scss
@mixin respond-to($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
@media (min-width: map-get($breakpoints, $breakpoint)) {
@content;
}
} @else {
@warn "Unknown breakpoint: #{$breakpoint}";
}
}
// Usage
.element {
width: 100%;
@include respond-to('md') {
width: 50%;
}
@include respond-to('lg') {
width: 33.333%;
}
}
Flexbox Utilities
scss
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin flex-between {
display: flex;
align-items: center;
justify-content: space-between;
}
@mixin flex-column {
display: flex;
flex-direction: column;
}
Typography
scss
@mixin font-size($size, $line-height: null) {
font-size: $size;
@if $line-height {
line-height: $line-height;
}
}
@mixin truncate($lines: 1) {
@if $lines == 1 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} @else {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
Accessibility
scss
@mixin visually-hidden {
position: absolute;
width: 1px;
height: 1px;
padding: 0;
margin: -1px;
overflow: hidden;
clip: rect(0, 0, 0, 0);
white-space: nowrap;
border: 0;
}
@mixin focus-visible {
&:focus-visible {
outline: 2px solid $color-primary;
outline-offset: 2px;
}
}
BEM Naming Convention
Structure
scss
// Block: Standalone component
// Element: Part of block (block__element)
// Modifier: Variant (block--modifier or block__element--modifier)
.card {
// Block styles
background: $color-background;
border-radius: $border-radius-md;
box-shadow: $shadow-md;
// Element
&__header {
padding: $spacing-md;
border-bottom: 1px solid $color-border;
}
&__title {
margin: 0;
font-size: $font-size-large;
font-weight: $font-weight-bold;
}
&__body {
padding: $spacing-md;
}
&__footer {
padding: $spacing-md;
border-top: 1px solid $color-border;
}
// Modifier
&--featured {
border: 2px solid $color-primary;
}
&--compact {
.card__header,
.card__body,
.card__footer {
padding: $spacing-sm;
}
}
}
Nesting Rules
Maximum Nesting Depth
scss
// BAD: Too deep nesting
.nav {
.nav-list {
.nav-item {
.nav-link {
.nav-icon {
// 5 levels deep - avoid this
}
}
}
}
}
// GOOD: Keep nesting to 3 levels maximum
.nav {
// Level 1
}
.nav__list {
// Level 1
}
.nav__item {
// Level 1
}
.nav__link {
color: $color-text;
&:hover,
&:focus {
// Level 2 - acceptable for states
color: $color-primary;
}
&--active {
// Level 2 - acceptable for modifiers
color: $color-primary;
font-weight: $font-weight-bold;
}
}
Acceptable Nesting
scss
.component {
// Direct child pseudo-elements
&::before,
&::after {
content: '';
}
// State modifiers
&:hover,
&:focus,
&:active {
// State styles
}
// BEM modifiers
&--variant {
// Modifier styles
}
// Media queries
@include respond-to('md') {
// Responsive styles
}
}
Functions
Color Functions
scss
@function tint($color, $percentage) {
@return mix(white, $color, $percentage);
}
@function shade($color, $percentage) {
@return mix(black, $color, $percentage);
}
// Usage
.element {
background: tint($color-primary, 20%);
border-color: shade($color-primary, 10%);
}
Unit Conversion
scss
@function px-to-rem($px, $base: 16) {
@return ($px / $base) * 1rem;
}
@function rem-to-px($rem, $base: 16) {
@return ($rem / 1rem) * $base * 1px;
}
// Usage
.element {
font-size: px-to-rem(18); // 1.125rem
padding: px-to-rem(24); // 1.5rem
}
Spacing Function
scss
@function spacing($multiplier) {
@return $spacing-unit * $multiplier;
}
// Usage
.element {
margin-bottom: spacing(2); // 16px
padding: spacing(3); // 24px
}
Extend and Placeholders
Use Placeholders Over Classes
scss
// Define placeholder
%button-base {
display: inline-flex;
align-items: center;
justify-content: center;
padding: $spacing-sm $spacing-md;
border: none;
border-radius: $border-radius-md;
font-family: inherit;
font-size: $font-size-base;
font-weight: $font-weight-bold;
text-decoration: none;
cursor: pointer;
transition: all $transition-base;
&:disabled {
opacity: 0.5;
cursor: not-allowed;
}
}
// Extend placeholder
.btn-primary {
@extend %button-base;
background: $color-primary;
color: white;
&:hover:not(:disabled) {
background: darken($color-primary, 10%);
}
}
.btn-secondary {
@extend %button-base;
background: transparent;
color: $color-primary;
border: 2px solid $color-primary;
&:hover:not(:disabled) {
background: $color-primary;
color: white;
}
}
Loops and Iteration
Generate Utility Classes
scss
// Spacing utilities
$spacing-directions: (
'': '',
't': '-top',
'r': '-right',
'b': '-bottom',
'l': '-left',
'x': '-inline',
'y': '-block'
);
@each $abbr, $direction in $spacing-directions {
@for $i from 0 through 8 {
.m#{$abbr}-#{$i} {
margin#{$direction}: spacing($i);
}
.p#{$abbr}-#{$i} {
padding#{$direction}: spacing($i);
}
}
}
// Color utilities
@each $name, $color in $colors {
.text-#{$name} {
color: $color;
}
.bg-#{$name} {
background-color: $color;
}
.border-#{$name} {
border-color: $color;
}
}
Performance Best Practices
- •Avoid overly specific selectors; aim for specificity of 0-1-0 (single class)
- •Never use
!importantexcept for utility classes - •Minimize use of
@extendacross files (can cause bloat) - •Use
@useand@forwardinstead of@import(deprecated) - •Compile with source maps in development, without in production
- •Use autoprefixer for vendor prefixes instead of manual prefixes
Modern SCSS Features
Module System
scss
// _variables.scss
$primary: #3498db;
// _mixins.scss
@use 'variables' as vars;
@mixin themed-button {
background: vars.$primary;
}
// main.scss
@use 'mixins';
.button {
@include mixins.themed-button;
}
Built-in Modules
scss
@use 'sass:math';
@use 'sass:color';
@use 'sass:list';
@use 'sass:map';
@use 'sass:string';
.element {
width: math.div(100%, 3);
background: color.adjust($color-primary, $lightness: 10%);
}
Code Style
- •Use 2 spaces for indentation
- •Use single quotes for strings
- •Add a space after colons in declarations
- •Add a space before opening braces
- •Put closing braces on new lines
- •Separate rule sets with blank lines
- •Order properties logically (positioning, box model, typography, visual, misc)
- •Comment complex calculations and non-obvious code