About sass-best-practices
A practical guide to indented-syntax Sass and maintainable stylesheet development for developers. It covers modular organization, naming, nesting, performance, and code style.
Key features
- 01Indented syntax conventions
- 02Stylesheet organization guidance
- 03Variables, mixins, and functions
- 04BEM naming and nesting guidance
- 05Module system and performance tips
Use cases
- 01Set up a Sass project structure
- 02Align a team's stylesheet conventions
- 03Build reusable responsive mixins
- 04Review Sass nesting and module organization
Install
Copy the command and run it in a terminal.
$
npx skills add https:/ / github.com/ mindrally/ skills --skill sass-best-practicesAfter installing, type/sass-best-practicesin Claude Code to confirm it loaded.
Details
From the project READMESass 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 (indented syntax)
.button
display: inline-flex
padding: 8px 16px
background: #3498db
&:hover
background: darken(#3498db, 10%)
// SCSS syntax (for comparison)
.button {
display: inline-flex;
padding: 8px 16px;
background: #3498db;
&:hover {
background: darken(#3498db, 10%);
}
}
File Organization
Project Structure
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
// 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
// _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
// 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
// _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
=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
=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
=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
=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
// 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
// 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
.component
// Pseudo-elements
&::before,
&::after
content: ''
// States
&:hover,
&:focus,
&:active
// State styles
// Modifiers
&--variant
// Modifier styles
// Media queries
+respond-to("md")
// Responsive styles
Functions
// _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
// 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
// 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
$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
=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
// _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
.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
Comments
Markdown supported