SASS (Syntactically Awesome Style Sheets) is a popular CSS preprocessor that adds power and elegance to basic CSS. It helps write maintainable, DRY (Don’t Repeat Yourself) code that compiles into standard CSS for browsers.
Core Concepts
Preprocessing:
You write code in a SASS syntax (SCSS or the older indented Sass syntax).
A SASS compiler processes this code.
Output: Standard CSS file that browsers can understand.
graph LR
A[Write SCSS/Sass Code] --> B(SASS Compiler);
B --> C[Generate Standard CSS];
C --> D{Browser Renders Styles};
Variables:
Store reusable values like colors, fonts, or spacing units.
Syntax: Use the $ symbol followed by the variable name.
Benefit: Easy updates – change the value in one place, and it reflects everywhere.
Mimic the structure of your HTML by nesting CSS selectors.
Benefit: Improves readability and organization by grouping related styles.
Example:
// SCSS with Nestingnav { ul { margin: 0; padding: 0; list-style: none; li { display: inline-block; a { text-decoration: none; color: $primary-color; // Using variable from above &:hover { // Use '&' to reference the parent selector text-decoration: underline; } } } }}
Compiled CSS Output:
nav ul { margin: 0; padding: 0; list-style: none;}nav ul li { display: inline-block;}nav ul li a { text-decoration: none; color: #3498db;}nav ul li a:hover { text-decoration: underline;}
Getting Started Checklist
Install a SASS compiler (e.g., Dart Sass via npm: npm install -g sass).
Create an SCSS file (e.g., styles.scss).
Write your styles using SASS features.
Compile your SCSS file to CSS: sass styles.scss styles.css.
Moving beyond the basics, SASS offers powerful tools for creating reusable code patterns and organizing large stylesheets effectively.
1. Partials and @import
Partials: SASS files named with a leading underscore (e.g., _variables.scss, _buttons.scss). They are not compiled into separate CSS files on their own.
@import: Used to include the contents of one SASS file (often a partial) into another.
Benefit: Break down large stylesheets into smaller, manageable, and modular files based on features or components. Note: Newer projects often prefer @use (see Advanced note).
// Import variables first@import 'variables';// Import base styles@import 'base';// Import components@import 'components/buttons';@import 'components/cards';// Styles specific to main.scssbody { background-color: #f0f0f0;}
2. Mixins (@mixin, @include)
@mixin: Defines a reusable block of styles, similar to a function in programming. Can accept arguments for flexibility.
@include: Includes the styles defined by a mixin into a selector.
Benefit: Perfect for applying common patterns (like vendor prefixes, complex backgrounds, or common layout structures) without duplicating code.
Example:
// Define a mixin for centering content@mixin flex-center($direction: row) { // Argument with a default value display: flex; justify-content: center; align-items: center; flex-direction: $direction;}// Define a mixin for transitions@mixin transition($property: all, $duration: 0.3s, $timing: ease) { transition: $property $duration $timing; -webkit-transition: $property $duration $timing; // Example vendor prefix}// Use the mixins.container { @include flex-center(column); // Use column direction}.button { @include flex-center; // Use default direction (row) @include transition(background-color); // Use specific property}
3. Extend (@extend)
@extend: Allows one selector to inherit the styles of another selector.
Benefit: Groups selectors that share the exact same style block in the final CSS, potentially reducing file size.
Placeholder Selectors (%): Selectors starting with % (e.g., %message-shared) are not output in the CSS unless they are extended. Ideal for creating base styles intended only for inheritance.
Caution with @extend: Overuse, especially across different files or with complex selectors, can sometimes lead to unexpected generated CSS and large selector groups. Mixins are often preferred for complex reusable patterns.
Advanced SASS features unlock programmatic capabilities, allowing for complex logic, custom calculations, and a more robust, modern approach to managing dependencies.
1. Control Directives (@if, @for, @each, @while)
These allow you to introduce logic directly into your stylesheets.
@if/@else if/@else: Conditionally apply styles based on expressions (often involving variables).
@for: Loop through a range of numbers to generate repetitive styles.
// Generate grid column classes@for $i from 1 through 12 { // 'through' is inclusive .col-#{$i} { // Use interpolation #{...} width: percentage($i / 12); }}
@each: Iterate over items in a list or map.
$colors: (primary: #007bff, success: #28a745, danger: #dc3545);@each $name, $color in $colors { .button-#{$name} { background-color: $color; &:hover { background-color: darken($color, 10%); // Using a built-in function } }}
@while: Repeat styles as long as a condition is true (less common than @for or @each).
2. Custom Functions (@function, @return)
Define your own reusable calculations or value manipulations.
Functions take arguments and must return a value using @return.
Example: Calculate REM values
$base-font-size: 16px; // Base size in pixels@function rem($pixels) { @if unitless($pixels) { // Assume pixels if no unit provided @return ($pixels / $base-font-size) * 1rem; } @else if unit($pixels) == 'px' { @return ($pixels / $base-font-size) * 1rem; } @else { @error "Function rem() expects a pixel value."; }}body { font-size: $base-font-size; // Sets the base for rem calculations}h1 { font-size: rem(32px); // Output: 2rem margin-bottom: rem(20); // Output: 1.25rem (unitless input assumed px)}
3. Modern Module System (@use, @forward)
This system is the recommended replacement for @import in modern SASS projects. It offers better control over scope and dependencies.
@use: Loads styles, functions, and variables from another module (file). Crucially, it namespaces the imported members by default (based on the filename). This prevents naming collisions.
graph LR
A[styles.scss] -- @use 'variables' as vars --> B[_variables.scss];
A -- @use 'components/button' --> C[_button.scss];
B -- defines $primary-color --> A;
C -- defines .button-base --> A;
subgraph Module Scope
direction LR
B --- |Namespace: variables| A;
C --- |Namespace: button| A;
end
_variables.scss:
$primary-color: #007bff;$base-padding: 10px;
_button.scss:
@use 'variables' as vars; // Load variables with namespace 'vars'.button { padding: vars.$base-padding; background-color: vars.$primary-color;}
main.scss:
@use 'variables' as v; // Use 'v' as namespace@use 'button'; // Use default 'button' namespacebody { padding: v.$base-padding * 2; // Access variable via namespace}// Styles from button module are included automatically
@forward: Makes members (variables, mixins, functions) from another module available to files that @use the current module. It acts like a “pass-through”. Often used in intermediary files (like _index.scss within a component folder) to expose specific members publicly.
_components/_index.scss:
@forward 'button'; // Make everything from _button.scss available@forward 'card' hide $internal-card-variable; // Forward card, but hide internal variable
main.scss:
@use 'components'; // Uses _components/_index.scss by default.my-button { @extend .button; // Can extend button class forwarded from index}
4. Built-in Modules
SASS provides powerful built-in modules (loaded via @use) for common tasks: