// Define variables with $
$primary-color: #3498db;
$font-stack: 'Helvetica Neue', sans-serif;
$base-spacing: 1rem;
$border-radius: 4px;
// Use variables
.button {
background-color: $primary-color;
font-family: $font-stack;
padding: $base-spacing;
border-radius: $border-radius;
}
// Default values (can be overridden)
$theme-color: blue !default;
// Variable scoping
.container {
$local-var: 10px; // only available in this block
padding: $local-var;
}
// Nest selectors to mirror HTML structure
nav {
background: #333;
ul {
list-style: none;
margin: 0;
}
li {
display: inline-block;
}
a {
color: white;
text-decoration: none;
&:hover { // & references parent selector
color: #3498db;
}
}
}
// Compiles to:
// nav { background: #333; }
// nav ul { list-style: none; margin: 0; }
// nav li { display: inline-block; }
// nav a { color: white; text-decoration: none; }
// nav a:hover { color: #3498db; }
// Parent selector variations
.button {
&.primary { } // .button.primary
&-icon { } // .button-icon (BEM style)
.sidebar & { } // .sidebar .button
}
// Partials start with underscore: _variables.scss, _mixins.scss
// They won't compile to separate CSS files
// Import partials (underscore and extension optional)
@use 'variables';
@use 'mixins';
// Access imported members with namespace
.button {
color: variables.$primary-color;
@include mixins.flex-center;
}
// Change namespace
@use 'variables' as vars;
@use 'mixins' as *; // no namespace needed
// Forward for library authors
@forward 'variables';
@forward 'mixins';
// Legacy @import (deprecated but still works)
@import 'variables';
@import 'mixins';
// Define a mixin
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
// Use a mixin
.container {
@include flex-center;
}
// Mixin with parameters
@mixin button($bg-color, $text-color: white) {
background-color: $bg-color;
color: $text-color;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn-primary {
@include button(#3498db);
}
.btn-danger {
@include button(#e74c3c, #fff);
}
// Mixin with content block
@mixin media($breakpoint) {
@if $breakpoint == mobile {
@media (max-width: 767px) { @content; }
} @else if $breakpoint == tablet {
@media (max-width: 1023px) { @content; }
} @else if $breakpoint == desktop {
@media (min-width: 1024px) { @content; }
}
}
.sidebar {
width: 300px;
@include media(mobile) {
width: 100%;
}
}
// Define a function
@function calculate-rem($pixels) {
@return $pixels / 16 * 1rem;
}
// Use the function
.heading {
font-size: calculate-rem(24); // 1.5rem
}
// Function with multiple parameters
@function color-shade($color, $percent) {
@return mix(black, $color, $percent);
}
.dark-blue {
background: color-shade(#3498db, 20%);
}
// Built-in color functions
$color: #3498db;
lighten($color, 20%); // lighter shade
darken($color, 20%); // darker shade
saturate($color, 20%); // more saturated
desaturate($color, 20%); // less saturated
adjust-hue($color, 45deg); // shift hue
rgba($color, 0.5); // add transparency
mix($color1, $color2, 50%); // blend colors
complement($color); // complementary color
// Built-in number functions
percentage(0.5); // 50%
round(4.6); // 5
ceil(4.2); // 5
floor(4.8); // 4
abs(-10); // 10
min(1, 2, 3); // 1
max(1, 2, 3); // 3
// Built-in string functions
quote(hello); // "hello"
unquote("hello"); // hello
str-length("hello"); // 5
to-upper-case("hello"); // "HELLO"
to-lower-case("HELLO"); // "hello"
// Built-in list functions
$list: 1, 2, 3, 4, 5;
length($list); // 5
nth($list, 2); // 2
append($list, 6); // 1, 2, 3, 4, 5, 6
join($list1, $list2); // combined list
index($list, 3); // 3 (position)
// Base styles to extend
%button-base {
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 1rem;
}
// Extend the placeholder
.btn-primary {
@extend %button-base;
background: #3498db;
color: white;
}
.btn-secondary {
@extend %button-base;
background: #95a5a6;
color: white;
}
// Extend a class (less preferred)
.error {
border: 1px solid red;
color: red;
}
.critical-error {
@extend .error;
font-weight: bold;
}
// @if / @else if / @else
@mixin theme($mode) {
@if $mode == dark {
background: #333;
color: #fff;
} @else if $mode == light {
background: #fff;
color: #333;
} @else {
background: #f5f5f5;
color: #333;
}
}
// @for loop
@for $i from 1 through 5 {
.col-#{$i} {
width: 20% * $i;
}
}
// Generates .col-1 { width: 20% } through .col-5 { width: 100% }
// @each loop
$colors: (primary: #3498db, secondary: #2ecc71, danger: #e74c3c);
@each $name, $color in $colors {
.btn-#{$name} {
background-color: $color;
}
}
// Simple list iteration
$sizes: sm, md, lg;
@each $size in $sizes {
.text-#{$size} {
font-size: if($size == sm, 0.875rem, if($size == md, 1rem, 1.25rem));
}
}
// @while loop
$i: 1;
@while $i <= 3 {
.item-#{$i} {
width: 100px * $i;
}
$i: $i + 1;
}
// Define a map
$breakpoints: (
'sm': 576px,
'md': 768px,
'lg': 992px,
'xl': 1200px
);
// Access map values
$tablet: map-get($breakpoints, 'md'); // 768px
// Check if key exists
@if map-has-key($breakpoints, 'md') {
// key exists
}
// Iterate over map
@each $name, $value in $breakpoints {
.container-#{$name} {
max-width: $value;
}
}
// Map functions
map-keys($breakpoints); // ('sm', 'md', 'lg', 'xl')
map-values($breakpoints); // (576px, 768px, 992px, 1200px)
map-merge($map1, $map2); // combine maps
map-remove($map, 'key'); // remove a key
// Nested maps
$theme: (
colors: (
primary: #3498db,
secondary: #2ecc71
),
spacing: (
sm: 0.5rem,
md: 1rem
)
);
// Access nested values
$primary: map-get(map-get($theme, colors), primary);
// Arithmetic operators
$width: 100px + 50px; // 150px
$height: 200px - 50px; // 150px
$double: 100px * 2; // 200px
$half: 100px / 2; // Use math.div() in modern Sass
$remainder: 10 % 3; // 1
// Modern division (Sass 1.33+)
@use 'sass:math';
$half: math.div(100px, 2); // 50px
// Comparison operators
$a > $b; // greater than
$a < $b; // less than
$a >= $b; // greater or equal
$a <= $b; // less or equal
$a == $b; // equal
$a != $b; // not equal
// Logical operators
$condition1 and $condition2; // both true
$condition1 or $condition2; // either true
not $condition; // negation
// String concatenation
$family: 'Helvetica' + ', sans-serif'; // "Helvetica, sans-serif"
$class: 'btn-' + primary; // "btn-primary"
// Use #{} to insert variables into selectors, property names, or strings
$side: left;
$property: margin;
.box {
#{$property}-#{$side}: 10px; // margin-left: 10px
}
// In selectors
$component: button;
.#{$component} {
// .button { ... }
}
// In @media queries
$breakpoint: 768px;
@media (min-width: #{$breakpoint}) {
// ...
}
// In calc()
$spacing: 20px;
.container {
width: calc(100% - #{$spacing * 2});
}
// In URLs
$path: '../images';
.hero {
background: url('#{$path}/hero.jpg');
}
// 1. Organize with partials
// _variables.scss, _mixins.scss, _base.scss, _components.scss
@use 'variables';
@use 'mixins';
@use 'base';
@use 'components';
// 2. Keep nesting shallow (max 3-4 levels)
// ✓ Good
.nav {
&__item { }
&__link { }
}
// ✗ Avoid deep nesting
.nav {
ul {
li {
a {
span { } // Too deep!
}
}
}
}
// 3. Use variables for repeated values
$spacing-unit: 8px;
$primary-color: #3498db;
// 4. Prefer @use over @import (modern Sass)
@use 'sass:math';
@use 'variables' as vars;
// 5. Use placeholder selectors for @extend
%visually-hidden {
position: absolute;
clip: rect(0, 0, 0, 0);
}
.sr-only {
@extend %visually-hidden;
}