Sass Best Practices
You are an expert in Sass (the indented syntax), CSS architecture, and maintainable stylesheet development.
Key Principles
- •Write clean, readable Sass using the indented syntax (whitespace-sensitive)
- •Leverage Sass features to create DRY, modular stylesheets
- •Maintain consistent indentation as it defines code structure
- •Prioritize simplicity and clarity in style organization
Sass vs SCSS
Sass uses the original indented syntax:
- •No curly braces
{} - •No semicolons
; - •Indentation defines nesting
- •File extension:
.sass
sass
// Sass (indented syntax)
.button
display: inline-flex
padding: 8px 16px
background: #3498db
&:hover
background: darken(#3498db, 10%)
scss
// SCSS syntax (for comparison)
.button {
display: inline-flex;
padding: 8px 16px;
background: #3498db;
&:hover {
background: darken(#3498db, 10%);
}
}
File Organization
Project Structure
code
sass/ ├── abstracts/ │ ├── _variables.sass │ ├── _functions.sass │ ├── _mixins.sass │ └── _placeholders.sass ├── base/ │ ├── _reset.sass │ ├── _typography.sass │ └── _base.sass ├── components/ │ ├── _buttons.sass │ ├── _cards.sass │ └── _forms.sass ├── layout/ │ ├── _header.sass │ ├── _footer.sass │ └── _grid.sass ├── pages/ │ └── _home.sass ├── themes/ │ └── _default.sass ├── vendors/ │ └── _normalize.sass └── main.sass
Main Manifest
sass
// main.sass @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/footer' @use 'components/buttons' @use 'components/cards' @use 'components/forms' @use 'pages/home' @use 'themes/default'
Variables
Naming and Organization
sass
// _variables.sass // 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 Scale $spacing-unit: 8px $spacing-xs: $spacing-unit * 0.5 $spacing-sm: $spacing-unit $spacing-md: $spacing-unit * 2 $spacing-lg: $spacing-unit * 3 $spacing-xl: $spacing-unit * 4 $spacing-xxl: $spacing-unit * 6 // Breakpoints $breakpoint-sm: 576px $breakpoint-md: 768px $breakpoint-lg: 992px $breakpoint-xl: 1200px // Z-index Scale $z-index-dropdown: 1000 $z-index-sticky: 1020 $z-index-fixed: 1030 $z-index-modal: 1050 $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 // 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)
Using Maps
sass
// Define maps for grouped values
$colors: ("primary": #3498db, "secondary": #2ecc71, "danger": #e74c3c, "warning": #f39c12, "success": #27ae60)
$breakpoints: ("sm": 576px, "md": 768px, "lg": 992px, "xl": 1200px)
// Access values
.element
color: map-get($colors, "primary")
Mixins
Responsive Design
sass
// _mixins.sass
=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%
+respond-to("md")
width: 50%
+respond-to("lg")
width: 33.333%
Flexbox Utilities
sass
=flex-center display: flex align-items: center justify-content: center =flex-between display: flex align-items: center justify-content: space-between =flex-column display: flex flex-direction: column // Usage .container +flex-center min-height: 100vh
Typography
sass
=font-size($size, $line-height: null)
font-size: $size
@if $line-height
line-height: $line-height
=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
// Usage
.title
+font-size(2rem, 1.2)
.description
+truncate(3)
Accessibility
sass
=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
=focus-visible
&:focus-visible
outline: 2px solid $color-primary
outline-offset: 2px
// Usage
.sr-only
+visually-hidden
.interactive
+focus-visible
Button Styles
sass
=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
=button-variant($bg-color, $text-color: white)
+button-base
background: $bg-color
color: $text-color
&:hover:not(:disabled)
background: darken($bg-color, 10%)
&:active:not(:disabled)
background: darken($bg-color, 15%)
// Usage
.btn-primary
+button-variant($color-primary)
.btn-secondary
+button-variant($color-secondary)
.btn-danger
+button-variant($color-error)
BEM Naming Convention
sass
// Block
.card
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 Guidelines
Keep Nesting Shallow
sass
// BAD: Too deep
.nav
.nav-list
.nav-item
.nav-link
.nav-icon
// 5 levels - avoid this
// GOOD: Flat BEM structure
.nav
display: flex
.nav__list
display: flex
list-style: none
margin: 0
padding: 0
.nav__item
margin: 0 $spacing-sm
.nav__link
color: $color-text
text-decoration: none
&:hover,
&:focus
color: $color-primary
&--active
color: $color-primary
font-weight: $font-weight-bold
Acceptable Nesting
sass
.component
// Pseudo-elements
&::before,
&::after
content: ''
// States
&:hover,
&:focus,
&:active
// State styles
// Modifiers
&--variant
// Modifier styles
// Media queries
+respond-to("md")
// Responsive styles
Functions
sass
// _functions.sass // Color functions @function tint($color, $percentage) @return mix(white, $color, $percentage) @function shade($color, $percentage) @return mix(black, $color, $percentage) // Unit conversion @function px-to-rem($px, $base: 16) @return ($px / $base) * 1rem @function rem-to-px($rem, $base: 16) @return ($rem / 1rem) * $base * 1px // Spacing helper @function spacing($multiplier) @return $spacing-unit * $multiplier // Usage .element background: tint($color-primary, 20%) border-color: shade($color-primary, 10%) font-size: px-to-rem(18) padding: spacing(3)
Placeholders and Extend
sass
// Define placeholders
%clearfix
&::after
content: ''
display: table
clear: both
%list-reset
list-style: none
margin: 0
padding: 0
%button-reset
appearance: none
background: none
border: none
padding: 0
font: inherit
cursor: pointer
// Usage
.container
@extend %clearfix
.nav-list
@extend %list-reset
.icon-button
@extend %button-reset
Loops and Iteration
Generate Utility Classes
sass
// Spacing utilities
$directions: ("": "", "t": "-top", "r": "-right", "b": "-bottom", "l": "-left")
@each $abbr, $dir in $directions
@for $i from 0 through 8
.m#{$abbr}-#{$i}
margin#{$dir}: spacing($i)
.p#{$abbr}-#{$i}
padding#{$dir}: spacing($i)
// Color utilities
@each $name, $color in $colors
.text-#{$name}
color: $color
.bg-#{$name}
background-color: $color
Grid Generation
sass
$grid-columns: 12
@for $i from 1 through $grid-columns
.col-#{$i}
width: percentage($i / $grid-columns)
// Responsive columns
@each $bp, $width in $breakpoints
@media (min-width: $width)
@for $i from 1 through $grid-columns
.col-#{$bp}-#{$i}
width: percentage($i / $grid-columns)
Conditionals
sass
=theme-button($variant)
@if $variant == "primary"
background: $color-primary
color: white
@else if $variant == "secondary"
background: transparent
color: $color-primary
border: 2px solid $color-primary
@else if $variant == "danger"
background: $color-error
color: white
@else
background: $color-text-muted
color: white
.btn-primary
+theme-button("primary")
.btn-secondary
+theme-button("secondary")
Modern Module System
Using @use and @forward
sass
// _variables.sass $primary: #3498db // _mixins.sass @use 'variables' as vars =themed-element color: vars.$primary // _index.sass (barrel file) @forward 'variables' @forward 'mixins' // main.sass @use 'abstracts' .element +abstracts.themed-element
Performance Tips
- •Keep selector specificity low (prefer single class selectors)
- •Avoid
!importantexcept for utility overrides - •Use
@useinstead of deprecated@import - •Limit
@extendusage across files - •Compile without source maps in production
- •Let autoprefixer handle vendor prefixes
Code Style Guidelines
- •Use 2 spaces for indentation (critical in Sass)
- •Use single quotes for strings
- •One blank line between rule sets
- •Group related properties together
- •Comment non-obvious code
- •Use meaningful variable names
- •Keep lines under 80 characters when possible
Property Order
sass
.element // Positioning position: relative top: 0 right: 0 z-index: 10 // Display & Box Model display: flex width: 100% padding: $spacing-md margin: $spacing-sm 0 // Typography font-family: $font-family-base font-size: $font-size-base line-height: $line-height-base color: $color-text // Visual background: $color-background border: 1px solid $color-border border-radius: $border-radius-md box-shadow: $shadow-sm // Animation transition: all $transition-base // Misc cursor: pointer