CSS Preprocessors Overview (SASS/SCSS)
Learning Objectives
- Master CSS styling techniques
- Control visual presentation
- Create attractive designs
- Understand CSS best practices
What Are CSS Preprocessors?
CSS preprocessors are scripting languages that extend the capabilities of standard CSS, allowing you to write more maintainable, reusable, and organized code. Think of CSS preprocessors as the difference between using a basic calculator versus a scientific calculator—both can add numbers, but one offers powerful features that make complex operations simpler.
The Problem With Native CSS
While CSS is powerful, it has limitations when building large-scale websites:
- No Variables: In standard CSS, you must repeat values like colors and font sizes throughout your stylesheets
- No Nesting: Selectors can't be nested, resulting in repetitive, verbose code
- No Modularity: It's difficult to split your CSS across multiple files without impacting performance
- Limited Calculations: Performing calculations for values like widths and padding is cumbersome
- No Functions: You can't create reusable code blocks for common patterns
CSS preprocessors solve these problems by providing programming-like features that compile down to standard CSS that browsers can understand.
CSS Preprocessor Workflow
flowchart LR
A["Preprocessor Files
.scss, .less, .styl"] --> B["Preprocessor
Compiler"]
B --> C["Standard CSS"]
C --> D["Browser Rendering"]
style A fill:#e6f3ff,stroke:#333,stroke-width:2px
style B fill:#f9f,stroke:#333,stroke-width:3px
style C fill:#bbf,stroke:#333,stroke-width:2px
style D fill:#e6ffe6,stroke:#333,stroke-width:2px
Popular CSS Preprocessors
| Feature | SASS/SCSS | LESS | Stylus | PostCSS |
|---|---|---|---|---|
| Syntax | SASS: Indented SCSS: CSS-like |
CSS-like | Flexible, whitespace-sensitive | CSS with plugins |
| Variables | $variable | @variable | variable = | With plugins |
| Adoption | Very High | Medium | Low | High |
| Learning Curve | Moderate | Easy | Moderate | Depends on plugins |
| Language Type | Ruby-based | JavaScript-based | JavaScript-based | JavaScript-based |
For this lecture, we'll focus on SASS/SCSS as it's the most widely adopted CSS preprocessor and is commonly used in WordPress theme development.
SASS/SCSS: The Most Popular CSS Preprocessor
SASS (Syntactically Awesome Style Sheets) is a scripting language that compiles to CSS. It exists in two syntaxes: the original indented syntax (SASS) and the newer SCSS (Sassy CSS) syntax. SCSS is CSS-compatible, meaning any valid CSS is also valid SCSS.
SASS vs SCSS Syntax
SASS (Indented Syntax)
// SASS syntax
$primary-color: #3a86ff
$secondary-color: #ff006e
.container
width: 100%
max-width: 1200px
margin: 0 auto
.header
background-color: $primary-color
color: white
padding: 20px
h1
font-size: 2em
margin: 0
SCSS (Sassy CSS Syntax)
// SCSS syntax
$primary-color: #3a86ff;
$secondary-color: #ff006e;
.container {
width: 100%;
max-width: 1200px;
margin: 0 auto;
.header {
background-color: $primary-color;
color: white;
padding: 20px;
h1 {
font-size: 2em;
margin: 0;
}
}
}
SCSS is the more popular syntax today because:
- It looks like standard CSS, making the learning curve easier
- You can copy/paste existing CSS into SCSS files
- Its syntax aligns better with other programming languages
We'll use SCSS syntax throughout this lecture, but the concepts apply to both formats.
Brief History
- 2006: Hampton Catlin designed SASS as a markup language preprocessor
- 2010: SCSS syntax was introduced in SASS 3, making adoption easier
- 2012: Integration with popular frameworks like Compass began
- 2014: LibSass emerged, allowing compilation in languages beyond Ruby
- 2016: SASS became an integral part of most front-end build systems
- 2020: Dart Sass became the primary implementation, replacing Ruby Sass
Core SASS Features
SASS provides several powerful features that extend CSS's capabilities, allowing you to write more efficient, maintainable code.
Variables
Variables allow you to store information that you can reuse throughout your stylesheet. Think of variables as storage containers for values that you'll use repeatedly.
Basic Variable Usage
// Variables in SCSS
$primary-color: #3a86ff;
$secondary-color: #ff006e;
$base-font-size: 16px;
$heading-font: 'Montserrat', sans-serif;
$body-font: 'Open Sans', sans-serif;
$spacing-unit: 8px;
// Using variables
body {
font-family: $body-font;
font-size: $base-font-size;
line-height: 1.6;
}
h1, h2, h3, h4, h5, h6 {
font-family: $heading-font;
color: $primary-color;
margin-bottom: $spacing-unit * 3; // 24px
}
.button {
background-color: $secondary-color;
padding: $spacing-unit * 2; // 16px
color: white;
border-radius: 4px;
}
Advantages of Variables
- Central Management: Update values in one place
- Consistency: Ensure the same values are used throughout
- Meaningful Names: Use descriptive names instead of cryptic values
- Calculations: Perform operations using variables
Real-World Example: Theme Color System
// Define brand colors
$brand-primary: #3a86ff;
$brand-secondary: #ff006e;
$brand-tertiary: #ffbe0b;
// Create color variations
$brand-primary-light: lighten($brand-primary, 15%);
$brand-primary-dark: darken($brand-primary, 15%);
$brand-secondary-light: lighten($brand-secondary, 15%);
$brand-secondary-dark: darken($brand-secondary, 15%);
// Text colors based on background
$text-on-dark: white;
$text-on-light: #333;
// Usage in components
.header {
background-color: $brand-primary;
color: $text-on-dark;
}
.footer {
background-color: $brand-primary-dark;
color: $text-on-dark;
}
.button-primary {
background-color: $brand-primary;
color: $text-on-dark;
&:hover {
background-color: $brand-primary-dark;
}
}
.button-secondary {
background-color: $brand-secondary;
color: $text-on-dark;
&:hover {
background-color: $brand-secondary-dark;
}
}
WordPress Theme Integration
Variables are perfect for WordPress themes as they allow for easy customization:
// _variables.scss
// Theme colors - can be easily modified for different theme versions
$theme-primary: #3a86ff;
$theme-secondary: #ff006e;
$theme-background: #f9f9f9;
$theme-text: #333333;
// WordPress-specific spacing
$wp-admin-bar-height: 32px;
$content-width: 960px;
$sidebar-width: 300px;
// Breakpoints aligned with WordPress content width
$breakpoint-small: 600px; // Mobile devices
$breakpoint-medium: 960px; // Tablets & small desktops
$breakpoint-large: 1280px; // Desktops
Nesting
Nesting allows you to write selectors inside other selectors, creating a visual hierarchy that matches your HTML structure. It's like organizing a filing cabinet with folders and subfolders.
Basic Nesting
SCSS
nav {
background-color: #333;
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
a {
display: block;
padding: 10px 15px;
color: white;
text-decoration: none;
&:hover {
background-color: #555;
}
}
}
}
Compiled CSS
nav {
background-color: #333;
}
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav li {
display: inline-block;
}
nav li a {
display: block;
padding: 10px 15px;
color: white;
text-decoration: none;
}
nav li a:hover {
background-color: #555;
}
Advanced Nesting with Parent Selector
The & symbol is a special character that refers to the parent selector. It's
useful for creating pseudo-classes, modifiers, and more.
.button {
background-color: #3a86ff;
color: white;
padding: 10px 20px;
border-radius: 4px;
// Pseudo-classes
&:hover {
background-color: darken(#3a86ff, 10%);
}
&:focus {
outline: 2px solid rgba(58, 134, 255, 0.5);
}
// Modifiers using BEM naming convention
&--large {
padding: 15px 30px;
font-size: 1.2em;
}
&--small {
padding: 5px 10px;
font-size: 0.8em;
}
// Parent states affecting child elements
.disabled & {
opacity: 0.5;
cursor: not-allowed;
}
// Media queries
@media (max-width: 768px) {
display: block;
width: 100%;
}
}
This compiles to:
.button {
background-color: #3a86ff;
color: white;
padding: 10px 20px;
border-radius: 4px;
}
.button:hover {
background-color: #0065f2;
}
.button:focus {
outline: 2px solid rgba(58, 134, 255, 0.5);
}
.button--large {
padding: 15px 30px;
font-size: 1.2em;
}
.button--small {
padding: 5px 10px;
font-size: 0.8em;
}
.disabled .button {
opacity: 0.5;
cursor: not-allowed;
}
@media (max-width: 768px) {
.button {
display: block;
width: 100%;
}
}
Nesting Warning
While nesting is powerful, excessive nesting can lead to several problems:
- Specificity Issues: Deeply nested selectors create highly specific CSS rules
- Overly Coupled Code: Styles become tightly coupled to HTML structure
- Difficult Maintenance: Deep nesting can make styles harder to update
- Larger Output: Deeply nested selectors create larger CSS files
Best practice: Limit nesting to 3 levels deep when possible.
WordPress Integration Example
// WordPress comment styling with nesting
.comments-area {
margin-top: 40px;
border-top: 1px solid #eee;
.comments-title {
font-size: 24px;
margin-bottom: 20px;
}
.comment-list {
list-style: none;
padding: 0;
.comment {
padding: 20px 0;
border-bottom: 1px solid #eee;
.comment-author {
display: flex;
align-items: center;
.avatar {
margin-right: 15px;
border-radius: 50%;
}
.fn {
font-weight: bold;
}
}
.comment-metadata {
font-size: 12px;
color: #777;
margin: 5px 0 15px;
}
.comment-content {
p {
margin-bottom: 15px;
}
}
.reply {
margin-top: 10px;
a {
color: #3a86ff;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
// Nested comments (replies)
.children {
list-style: none;
padding-left: 40px;
margin-top: 20px;
}
}
}
}
Partials and Imports
Partials are smaller SCSS files that you can include in other SCSS files. This feature allows you to modularize your CSS and organize it into smaller, more maintainable chunks.
How Partials Work
Partials use a naming convention with a leading underscore (e.g., _variables.scss).
The underscore tells SASS not to compile this file into a separate CSS file.
You can then import these partials into your main SCSS file using the @import directive.
File Structure Example
scss/
├── main.scss
├── _variables.scss
├── _typography.scss
├── _buttons.scss
├── _forms.scss
└── _layout.scss
main.scss
// Import partials
@import 'variables';
@import 'typography';
@import 'buttons';
@import 'forms';
@import 'layout';
// Additional styles...
body {
background-color: $bg-color;
}
Advantages of Partials
- Organization: Split CSS into logical components
- Maintainability: Easier to locate and update specific styles
- Team Collaboration: Different team members can work on different files
- Performance: Compiles into a single CSS file, avoiding multiple HTTP requests
- Reusability: Import only what you need in specific contexts
WordPress Theme Partials Structure
scss/
├── main.scss /* Main file for importing all others */
├── abstracts/
│ ├── _variables.scss /* Theme variables */
│ ├── _functions.scss /* Custom functions */
│ ├── _mixins.scss /* Reusable mixins */
│ └── _placeholders.scss /* Extend placeholders */
├── base/
│ ├── _reset.scss /* CSS reset/normalize */
│ ├── _typography.scss /* Typography styles */
│ └── _helpers.scss /* Helper classes */
├── components/
│ ├── _buttons.scss /* Button styles */
│ ├── _navigation.scss /* Navigation menus */
│ ├── _sidebar.scss /* Sidebar styles */
│ ├── _forms.scss /* Form elements */
│ └── _widgets.scss /* WordPress widgets */
├── layouts/
│ ├── _header.scss /* Header styles */
│ ├── _footer.scss /* Footer styles */
│ ├── _grid.scss /* Grid system */
│ └── _blog.scss /* Blog layout */
├── pages/
│ ├── _home.scss /* Homepage styles */
│ ├── _about.scss /* About page */
│ └── _contact.scss /* Contact page */
├── plugins/
│ ├── _woocommerce.scss /* WooCommerce styles */
│ └── _acf.scss /* Advanced Custom Fields styles */
└── admin/
└── _editor.scss /* Gutenberg editor styles */
The main.scss file would import all these partials in the proper order:
/*!
Theme Name: My WordPress Theme
Theme URI: https://example.com/my-theme
Author: Your Name
Description: A custom WordPress theme
Version: 1.0.0
License: GNU General Public License v2 or later
*/
// Abstracts
@import 'abstracts/variables';
@import 'abstracts/functions';
@import 'abstracts/mixins';
@import 'abstracts/placeholders';
// Base
@import 'base/reset';
@import 'base/typography';
@import 'base/helpers';
// Layouts
@import 'layouts/grid';
@import 'layouts/header';
@import 'layouts/footer';
@import 'layouts/blog';
// Components
@import 'components/buttons';
@import 'components/navigation';
@import 'components/sidebar';
@import 'components/forms';
@import 'components/widgets';
// Pages
@import 'pages/home';
@import 'pages/about';
@import 'pages/contact';
// Plugins
@import 'plugins/woocommerce';
@import 'plugins/acf';
Mixins
Mixins allow you to define reusable blocks of CSS that you can include in other rules. Think of them as functions in programming languages that accept parameters and return a set of styles.
Basic Mixin Usage
// Defining a mixin
@mixin border-radius($radius) {
-webkit-border-radius: $radius;
-moz-border-radius: $radius;
border-radius: $radius;
}
// Using the mixin
.button {
@include border-radius(5px);
}
.avatar {
@include border-radius(50%);
}
This compiles to:
.button {
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
}
.avatar {
-webkit-border-radius: 50%;
-moz-border-radius: 50%;
border-radius: 50%;
}
Advanced Mixins
Mixins can include default parameters, conditional logic, and even other mixins:
// Mixin with default values and logic
@mixin button-style($bg-color: #3a86ff, $text-color: white, $padding: 10px) {
display: inline-block;
background-color: $bg-color;
color: $text-color;
padding: $padding $padding * 2;
border: none;
border-radius: 4px;
text-decoration: none;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
// Check if background is light and adjust text color
@if (lightness($bg-color) > 70%) {
color: #333;
}
&:hover {
background-color: darken($bg-color, 10%);
}
}
// Using the advanced mixin
.button-primary {
@include button-style(); // Uses defaults
}
.button-secondary {
@include button-style(#ff006e); // Custom background
}
.button-success {
@include button-style(#28a745, white, 15px); // All custom values
}
.button-light {
@include button-style(#f8f9fa); // Light background, dark text
}
Practical Mixins for Responsive Design
// Responsive breakpoints mixin
@mixin respond-to($breakpoint) {
@if $breakpoint == small {
@media (max-width: 576px) { @content; }
} @else if $breakpoint == medium {
@media (max-width: 768px) { @content; }
} @else if $breakpoint == large {
@media (max-width: 992px) { @content; }
} @else if $breakpoint == xlarge {
@media (max-width: 1200px) { @content; }
}
}
// Using the responsive mixin
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 15px;
@include respond-to(xlarge) {
max-width: 1140px;
}
@include respond-to(large) {
max-width: 960px;
}
@include respond-to(medium) {
max-width: 720px;
}
@include respond-to(small) {
max-width: 100%;
}
}
WordPress-Specific Mixins
// WordPress admin bar spacing mixin
@mixin admin-bar-spacing($top: 0) {
.admin-bar & {
top: calc(#{$top} + 32px);
@media screen and (max-width: 782px) {
top: calc(#{$top} + 46px);
}
}
}
// WordPress featured image aspect ratio mixin
@mixin featured-image-ratio($width: 16, $height: 9) {
position: relative;
overflow: hidden;
padding-top: percentage($height / $width);
img {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
}
// Usage
.site-header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: 100;
@include admin-bar-spacing();
}
.post-thumbnail {
@include featured-image-ratio();
// Different ratio for featured posts
&.featured {
@include featured-image-ratio(2, 1);
}
}
Extend/Inheritance
The @extend directive lets you share styles between selectors. It's like inheriting
properties in object-oriented programming.
Basic @extend Usage
// Base message style
.message {
padding: 10px;
border: 1px solid #ccc;
color: #333;
}
// Extended styles
.success {
@extend .message;
border-color: green;
color: green;
}
.error {
@extend .message;
border-color: red;
color: red;
}
.warning {
@extend .message;
border-color: orange;
color: orange;
}
This compiles to:
.message, .success, .error, .warning {
padding: 10px;
border: 1px solid #ccc;
color: #333;
}
.success {
border-color: green;
color: green;
}
.error {
border-color: red;
color: red;
}
.warning {
border-color: orange;
color: orange;
}
Placeholder Selectors
Placeholder selectors (using %) are special selectors that only
appear in the CSS when extended. They're ideal for creating reusable style patterns.
// Placeholder selector (won't be output to CSS unless extended)
%button-base {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 4px;
font-weight: bold;
text-decoration: none;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
}
// Button variations that extend the placeholder
.button-primary {
@extend %button-base;
background-color: #3a86ff;
color: white;
&:hover {
background-color: darken(#3a86ff, 10%);
}
}
.button-secondary {
@extend %button-base;
background-color: #ff006e;
color: white;
&:hover {
background-color: darken(#ff006e, 10%);
}
}
.button-outline {
@extend %button-base;
background-color: transparent;
border: 2px solid #3a86ff;
color: #3a86ff;
&:hover {
background-color: #3a86ff;
color: white;
}
}
This compiles to:
.button-primary, .button-secondary, .button-outline {
display: inline-block;
padding: 10px 20px;
border: none;
border-radius: 4px;
font-weight: bold;
text-decoration: none;
text-align: center;
cursor: pointer;
transition: all 0.3s ease;
}
.button-primary {
background-color: #3a86ff;
color: white;
}
.button-primary:hover {
background-color: #0065f2;
}
.button-secondary {
background-color: #ff006e;
color: white;
}
.button-secondary:hover {
background-color: #d80058;
}
.button-outline {
background-color: transparent;
border: 2px solid #3a86ff;
color: #3a86ff;
}
.button-outline:hover {
background-color: #3a86ff;
color: white;
}
@extend vs @mixin
Both @extend and @mixin help create reusable code, but they work differently:
| @extend | @mixin |
|---|---|
| Combines selectors in CSS output | Duplicates code in each selector |
| Results in smaller CSS file size | Can result in larger CSS file size |
| No parameters or arguments | Can accept parameters for customization |
| Maintains relationships in the cascade | Each instance is independent |
| Can lead to complex selectors | Creates predictable output |
When to use which:
-
Use @extend when:
- The styles truly represent an "is-a" relationship
- File size is a concern
- You don't need to customize the styles with parameters
-
Use @mixin when:
- You need to pass variables to customize the output
- You want to avoid long, complex selector chains
- You need conditional logic in your styles
WordPress Theme Integration
// WordPress-specific placeholders
%entry-meta {
font-size: 0.875rem;
color: #666;
margin-bottom: 1rem;
a {
color: inherit;
text-decoration: none;
&:hover {
color: $primary-color;
}
}
}
%block-padding {
padding: 2rem;
@media (max-width: 768px) {
padding: 1.5rem;
}
@media (max-width: 576px) {
padding: 1rem;
}
}
// Usage in WordPress templates
.post {
.entry-meta {
@extend %entry-meta;
}
}
.page {
.entry-meta {
@extend %entry-meta;
text-align: center;
}
}
.widget {
@extend %block-padding;
background-color: #f8f9fa;
}
.comment-body {
@extend %block-padding;
border: 1px solid #eee;
}
Functions and Operators
SASS provides built-in functions and allows you to perform operations on values. This gives you the power to calculate and manipulate values dynamically.
Built-in Functions
SASS includes numerous functions for working with colors, strings, numbers, and more:
Color Functions
// Color manipulation
$base-color: #3a86ff;
.light-variation {
// Lighten - makes a color lighter
color: lighten($base-color, 20%); // #9dbdff
}
.dark-variation {
// Darken - makes a color darker
color: darken($base-color, 20%); // #004cb3
}
.desaturated {
// Desaturate - reduces color saturation
color: desaturate($base-color, 30%); // #5379d6
}
.transparent {
// Rgba - adds transparency
background-color: rgba($base-color, 0.5); // rgba(58, 134, 255, 0.5)
}
.complement {
// Complement - gets the opposite color on the color wheel
color: complement($base-color); // #ff973a
}
Math Functions
// Math operations
$width: 100%;
$columns: 12;
$margin: 2%;
.column {
width: ($width / $columns) - ($margin * 2); // 4.33%
&.span-2 {
// Round - rounds to the nearest whole number
width: round(($width / ($columns / 2)) - ($margin * 2)); // 46%
}
&.span-3 {
// Floor - rounds down
width: floor(($width / ($columns / 3)) - ($margin * 2)); // 29%
}
&.span-4 {
// Ceil - rounds up
width: ceil(($width / ($columns / 4)) - ($margin * 2)); // 30%
}
}
String Functions
// String manipulation
$theme-name: "bootstrap";
$version: 5;
.theme-info {
// To-upper-case - converts to uppercase
content: to-upper-case($theme-name); // "BOOTSTRAP"
// Str-length - gets string length
font-size: str-length($theme-name) * 1px; // 9px
// String interpolation
font-family: "#{$theme-name}-v#{$version}"; // "bootstrap-v5"
}
List Functions
// List operations
$social-colors: (facebook: #3b5998, twitter: #1da1f2, instagram: #e1306c);
$spacing-sizes: 0 5px 10px 15px 20px;
.social-icon {
// Map-get - retrieves value from a map
&.facebook { color: map-get($social-colors, facebook); } // #3b5998
&.twitter { color: map-get($social-colors, twitter); } // #1da1f2
// Nth - retrieves value at position
padding: nth($spacing-sizes, 3); // 10px
// Length - gets list length
z-index: length($spacing-sizes); // 5
}
Custom Functions
You can create your own functions with @function to perform specific calculations:
// Pixels to REMs converter
@function rem($pixels, $context: 16) {
@return #{$pixels / $context}rem;
}
// Z-index management
$z-layers: (
modal: 9000,
overlay: 8000,
dropdown: 7000,
header: 6000,
footer: 5000
);
@function z($layer) {
@if not map-has-key($z-layers, $layer) {
@error "No z-index found for `#{$layer}` in $z-layers map.";
}
@return map-get($z-layers, $layer);
}
// Color contrast checker and adjustor
@function color-contrast($color) {
$luminance: 0.2126 * red($color) + 0.7152 * green($color) + 0.0722 * blue($color);
@return if($luminance > 165, #333, #fff);
}
// Usage
body {
font-size: rem(16); // 1rem
}
h1 {
font-size: rem(32); // 2rem
margin-bottom: rem(24); // 1.5rem
}
.modal {
z-index: z(modal); // 9000
}
.header {
z-index: z(header); // 6000
}
.button {
background-color: #3a86ff;
color: color-contrast(#3a86ff); // white
}
WordPress Grid System Function
// WordPress-specific column calculator
$content-width: 1200px;
$gutter: 30px;
@function col-width($columns, $total-columns: 12) {
@return calc((#{$content-width} - (#{$total-columns - 1} * #{$gutter})) / #{$total-columns} * #{$columns} + (#{$columns - 1} * #{$gutter}));
}
// WordPress layout with custom grid functions
.site-content {
max-width: $content-width;
margin: 0 auto;
display: flex;
flex-wrap: wrap;
gap: $gutter;
}
.content-area {
width: col-width(8); // Main content area
@media (max-width: 768px) {
width: 100%;
}
}
.widget-area {
width: col-width(4); // Sidebar
@media (max-width: 768px) {
width: 100%;
}
}
// Footer widgets
.footer-widgets {
display: flex;
flex-wrap: wrap;
gap: $gutter;
.widget {
width: col-width(3);
@media (max-width: 992px) {
width: col-width(6);
}
@media (max-width: 576px) {
width: 100%;
}
}
}
Control Directives
SASS includes control directives like @if, @for, @each, and @while
that allow you to create complex, dynamic styles with programmatic logic.
Conditional Logic with @if
// Theme mode variable
$theme-mode: 'light'; // or 'dark'
// Conditional styling based on theme mode
body {
@if $theme-mode == 'light' {
background-color: #fff;
color: #333;
} @else if $theme-mode == 'dark' {
background-color: #222;
color: #f5f5f5;
} @else {
// Fallback
background-color: #f8f9fa;
color: #333;
}
}
// Function with conditional logic
@function text-color($bg-color) {
@if (lightness($bg-color) > 60%) {
@return #333; // Dark text for light backgrounds
} @else {
@return #fff; // Light text for dark backgrounds
}
}
// Usage
.card {
background-color: #f8f9fa;
color: text-color(#f8f9fa); // #333
}
.button-primary {
background-color: #3a86ff;
color: text-color(#3a86ff); // #fff
}
Loops with @for, @each, and @while
// @for loop for generating grid classes
@for $i from 1 through 12 {
.col-#{$i} {
width: percentage($i / 12);
}
}
// @each loop for iterating over a list or map
$theme-colors: (
'primary': #3a86ff,
'secondary': #ff006e,
'success': #28a745,
'warning': #ffc107,
'danger': #dc3545
);
@each $name, $color in $theme-colors {
.bg-#{$name} {
background-color: $color;
color: text-color($color);
}
.text-#{$name} {
color: $color;
}
.border-#{$name} {
border-color: $color;
}
}
// @while loop example
$opacity: 1;
@while $opacity > 0 {
.opacity-#{$opacity * 100} {
opacity: $opacity;
}
$opacity: $opacity - 0.2;
}
WordPress Theme Integration with Control Directives
// WordPress-specific control directives
// Generate admin color scheme classes
$admin-colors: (
'blue': #2271b1,
'coffee': #c7a589,
'ectoplasm': #523f6d,
'midnight': #363b3f,
'ocean': #738e96,
'sunrise': #cf4944
);
@each $name, $primary in $admin-colors {
.admin-color-#{$name} {
// Admin bar style
#wpadminbar {
background-color: $primary;
}
// Admin menu style
#adminmenu,
#adminmenu .wp-submenu,
#adminmenuback,
#adminmenuwrap {
background-color: darken($primary, 10%);
}
}
}
// Generate editor block spacing classes
$spacings: (
'tiny': 0.5rem,
'small': 1rem,
'medium': 2rem,
'large': 3rem,
'huge': 5rem
);
@each $name, $size in $spacings {
.has-#{$name}-spacing {
margin-bottom: $size;
}
.has-#{$name}-padding {
padding: $size;
}
}
// Generate responsive visibility classes
$breakpoints: (
'small': 576px,
'medium': 768px,
'large': 992px,
'xlarge': 1200px
);
@each $name, $width in $breakpoints {
@media (max-width: $width) {
.hide-on-#{$name} {
display: none !important;
}
}
@media (min-width: $width) {
.show-on-#{$name} {
display: block !important;
}
}
}
Integrating SASS into WordPress Development
Now that we understand the core SASS features, let's explore how to integrate SASS into WordPress theme development workflow.
Setting Up Your Development Environment
Installing Node.js and NPM
Most modern WordPress theme development uses Node.js and NPM to manage dependencies and build processes. Here's how to set it up:
- Download and install Node.js from nodejs.org
- Open your terminal/command prompt and verify installation:
node -v npm -v - Navigate to your WordPress theme folder:
cd /path/to/wordpress/wp-content/themes/your-theme - Initialize a new NPM project:
npm init -y
Installing SASS and Build Tools
# Install development dependencies
npm install --save-dev sass node-sass gulp gulp-sass gulp-postcss autoprefixer cssnano gulp-sourcemaps gulp-rename browser-sync
These packages serve the following purposes:
- sass/node-sass: Compile SCSS to CSS
- gulp: Task runner for automation
- gulp-sass: Gulp plugin for SASS
- gulp-postcss: Gulp plugin for PostCSS
- autoprefixer: Adds vendor prefixes automatically
- cssnano: Minifies CSS output
- gulp-sourcemaps: Generates sourcemaps for debugging
- gulp-rename: Renames output files
- browser-sync: Live reloading for development
Creating a Gulp Configuration
Create a file named gulpfile.js in your theme's root directory:
// gulpfile.js
const gulp = require('gulp');
const sass = require('gulp-sass')(require('sass'));
const postcss = require('gulp-postcss');
const autoprefixer = require('autoprefixer');
const cssnano = require('cssnano');
const sourcemaps = require('gulp-sourcemaps');
const rename = require('gulp-rename');
const browserSync = require('browser-sync').create();
// Define file paths
const paths = {
styles: {
src: './sass/**/*.scss',
dest: './assets/css'
},
php: {
src: './**/*.php'
}
};
// Compile SASS
function styles() {
return gulp.src(paths.styles.src)
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(postcss([
autoprefixer(),
cssnano()
]))
.pipe(rename({
suffix: '.min'
}))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest(paths.styles.dest))
.pipe(browserSync.stream());
}
// Watch for changes
function watch() {
browserSync.init({
proxy: 'localhost/wordpress', // Change to your local development URL
notify: false
});
gulp.watch(paths.styles.src, styles);
gulp.watch(paths.php.src).on('change', browserSync.reload);
}
// Define tasks
exports.styles = styles;
exports.watch = watch;
exports.default = gulp.series(styles, watch);
WordPress Theme Folder Structure with SASS
your-theme/
├── assets/
│ ├── css/
│ │ └── main.min.css /* Compiled and minified CSS */
│ ├── js/
│ └── images/
├── inc/ /* Theme functions and features */
├── sass/ /* SASS source files */
│ ├── abstracts/
│ ├── base/
│ ├── components/
│ ├── layouts/
│ ├── pages/
│ └── main.scss /* Main SASS file */
├── template-parts/ /* Reusable template parts */
├── functions.php /* Theme functions */
├── index.php /* Main template */
├── style.css /* Theme information */
├── package.json /* NPM configuration */
└── gulpfile.js /* Gulp configuration */
Real-World WordPress Theme SCSS Structure
Abstracts Folder
The abstracts folder contains all SASS tools and helpers used across the project. These files don't output any CSS when compiled.
sass/abstracts/
├── _variables.scss /* Global variables */
├── _functions.scss /* Custom functions */
├── _mixins.scss /* Reusable mixins */
└── _placeholders.scss /* Placeholder selectors */
Example _variables.scss
// Theme identity
$theme-name: 'my-theme';
$theme-version: '1.0.0';
// Colors
$color-primary: #3a86ff;
$color-secondary: #ff006e;
$color-accent: #ffbe0b;
$color-success: #28a745;
$color-warning: #ffc107;
$color-danger: #dc3545;
$color-info: #17a2b8;
$color-dark: #333333;
$color-light: #f8f9fa;
$color-gray: #6c757d;
$body-bg: white;
$body-color: $color-dark;
$link-color: $color-primary;
$link-hover-color: darken($color-primary, 15%);
// Typography
$font-family-base: 'Open Sans', -apple-system, system-ui, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
$font-family-heading: 'Montserrat', $font-family-base;
$font-size-base: 16px;
$line-height-base: 1.6;
// Spacing
$spacer: 1rem;
$spacers: (
0: 0,
1: $spacer * 0.25, // 4px
2: $spacer * 0.5, // 8px
3: $spacer, // 16px
4: $spacer * 1.5, // 24px
5: $spacer * 3 // 48px
);
// Container
$container-max-width: 1200px;
$grid-gutter-width: 30px;
// WordPress specific
$content-width: 800px;
$sidebar-width: 300px;
// Breakpoints
$breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
);
// Z-index
$z-indices: (
dropdown: 1000,
sticky: 1020,
fixed: 1030,
modal-backdrop: 1040,
modal: 1050,
popover: 1060,
tooltip: 1070
);
Example _mixins.scss
// Responsive breakpoints mixin
@mixin breakpoint($point) {
@if map-has-key($breakpoints, $point) {
@media (min-width: map-get($breakpoints, $point)) {
@content;
}
} @else {
@media (min-width: $point) {
@content;
}
}
}
// Clearfix mixin
@mixin clearfix() {
&::after {
display: block;
content: "";
clear: both;
}
}
// Visually hidden but accessible content
@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;
}
// WordPress admin bar spacing
@mixin admin-bar-padding($top: 0) {
.admin-bar & {
padding-top: $top + 32px;
@media screen and (max-width: 782px) {
padding-top: $top + 46px;
}
}
}
Base Folder
The base folder contains the boilerplate code for the project. Here you'll find reset files, typography rules, and foundational styles.
sass/base/
├── _reset.scss /* CSS reset/normalize */
├── _typography.scss /* Typography rules */
├── _forms.scss /* Base form styles */
└── _accessibility.scss /* WordPress accessibility */
Example _typography.scss
// Base typography styles
body {
font-family: $font-family-base;
font-size: $font-size-base;
line-height: $line-height-base;
color: $body-color;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
// Headings
h1, h2, h3, h4, h5, h6,
.h1, .h2, .h3, .h4, .h5, .h6 {
font-family: $font-family-heading;
font-weight: 700;
line-height: 1.2;
margin-top: 0;
margin-bottom: $spacer;
color: $color-dark;
}
h1, .h1 { font-size: $font-size-base * 2.5; } // 40px
h2, .h2 { font-size: $font-size-base * 2; } // 32px
h3, .h3 { font-size: $font-size-base * 1.75; } // 28px
h4, .h4 { font-size: $font-size-base * 1.5; } // 24px
h5, .h5 { font-size: $font-size-base * 1.25; } // 20px
h6, .h6 { font-size: $font-size-base; } // 16px
// Responsive heading sizes
@include breakpoint(md) {
h1, .h1 { font-size: $font-size-base * 3; } // 48px
h2, .h2 { font-size: $font-size-base * 2.5; } // 40px
h3, .h3 { font-size: $font-size-base * 2; } // 32px
h4, .h4 { font-size: $font-size-base * 1.75; } // 28px
h5, .h5 { font-size: $font-size-base * 1.5; } // 24px
h6, .h6 { font-size: $font-size-base * 1.25; } // 20px
}
// Links
a {
color: $link-color;
text-decoration: none;
transition: color 0.3s ease;
&:hover {
color: $link-hover-color;
text-decoration: underline;
}
}
// Paragraphs
p {
margin-top: 0;
margin-bottom: $spacer;
}
// Lists
ul, ol {
margin-top: 0;
margin-bottom: $spacer;
padding-left: $spacer * 2;
}
// Blockquotes
blockquote {
margin: 0 0 $spacer;
padding: $spacer;
border-left: 4px solid $color-primary;
background-color: rgba($color-primary, 0.05);
p {
margin-bottom: 0;
}
cite {
display: block;
margin-top: $spacer * 0.5;
font-size: 0.875em;
color: $color-gray;
}
}
// Code
pre, code {
font-family: SFMono-Regular, Consolas, Liberation Mono, Menlo, monospace;
}
code {
padding: 0.2em 0.4em;
font-size: 0.875em;
background-color: rgba($color-gray, 0.1);
border-radius: 3px;
}
pre {
padding: $spacer;
margin-top: 0;
margin-bottom: $spacer;
overflow: auto;
background-color: $color-light;
border-radius: 4px;
code {
padding: 0;
background-color: transparent;
}
}
Components Folder
The components folder contains all reusable UI components, such as buttons, forms, navigation, etc.
sass/components/
├── _buttons.scss /* Button styles */
├── _card.scss /* Card component */
├── _navigation.scss /* Navigation menus */
├── _pagination.scss /* Pagination styles */
├── _comments.scss /* WordPress comments */
└── _widgets.scss /* WordPress widgets */
Example _buttons.scss
// Base button styles
.btn,
button,
input[type="button"],
input[type="reset"],
input[type="submit"] {
display: inline-block;
padding: $spacer * 0.5 $spacer;
font-size: 1rem;
font-weight: 600;
line-height: 1.5;
text-align: center;
text-decoration: none;
color: $color-light;
background-color: $color-primary;
border: 1px solid transparent;
border-radius: 4px;
cursor: pointer;
transition: all 0.3s ease;
&:hover, &:focus {
background-color: darken($color-primary, 10%);
text-decoration: none;
color: $color-light;
}
&:focus {
outline: 4px solid rgba($color-primary, 0.25);
outline-offset: 1px;
}
&:active {
transform: translateY(1px);
}
&:disabled, &.disabled {
opacity: 0.65;
cursor: not-allowed;
}
}
// Button variations
.btn-secondary {
background-color: $color-secondary;
&:hover, &:focus {
background-color: darken($color-secondary, 10%);
}
&:focus {
outline-color: rgba($color-secondary, 0.25);
}
}
.btn-success {
background-color: $color-success;
&:hover, &:focus {
background-color: darken($color-success, 10%);
}
&:focus {
outline-color: rgba($color-success, 0.25);
}
}
.btn-outline {
color: $color-primary;
background-color: transparent;
border-color: $color-primary;
&:hover, &:focus {
color: $color-light;
background-color: $color-primary;
}
}
// Button sizes
.btn-lg {
padding: $spacer * 0.75 $spacer * 1.5;
font-size: 1.25rem;
}
.btn-sm {
padding: $spacer * 0.25 $spacer * 0.5;
font-size: 0.875rem;
}
// Full width button
.btn-block {
display: block;
width: 100%;
}
Example _navigation.scss
// Main navigation
.main-navigation {
display: flex;
align-items: center;
.menu {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.menu-item {
position: relative;
a {
display: block;
padding: $spacer * 0.5 $spacer;
color: $color-dark;
text-decoration: none;
transition: color 0.3s ease;
&:hover {
color: $color-primary;
}
}
&.current-menu-item > a {
color: $color-primary;
font-weight: 600;
}
}
// Sub-menu (dropdown)
.sub-menu {
display: none;
position: absolute;
top: 100%;
left: 0;
min-width: 200px;
background-color: $color-light;
border: 1px solid rgba(0, 0, 0, 0.1);
border-radius: 4px;
padding: $spacer * 0.5;
z-index: map-get($z-indices, dropdown);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
.menu-item {
width: 100%;
a {
padding: $spacer * 0.5;
}
}
}
.menu-item-has-children {
&:hover > .sub-menu {
display: block;
}
> a {
&::after {
content: "";
display: inline-block;
width: 0;
height: 0;
margin-left: $spacer * 0.5;
border-top: 4px solid currentColor;
border-right: 4px solid transparent;
border-left: 4px solid transparent;
vertical-align: middle;
}
}
}
// Mobile navigation
@media (max-width: map-get($breakpoints, md)) {
.menu {
display: none;
&.toggled {
display: flex;
flex-direction: column;
position: absolute;
top: 100%;
left: 0;
width: 100%;
background-color: $color-light;
padding: $spacer;
border-top: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
}
.menu-toggle {
display: block;
}
.sub-menu {
position: static;
box-shadow: none;
border: none;
padding-left: $spacer;
}
}
// Hide menu toggle on desktop
.menu-toggle {
display: none;
}
}
Layouts Folder
The layouts folder contains SCSS files defining the main layout components of the site.
sass/layouts/
├── _header.scss /* Header styles */
├── _footer.scss /* Footer styles */
├── _sidebar.scss /* Sidebar styles */
├── _grid.scss /* Custom grid system */
└── _posts.scss /* Post layout styles */
Example _grid.scss
// Custom grid system for WordPress theme
.container {
width: 100%;
max-width: $container-max-width;
margin-right: auto;
margin-left: auto;
padding-right: $grid-gutter-width / 2;
padding-left: $grid-gutter-width / 2;
// Responsive container sizes
@each $breakpoint, $width in $breakpoints {
@if $width > 0 {
@include breakpoint($breakpoint) {
@if $width < $container-max-width {
max-width: $width - $grid-gutter-width;
}
}
}
}
}
.row {
display: flex;
flex-wrap: wrap;
margin-right: -$grid-gutter-width / 2;
margin-left: -$grid-gutter-width / 2;
}
// Column base class
[class^="col-"] {
position: relative;
width: 100%;
padding-right: $grid-gutter-width / 2;
padding-left: $grid-gutter-width / 2;
}
// Function to calculate column width
@function col-width($size, $columns: 12) {
@return percentage($size / $columns);
}
// Generate column classes for each breakpoint
@each $breakpoint, $width in $breakpoints {
$infix: if($width > 0, "-#{$breakpoint}", "");
@include breakpoint($width) {
@for $i from 1 through 12 {
.col#{$infix}-#{$i} {
flex: 0 0 col-width($i);
max-width: col-width($i);
}
}
// Offset classes
@for $i from 0 through 11 {
.offset#{$infix}-#{$i} {
@if $i > 0 {
margin-left: col-width($i);
} @else {
margin-left: 0;
}
}
}
}
}
Example _header.scss
// Site header styles
.site-header {
background-color: $color-light;
padding: $spacer 0;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
// Fixed header option
&.fixed-header {
position: fixed;
top: 0;
left: 0;
width: 100%;
z-index: map-get($z-indices, fixed);
// Add padding for WordPress admin bar
@include admin-bar-padding();
}
.container {
display: flex;
align-items: center;
justify-content: space-between;
flex-wrap: wrap;
}
// Site branding
.site-branding {
display: flex;
align-items: center;
margin-right: $spacer;
}
.site-title {
font-size: 1.5rem;
font-weight: 700;
margin: 0;
a {
color: $color-dark;
text-decoration: none;
&:hover {
color: $color-primary;
}
}
}
.site-description {
margin: 0;
font-size: 0.875rem;
color: $color-gray;
}
// Custom logo
.custom-logo-link {
margin-right: $spacer;
img {
max-height: 50px;
width: auto;
}
}
// Responsive styles
@media (max-width: map-get($breakpoints, md)) {
.container {
flex-direction: column;
align-items: flex-start;
}
.site-branding {
margin-bottom: $spacer;
}
.main-navigation {
width: 100%;
}
}
}
Pages Folder
The pages folder contains styles specific to individual pages.
sass/pages/
├── _home.scss /* Homepage styles */
├── _blog.scss /* Blog archive styles */
├── _single.scss /* Single post styles */
└── _404.scss /* 404 page styles */
Example _home.scss
// Homepage-specific styles
.home {
// Hero section
.hero-section {
background-color: $color-primary;
color: $color-light;
padding: $spacer * 3 0;
margin-bottom: $spacer * 3;
h1 {
font-size: 2.5rem;
color: $color-light;
margin-bottom: $spacer;
@include breakpoint(md) {
font-size: 3.5rem;
}
}
p {
font-size: 1.25rem;
margin-bottom: $spacer * 2;
max-width: 800px;
}
.btn {
margin-right: $spacer;
margin-bottom: $spacer;
}
}
// Featured posts section
.featured-posts {
margin-bottom: $spacer * 3;
.section-title {
text-align: center;
margin-bottom: $spacer * 2;
}
.post {
margin-bottom: $spacer * 2;
.entry-title {
font-size: 1.5rem;
}
}
}
// Call to action section
.cta-section {
background-color: $color-light;
padding: $spacer * 3 0;
text-align: center;
border-top: 1px solid rgba(0, 0, 0, 0.1);
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
margin-bottom: $spacer * 3;
h2 {
font-size: 2rem;
margin-bottom: $spacer;
}
p {
max-width: 700px;
margin: 0 auto $spacer * 2;
}
}
}
Main SCSS File
The main.scss file imports all other SCSS files in the correct order.
/*!
Theme Name: My WordPress Theme
Theme URI: https://example.com/my-theme
Author: Your Name
Author URI: https://example.com/
Description: A custom WordPress theme
Version: 1.0.0
License: GNU General Public License v2 or later
License URI: http://www.gnu.org/licenses/gpl-2.0.html
Text Domain: my-theme
*/
// Abstracts - no CSS output
@import "abstracts/variables";
@import "abstracts/functions";
@import "abstracts/mixins";
@import "abstracts/placeholders";
// Base
@import "base/reset";
@import "base/typography";
@import "base/forms";
@import "base/accessibility";
// Layouts
@import "layouts/grid";
@import "layouts/header";
@import "layouts/footer";
@import "layouts/sidebar";
@import "layouts/posts";
// Components
@import "components/buttons";
@import "components/card";
@import "components/navigation";
@import "components/pagination";
@import "components/comments";
@import "components/widgets";
// Pages
@import "pages/home";
@import "pages/blog";
@import "pages/single";
@import "pages/404";
Enqueueing Compiled CSS in WordPress
After compiling your SCSS to CSS, you need to properly enqueue the stylesheets in your WordPress theme.
How to Enqueue CSS in functions.php
get('Version');
// Enqueue main stylesheet (compiled from SASS)
wp_enqueue_style(
'mytheme-styles',
get_template_directory_uri() . '/assets/css/main.min.css',
array(),
$theme_version
);
// Add conditional stylesheets if needed
if (is_page_template('templates/landing-page.php')) {
wp_enqueue_style(
'mytheme-landing',
get_template_directory_uri() . '/assets/css/landing.min.css',
array('mytheme-styles'),
$theme_version
);
}
// WooCommerce styles (only if plugin is active)
if (class_exists('WooCommerce')) {
wp_enqueue_style(
'mytheme-woocommerce',
get_template_directory_uri() . '/assets/css/woocommerce.min.css',
array('mytheme-styles'),
$theme_version
);
}
}
add_action('wp_enqueue_scripts', 'mytheme_enqueue_styles');
/**
* Enqueue block editor assets
*/
function mytheme_editor_assets() {
// Get theme version
$theme_version = wp_get_theme()->get('Version');
// Editor styles
wp_enqueue_style(
'mytheme-editor-styles',
get_template_directory_uri() . '/assets/css/editor.min.css',
array(),
$theme_version
);
}
add_action('enqueue_block_editor_assets', 'mytheme_editor_assets');
Gulp Task for Separate Editor CSS
// Create editor styles from SASS
function editorStyles() {
return gulp.src('./sass/editor.scss')
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(postcss([
autoprefixer(),
cssnano()
]))
.pipe(rename({
suffix: '.min'
}))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./assets/css'));
}
// Add to your exports
exports.editorStyles = editorStyles;
exports.default = gulp.series(styles, editorStyles, watch);
Supporting WordPress Block Editor with SASS
For Gutenberg Block Editor support, create a separate SCSS file for editor styles:
// editor.scss
// Import shared variables and mixins
@import "abstracts/variables";
@import "abstracts/functions";
@import "abstracts/mixins";
// Import typography and base styles that should be available in the editor
@import "base/typography";
// Editor-specific styles
.wp-block {
max-width: $content-width;
&[data-align="wide"] {
max-width: $content-width * 1.2;
}
&[data-align="full"] {
max-width: none;
}
}
// Style the editor to match your theme
.editor-styles-wrapper {
font-family: $font-family-base;
color: $body-color;
h1, h2, h3, h4, h5, h6 {
font-family: $font-family-heading;
color: $color-dark;
}
// Style specific blocks to match front-end
.wp-block-quote {
border-left: 4px solid $color-primary;
padding-left: $spacer;
}
.wp-block-button {
.wp-block-button__link {
background-color: $color-primary;
color: $color-light;
&:hover {
background-color: darken($color-primary, 10%);
}
}
&.is-style-outline {
.wp-block-button__link {
background-color: transparent;
border: 1px solid $color-primary;
color: $color-primary;
&:hover {
background-color: $color-primary;
color: $color-light;
}
}
}
}
}
SASS Performance and Optimization
As your SASS codebase grows, it's important to consider performance and optimization best practices.
SASS Best Practices
Avoid Deep Nesting
Avoid This
// Excessive nesting
.header {
.navigation {
.nav-list {
.nav-item {
.nav-link {
// 5 levels deep!
&:hover {
color: red;
}
}
}
}
}
}
Use This Instead
// Flatter selectors with BEM
.nav-list {
// Styles...
}
.nav-item {
// Styles...
}
.nav-link {
// Styles...
&:hover {
color: red;
}
}
Use Variables for Repeated Values
Avoid This
// Hardcoded values
.header {
background-color: #3a86ff;
}
.button {
background-color: #3a86ff;
}
.footer {
border-top: 1px solid #3a86ff;
}
Use This Instead
// Variables for consistency
$color-primary: #3a86ff;
.header {
background-color: $color-primary;
}
.button {
background-color: $color-primary;
}
.footer {
border-top: 1px solid $color-primary;
}
Modularize Your Code
Split your SASS into logical modules using partials. This makes your codebase more maintainable and easier to navigate.
Optimize Compiled CSS
Use compression and autoprefixing in your build process to minimize the final CSS file size and ensure cross-browser compatibility.
// Optimization setup in gulpfile.js
function styles() {
return gulp.src('./sass/main.scss')
.pipe(sourcemaps.init())
.pipe(sass().on('error', sass.logError))
.pipe(postcss([
autoprefixer(),
cssnano({
preset: ['default', {
discardComments: {
removeAll: true
},
normalizeWhitespace: true
}]
})
]))
.pipe(rename({
suffix: '.min'
}))
.pipe(sourcemaps.write('./'))
.pipe(gulp.dest('./assets/css'));
}
Use Source Maps for Debugging
Source maps help you debug your SASS code by mapping the compiled CSS back to the original SCSS files.
Take Advantage of SASS Features
Leverage SASS features like functions, mixins, and extends to create reusable code patterns.
Advanced Optimization Techniques
Critical CSS
Extract critical CSS for above-the-fold content to improve initial page load performance.
// Install critical CSS package
// npm install --save-dev critical
const critical = require('critical');
// Generate critical CSS
function criticalCSS() {
return critical.generate({
base: './',
src: 'index.html',
target: {
css: 'assets/css/critical.min.css',
html: 'index-critical.html',
inline: true
},
width: 1300,
height: 900,
minify: true
});
}
CSS Code Splitting
Split your CSS into multiple files based on usage to avoid loading unnecessary styles.
// Create separate CSS bundles for different template types
function templateStyles() {
// Array of template-specific styles
const templates = [
'home',
'blog',
'shop',
'contact'
];
// Create a stream for each template
const streams = templates.map(template => {
return gulp.src(`./sass/templates/${template}.scss`)
.pipe(sass().on('error', sass.logError))
.pipe(postcss([autoprefixer(), cssnano()]))
.pipe(rename({ suffix: '.min' }))
.pipe(gulp.dest('./assets/css/templates'));
});
// Merge all streams
return merge(streams);
}
PurgeCSS for WordPress
Remove unused CSS from your final stylesheet to reduce file size.
// Install PurgeCSS
// npm install --save-dev gulp-purgecss
const purgecss = require('gulp-purgecss');
// PurgeCSS for production build
function purgeCSS() {
return gulp.src('./assets/css/main.min.css')
.pipe(purgecss({
content: [
'./**/*.php',
'./assets/js/**/*.js'
],
safelist: {
standard: [/^wp-/, /^has-/, /^is-/, /^align/, /^admin-/],
deep: [/^(has|is|wp)-.*$/],
greedy: [/^(wp-block-|woocommerce-|widget_)/]
}
}))
.pipe(rename({
suffix: '.purged'
}))
.pipe(gulp.dest('./assets/css'));
}
Additional Resources for SASS Development
Documentation and Learning
- Official SASS Documentation
- SassMeister (Online SASS Playground)
- CSS-Tricks: SASS Techniques from the Trenches
- SitePoint: Complete SASS Reference
WordPress SASS Resources
SASS Tools and Libraries
Practical Exercises
Let's apply what we've learned with some practical exercises for SASS implementation in WordPress.
Exercise 1: Convert CSS to SCSS
Take the following CSS code and convert it to well-organized SCSS using nesting, variables, and mixins.
Original CSS
/* Navigation styles */
.main-menu {
background-color: #333;
padding: 15px 0;
}
.main-menu ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
}
.main-menu li {
margin: 0 10px;
}
.main-menu a {
color: white;
text-decoration: none;
font-weight: bold;
padding: 5px 10px;
}
.main-menu a:hover {
background-color: #555;
border-radius: 3px;
}
/* Button styles */
.button {
display: inline-block;
padding: 10px 20px;
background-color: #2c7ad6;
color: white;
text-decoration: none;
border-radius: 4px;
font-weight: bold;
}
.button:hover {
background-color: #1c5fa8;
}
.button.secondary {
background-color: #6c757d;
}
.button.secondary:hover {
background-color: #5a6268;
}
.button.large {
padding: 15px 30px;
font-size: 18px;
}
.button.small {
padding: 5px 10px;
font-size: 14px;
}
/* Media queries */
@media (max-width: 768px) {
.main-menu ul {
flex-direction: column;
}
.main-menu li {
margin: 5px 0;
}
}
Converted SCSS Solution
// Variables
$color-dark: #333;
$color-dark-hover: #555;
$color-primary: #2c7ad6;
$color-primary-hover: #1c5fa8;
$color-secondary: #6c757d;
$color-secondary-hover: #5a6268;
$color-white: white;
$border-radius-small: 3px;
$border-radius-medium: 4px;
$font-size-small: 14px;
$font-size-base: 16px;
$font-size-large: 18px;
// Mixins
@mixin button-variant($bg-color, $hover-color) {
background-color: $bg-color;
color: $color-white;
&:hover {
background-color: $hover-color;
}
}
@mixin button-size($padding-x, $padding-y, $font-size) {
padding: $padding-y $padding-x;
font-size: $font-size;
}
// Breakpoint mixin
@mixin breakpoint($point) {
@if $point == medium {
@media (max-width: 768px) { @content; }
}
}
// Navigation styles
.main-menu {
background-color: $color-dark;
padding: 15px 0;
ul {
list-style: none;
margin: 0;
padding: 0;
display: flex;
@include breakpoint(medium) {
flex-direction: column;
}
}
li {
margin: 0 10px;
@include breakpoint(medium) {
margin: 5px 0;
}
}
a {
color: $color-white;
text-decoration: none;
font-weight: bold;
padding: 5px 10px;
&:hover {
background-color: $color-dark-hover;
border-radius: $border-radius-small;
}
}
}
// Button styles
.button {
display: inline-block;
text-decoration: none;
border-radius: $border-radius-medium;
font-weight: bold;
@include button-variant($color-primary, $color-primary-hover);
@include button-size(20px, 10px, $font-size-base);
&.secondary {
@include button-variant($color-secondary, $color-secondary-hover);
}
&.large {
@include button-size(30px, 15px, $font-size-large);
}
&.small {
@include button-size(10px, 5px, $font-size-small);
}
}
Exercise 2: Create SCSS for WordPress Gutenberg Blocks
Create a SCSS implementation for styling a custom WordPress block with variations.
Solution: Custom Feature Block SCSS
// _block-feature.scss
// Block variables
$feature-block-bg: $color-light;
$feature-block-padding: $spacer * 2;
$feature-block-border-radius: 8px;
$feature-block-margin: $spacer * 3;
// Feature block component
.wp-block-mytheme-feature {
background-color: $feature-block-bg;
padding: $feature-block-padding;
border-radius: $feature-block-border-radius;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
margin-bottom: $feature-block-margin;
transition: transform 0.3s ease, box-shadow 0.3s ease;
// Inner block layout
display: flex;
flex-direction: column;
align-items: center;
text-align: center;
// Block icon
&__icon {
font-size: 3rem;
margin-bottom: $spacer;
color: $color-primary;
svg {
width: 64px;
height: 64px;
}
}
// Block title
&__title {
font-size: 1.5rem;
margin-bottom: $spacer;
color: $color-dark;
}
// Block content
&__content {
color: $color-gray;
margin-bottom: $spacer;
p:last-child {
margin-bottom: 0;
}
}
// Hover effect
&:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.15);
}
// Block variations/styles
&.is-style-primary {
background-color: $color-primary;
color: $color-white;
.wp-block-mytheme-feature__title {
color: $color-white;
}
.wp-block-mytheme-feature__content {
color: rgba(255, 255, 255, 0.8);
}
.wp-block-mytheme-feature__icon {
color: $color-white;
}
}
&.is-style-outlined {
background-color: transparent;
border: 2px solid $color-primary;
box-shadow: none;
.wp-block-mytheme-feature__icon {
color: $color-primary;
}
}
&.is-style-minimal {
background-color: transparent;
box-shadow: none;
padding: 0;
&:hover {
transform: none;
}
}
// Responsive styles
@include breakpoint(md) {
padding: $feature-block-padding * 0.75;
&__icon svg {
width: 48px;
height: 48px;
}
&__title {
font-size: 1.25rem;
}
}
@include breakpoint(sm) {
padding: $feature-block-padding * 0.5;
&__icon svg {
width: 40px;
height: 40px;
}
&__title {
font-size: 1.1rem;
}
}
}
// Editor-specific styles
.editor-styles-wrapper {
.wp-block-mytheme-feature {
// Add any editor-specific styles
}
}
Registering Block Styles in WordPress
'primary',
'label' => __('Primary', 'mytheme'),
'style_handle' => 'mytheme-styles',
)
);
register_block_style(
'mytheme/feature',
array(
'name' => 'outlined',
'label' => __('Outlined', 'mytheme'),
'style_handle' => 'mytheme-styles',
)
);
register_block_style(
'mytheme/feature',
array(
'name' => 'minimal',
'label' => __('Minimal', 'mytheme'),
'style_handle' => 'mytheme-styles',
)
);
}
add_action('init', 'mytheme_register_block_styles');
Exercise 3: WordPress Theme Color Scheme using SASS
Create a SASS implementation that allows easy theme color scheme customization.
Solution: Color Scheme SCSS Architecture
// _color-schemes.scss
// Base color schemes
$color-schemes: (
'default': (
'primary': #3a86ff,
'secondary': #ff006e,
'accent': #ffbe0b,
'text': #333333,
'background': #ffffff,
'border': #e0e0e0
),
'dark': (
'primary': #5e96ff,
'secondary': #ff4b93,
'accent': #ffcf4b,
'text': #f1f1f1,
'background': #1a1a1a,
'border': #444444
),
'ocean': (
'primary': #0077b6,
'secondary': #00b4d8,
'accent': #90e0ef,
'text': #212529,
'background': #f8f9fa,
'border': #caf0f8
),
'forest': (
'primary': #2d6a4f,
'secondary': #40916c,
'accent': #b7e4c7,
'text': #212529,
'background': #f8f9fa,
'border': #d8f3dc
)
);
// Get current color scheme from WordPress customizer
$current-scheme: get-theme-mod('color_scheme', 'default');
// Function to get color from the current scheme
@function theme-color($color-name) {
@if map-has-key(map-get($color-schemes, $current-scheme), $color-name) {
@return map-get(map-get($color-schemes, $current-scheme), $color-name);
} @else {
@error "Color '#{$color-name}' not found in color scheme '#{$current-scheme}'";
@return null;
}
}
// Set theme colors based on selected scheme
$color-primary: theme-color('primary');
$color-secondary: theme-color('secondary');
$color-accent: theme-color('accent');
$body-color: theme-color('text');
$body-bg: theme-color('background');
$border-color: theme-color('border');
// Generate CSS custom properties for the color scheme
:root {
@each $color-name, $color-value in map-get($color-schemes, $current-scheme) {
--theme-#{$color-name}: #{$color-value};
}
}
// CSS for color scheme body classes
@each $scheme-name, $scheme-colors in $color-schemes {
body.color-scheme-#{$scheme-name} {
@each $color-name, $color-value in $scheme-colors {
--theme-#{$color-name}: #{$color-value};
}
background-color: var(--theme-background);
color: var(--theme-text);
}
}
WordPress Integration for Color Schemes
add_section('mytheme_color_scheme', array(
'title' => __('Color Scheme', 'mytheme'),
'priority' => 30,
));
// Add color scheme setting
$wp_customize->add_setting('color_scheme', array(
'default' => 'default',
'sanitize_callback' => 'sanitize_key',
'transport' => 'refresh',
));
// Add color scheme control
$wp_customize->add_control('color_scheme', array(
'label' => __('Select Color Scheme', 'mytheme'),
'section' => 'mytheme_color_scheme',
'type' => 'select',
'choices' => array(
'default' => __('Default', 'mytheme'),
'dark' => __('Dark', 'mytheme'),
'ocean' => __('Ocean', 'mytheme'),
'forest' => __('Forest', 'mytheme'),
),
));
}
add_action('customize_register', 'mytheme_customizer_color_schemes');
/**
* Add color scheme body class
*/
function mytheme_color_scheme_body_class($classes) {
$color_scheme = get_theme_mod('color_scheme', 'default');
$classes[] = 'color-scheme-' . $color_scheme;
return $classes;
}
add_filter('body_class', 'mytheme_color_scheme_body_class');
Conclusion: The Power of SASS in WordPress Development
SASS/SCSS brings powerful tools to your CSS workflow, making it more organized, maintainable, and efficient. By adopting SASS in your WordPress development process, you can:
- Improve Code Organization: Using partials, nesting, and modular architecture
- Enhance Maintainability: With variables, mixins, and functions
- Increase Development Speed: Through reusable components and patterns
- Build Responsive Themes: Using mixins for breakpoints and responsive designs
- Future-Proof Your Code: With a structured, scalable approach to styling
The time invested in learning and implementing SASS pays off in the long run, especially for WordPress theme development where maintaining consistent styles across complex layouts and multiple templates is crucial.
As you continue your WordPress development journey, keep exploring advanced SASS techniques and integrating them with modern WordPress features like the Block Editor. This combination of powerful preprocessing capabilities and WordPress flexibility will enable you to create themes that are both visually impressive and maintainable.