CSS - Sass / Scss
Mastering CSS Pre-processors: A Thorough Exploration of SASS and SCSS with Syntax and Examples.
What is SASS?
SASS (Syntactically Awesome Stylesheets) is a CSS preprocessor that extends CSS with programming capabilities, allowing developers to write more maintainable and reusable code. It introduces features like variables, nesting, mixins, and functions that help streamline the styling process for complex websites and applications.
Variables
Variables in SASS allow you to store values like colors, font stacks, and other CSS values to be reused throughout your stylesheet. This promotes consistency in your design and makes large-scale changes much easier to implement.
Syntax:
$variable-name: value;
Example 1:
$primary-color: #3498db;
$font-stack: 'Roboto', sans-serif;
body {
color: $primary-color;
font-family: $font-stack;
}
Output:
body {
color: #3498db;
font-family: 'Roboto', sans-serif;
}
Example 2:
$spacing-base: 8px;
$spacing-large: $spacing-base * 2;
.card {
padding: $spacing-base;
margin-bottom: $spacing-large;
}
Output:
.card {
padding: 8px;
margin-bottom: 16px;
}
Tips:
Use descriptive variable names that indicate the purpose rather than the value
Organize variables in a separate partial file (like
_variables.scss
)Consider using variables for recurring values used three or more times
Nesting
Nesting allows you to organize your CSS in a hierarchical way that reflects the HTML structure, making your stylesheets more readable and maintainable. It helps avoid repetition of parent selectors.
Syntax:
parent-selector {
property: value;
child-selector {
property: value;
}
}
Example 1:
nav {
background-color: #333;
ul {
margin: 0;
padding: 0;
list-style: none;
}
li {
display: inline-block;
}
}
Output:
nav {
background-color: #333;
}
nav ul {
margin: 0;
padding: 0;
list-style: none;
}
nav li {
display: inline-block;
}
Example 2:
.card {
border: 1px solid #ddd;
&__header {
font-weight: bold;
padding: 10px;
}
&:hover {
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
}
Output:
.card {
border: 1px solid #ddd;
}
.card__header {
font-weight: bold;
padding: 10px;
}
.card:hover {
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
Tips:
Avoid nesting more than 3 levels deep to prevent overly specific selectors
Use the
&
character to reference the parent selector in compound names (BEM methodology)Remember that nesting makes your code more readable but can generate more CSS if overused
Mixins
Mixins allow you to define reusable pieces of CSS code that can be included in other selectors. They function similarly to functions in programming languages and can accept parameters for greater flexibility.
Syntax:
@mixin mixin-name($parameter1: default, $parameter2: default) {
property1: value;
property2: $parameter1;
}
.selector {
@include mixin-name(value1, value2);
}
Example 1:
@mixin flex-center {
display: flex;
justify-content: center;
align-items: center;
}
.hero-container {
@include flex-center;
height: 100vh;
}
Output:
.hero-container {
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
}
Example 2:
@mixin button-variant($bg-color, $text-color: white) {
background-color: $bg-color;
color: $text-color;
padding: 8px 16px;
border-radius: 4px;
border: none;
&:hover {
background-color: darken($bg-color, 10%);
}
}
.button-primary {
@include button-variant(#3498db);
}
.button-warning {
@include button-variant(#f39c12, #333);
}
Output:
.button-primary {
background-color: #3498db;
color: white;
padding: 8px 16px;
border-radius: 4px;
border: none;
}
.button-primary:hover {
background-color: #217dbb;
}
.button-warning {
background-color: #f39c12;
color: #333;
padding: 8px 16px;
border-radius: 4px;
border: none;
}
.button-warning:hover {
background-color: #c87f0a;
}
Tips:
Create a library of mixins for common patterns like flexbox layouts, media queries, and typography
Use default parameter values to make mixins more flexible
Consider using mixins instead of extends for better control over the generated CSS
Functions and Operators
SASS functions allow you to perform calculations and manipulate values dynamically, while operators enable mathematical operations directly in your stylesheets. This brings programming capabilities to your CSS workflow.
Syntax:
@function function-name($parameter) {
@return calculation-or-transformation;
}
Example 1:
@function calculate-width($columns, $total: 12) {
@return percentage($columns / $total);
}
.sidebar {
width: calculate-width(4); // 4 columns out of 12
}
.main-content {
width: calculate-width(8); // 8 columns out of 12
}
Output:
.sidebar {
width: 33.33333%;
}
.main-content {
width: 66.66667%;
}
Example 2:
$base-spacing: 8px;
.container {
padding: $base-spacing * 2;
margin-bottom: $base-spacing * 3;
font-size: 16px + 2px;
line-height: 1.5 * $base-spacing;
}
Output:
.container {
padding: 16px;
margin-bottom: 24px;
font-size: 18px;
line-height: 12px;
}
Tips:
Use built-in SASS functions like
darken()
,lighten()
, andmix()
for color manipulationsCreate custom functions for calculations you use frequently across projects
Consider using math functions for responsive sizing calculations
Setting up environment for SCSS
Setting up a SASS/SCSS environment involves installing the necessary tools to compile your SCSS files into standard CSS that browsers can understand. The process typically includes installing Node.js, choosing a compiler (like node-sass, Dart Sass, or LibSass), and configuring your build process.
Here are three popular approaches to set up SCSS in your project:
Using npm and node-sass/sass package:
# Install node-sass (older) or sass (newer Dart Sass implementation) npm install sass --save-dev # Add a script to package.json # "scripts": { "sass": "sass src/scss:dist/css --watch" } # Run the compiler npm run sass
Using a task runner like Gulp:
# Install gulp and sass plugin npm install gulp gulp-sass sass --save-dev # Create a gulpfile.js with SCSS compilation task # Then run: npx gulp sass
Using a bundler like Webpack:
# Install webpack and sass loaders npm install webpack sass-loader css-loader style-loader sass --save-dev # Configure webpack.config.js to handle .scss files # Then build with: npx webpack
Tips:
Use source maps during development to help with debugging SCSS in the browser
Set up file watching for automatic compilation during development
Consider using a modern framework's built-in SCSS support (Next.js, Create React App, etc.)
SCSS or SASS? and Setting Up SCSS
SASS has two syntaxes: the older indentation-based syntax (.sass) and the newer CSS-compatible SCSS syntax (.scss). SCSS is more popular because it's a superset of CSS (meaning valid CSS is also valid SCSS), making it easier to adopt incrementally in existing projects.
Key differences between SASS and SCSS:
SASS (Indented Syntax):
Uses indentation instead of braces to define nesting
Omits semicolons
More concise but less familiar to CSS developers
.container
width: 100%
margin: 0 auto
h1
font-size: 2em
SCSS (Sassy CSS):
Uses braces and semicolons like CSS
Easier migration path from CSS
More widely used in the industry
.container {
width: 100%;
margin: 0 auto;
h1 {
font-size: 2em;
}
}
Setting Up SCSS in a new project:
Example 1: Basic project structure
project/
├── scss/
│ ├── main.scss
│ ├── _variables.scss
│ ├── _mixins.scss
│ └── components/
│ ├── _buttons.scss
│ └── _navigation.scss
├── css/
│ └── main.css (compiled output)
├── index.html
└── package.json
Example 2: Setting up with npm scripts
// package.json
{
"name": "code-subtle-project",
"scripts": {
"scss": "sass scss/main.scss:css/main.css",
"scss:watch": "sass scss/main.scss:css/main.css --watch",
"scss:prod": "sass scss/main.scss:css/main.css --style compressed"
},
"devDependencies": {
"sass": "^1.58.0"
}
}
Tips:
Choose SCSS if you're migrating from CSS or working with a team familiar with CSS
Use a consistent file naming convention (e.g., prefixing partials with underscore)
Configure your editor for SCSS linting and formatting to maintain code quality
Working with SASS
Variables
Variables in SASS are defined using the $
symbol and can store values ranging from colors and font definitions to complex lists and maps. They help maintain consistent design systems and make global changes much easier.
Syntax:
$variable-name: value;
// Variable scoping
.selector {
$local-variable: value; // only available within this block
}
Example 1: Creating a color system
// _variables.scss by Code Subtle
$color-primary: #4a90e2;
$color-secondary: #50e3c2;
$color-text: #333333;
$color-background: #f7f7f7;
// Using variables
.button {
background-color: $color-primary;
color: white;
&.button--alt {
background-color: $color-secondary;
}
}
Output:
.button {
background-color: #4a90e2;
color: white;
}
.button.button--alt {
background-color: #50e3c2;
}
Example 2: Responsive breakpoints as variables
// By Code Subtle
$breakpoint-sm: 576px;
$breakpoint-md: 768px;
$breakpoint-lg: 992px;
$breakpoint-xl: 1200px;
.container {
width: 100%;
@media (min-width: $breakpoint-md) {
width: 720px;
}
@media (min-width: $breakpoint-lg) {
width: 960px;
}
}
Output:
.container {
width: 100%;
}
@media (min-width: 768px) {
.container {
width: 720px;
}
}
@media (min-width: 992px) {
.container {
width: 960px;
}
}
Tips:
Group related variables together (e.g., all colors, all typography settings)
Use !default flag to allow variable overrides when importing
Consider using SASS maps for related sets of values (like different shades of a color)
Nesting
Nesting in SASS allows you to mirror your HTML structure in your stylesheet, making the relationship between elements clearer and reducing the repetition of parent selectors. It helps create more maintainable stylesheets for complex components.
Syntax:
.parent {
// Parent styles
.child {
// Child styles
}
&:hover {
// Parent hover state
}
&__element {
// BEM element
}
}
Example 1: Navigation menu styling
// Code Subtle's navigation component
.nav {
background: #2c3e50;
padding: 1rem 0;
&__list {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
&__item {
margin-right: 1rem;
&:last-child {
margin-right: 0;
}
}
&__link {
color: white;
text-decoration: none;
&:hover {
text-decoration: underline;
}
}
}
Output:
.nav {
background: #2c3e50;
padding: 1rem 0;
}
.nav__list {
display: flex;
list-style: none;
margin: 0;
padding: 0;
}
.nav__item {
margin-right: 1rem;
}
.nav__item:last-child {
margin-right: 0;
}
.nav__link {
color: white;
text-decoration: none;
}
.nav__link:hover {
text-decoration: underline;
}
Example 2: Form component with states
// Code Subtle's form component
.form-field {
margin-bottom: 1.5rem;
label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
input {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
&:focus {
outline: none;
border-color: #4a90e2;
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}
&.is-error {
border-color: #e74c3c;
}
}
.error-message {
color: #e74c3c;
font-size: 0.875rem;
margin-top: 0.5rem;
}
}
Output:
.form-field {
margin-bottom: 1.5rem;
}
.form-field label {
display: block;
margin-bottom: 0.5rem;
font-weight: 500;
}
.form-field input {
width: 100%;
padding: 0.75rem;
border: 1px solid #ddd;
border-radius: 4px;
}
.form-field input:focus {
outline: none;
border-color: #4a90e2;
box-shadow: 0 0 0 2px rgba(74, 144, 226, 0.2);
}
.form-field input.is-error {
border-color: #e74c3c;
}
.form-field .error-message {
color: #e74c3c;
font-size: 0.875rem;
margin-top: 0.5rem;
}
Tips:
Use the
&
character creatively for BEM naming, pseudo-classes, and state classesKeep nesting to a maximum of 3-4 levels to prevent specificity issues
Consider breaking deeply nested components into separate partial files for better organization
Partials and Imports
Partials in SASS are separate files containing SASS code snippets that can be imported into other SASS files. This modular approach allows you to break your stylesheets into smaller, more manageable pieces that are easier to maintain.
Syntax:
// Defining a partial (_filename.scss)
// Importing partials
@import 'filename'; // The underscore and extension are optional
Example 1: Project structure with partials
// _variables.scss
$color-primary: #4a90e2;
$font-stack: 'Roboto', sans-serif;
// _reset.scss
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
// _typography.scss
body {
font-family: $font-stack;
line-height: 1.6;
}
// main.scss (by Code Subtle)
@import 'variables';
@import 'reset';
@import 'typography';
// Component-specific styles
@import 'components/buttons';
@import 'components/forms';
@import 'components/navigation';
// Page-specific styles
@import 'pages/home';
@import 'pages/about';
Output (main.css):
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: "Roboto", sans-serif;
line-height: 1.6;
}
/* Followed by all component and page styles */
Example 2: Creating a component library
// components/_card.scss
.card {
border-radius: 4px;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
padding: 1.5rem;
&__title {
font-size: 1.25rem;
margin-bottom: 0.75rem;
}
&__content {
color: #666;
}
}
// components/_button.scss
.button {
display: inline-block;
padding: 0.75rem 1.5rem;
border-radius: 4px;
font-weight: 500;
cursor: pointer;
&--primary {
background-color: $color-primary;
color: white;
}
&--secondary {
background-color: transparent;
border: 1px solid $color-primary;
color: $color-primary;
}
}
// components/_index.scss (by Code Subtle)
@import 'card';
@import 'button';
// other components...
// main.scss
@import 'variables';
@import 'base/index';
@import 'components/index';
@import 'layouts/index';
Output: Complete CSS with all imported styles compiled together.
Tips:
Prefix partial filenames with an underscore (e.g.,
_variables.scss
) to indicate they shouldn't be compiled on their ownUse index files in directories to simplify imports
Consider using the newer
@use
rule instead of@import
for better module encapsulation (Dart Sass)
Mixins
Mixins in SASS are reusable blocks of CSS declarations that can be included within multiple selectors. Unlike functions, mixins can output multiple CSS properties and can be parameterized for greater flexibility.
Syntax:
@mixin mixin-name($param1: default, $param2: default) {
property1: value;
property2: $param1;
}
// Using a mixin
.selector {
@include mixin-name(value1, value2);
}
Example 1: Responsive typography mixin
// Code Subtle's typography mixins
@mixin responsive-font-size($min-size, $max-size, $min-width: 320px, $max-width: 1200px) {
font-size: $min-size;
@media screen and (min-width: $min-width) {
font-size: calc(#{$min-size} + #{strip-unit($max-size - $min-size)} * ((100vw - #{$min-width}) / #{strip-unit($max-width - $min-width)}));
}
@media screen and (min-width: $max-width) {
font-size: $max-size;
}
}
// Helper function
@function strip-unit($value) {
@return $value / ($value * 0 + 1);
}
// Usage
h1 {
@include responsive-font-size(1.5rem, 3rem);
}
p {
@include responsive-font-size(1rem, 1.25rem);
}
Output:
h1 {
font-size: 1.5rem;
}
@media screen and (min-width: 320px) {
h1 {
font-size: calc(1.5rem + 1.5 * ((100vw - 320px) / 880));
}
}
@media screen and (min-width: 1200px) {
h1 {
font-size: 3rem;
}
}
p {
font-size: 1rem;
}
@media screen and (min-width: 320px) {
p {
font-size: calc(1rem + 0.25 * ((100vw - 320px) / 880));
}
}
@media screen and (min-width: 1200px) {
p {
font-size: 1.25rem;
}
}
Example 2: Cross-browser flexbox mixin
// Code Subtle's flexbox toolkit
@mixin flexbox($direction: row, $justify: flex-start, $align: stretch, $wrap: nowrap) {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: $direction;
flex-direction: $direction;
-webkit-box-pack: $justify;
-ms-flex-pack: $justify;
justify-content: $justify;
-webkit-box-align: $align;
-ms-flex-align: $align;
align-items: $align;
-ms-flex-wrap: $wrap;
flex-wrap: $wrap;
}
// Usage
.card-container {
@include flexbox(row, space-between, center, wrap);
width: 100%;
}
.centered-content {
@include flexbox(column, center, center);
height: 100vh;
}
Output:
.card-container {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: row;
flex-direction: row;
-webkit-box-pack: space-between;
-ms-flex-pack: space-between;
justify-content: space-between;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-ms-flex-wrap: wrap;
flex-wrap: wrap;
width: 100%;
}
.centered-content {
display: -webkit-box;
display: -ms-flexbox;
display: flex;
-webkit-box-orient: vertical;
-webkit-box-direction: normal;
-ms-flex-direction: column;
flex-direction: column;
-webkit-box-pack: center;
-ms-flex-pack: center;
justify-content: center;
-webkit-box-align: center;
-ms-flex-align: center;
align-items: center;
-ms-flex-wrap: nowrap;
flex-wrap: nowrap;
height: 100vh;
}
Tips:
Create a mixin library for common patterns like media queries, gradients, and animations
Document your mixins with comments explaining parameters and usage
Use content blocks with
@content
for even more flexible mixins that can wrap arbitrary CSS
Inheritance/Extends
The @extend
directive in SASS allows one selector to inherit the styles of another, promoting code reuse without duplicating CSS properties. It's particularly useful for creating variations of a base component while maintaining clean CSS output.
Syntax:
.base-class {
property1: value1;
property2: value2;
}
.extended-class {
@extend .base-class;
property3: value3;
}
Example 1: Button variants using extends
// Code Subtle's button system
%button-base {
display: inline-block;
padding: 0.75em 1.5em;
border-radius: 4px;
font-weight: 500;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
}
.button-primary {
@extend %button-base;
background-color: #4a90e2;
color: white;
&:hover {
background-color: darken(#4a90e2, 10%);
}
}
.button-secondary {
@extend %button-base;
background-color: transparent;
border: 1px solid #4a90e2;
color: #4a90e2;
&:hover {
background-color: rgba(74, 144, 226, 0.1);
}
}
.button-danger {
@extend %button-base;
background-color: #e74c3c;
color: white;
&:hover {
background-color: darken(#e74c3c, 10%);
}
}
Output:
.button-primary, .button-secondary, .button-danger {
display: inline-block;
padding: 0.75em 1.5em;
border-radius: 4px;
font-weight: 500;
text-align: center;
cursor: pointer;
transition: all 0.2s ease;
}
.button-primary {
background-color: #4a90e2;
color: white;
}
.button-primary:hover {
background-color: #2275d7;
}
.button-secondary {
background-color: transparent;
border: 1px solid #4a90e2;
color: #4a90e2;
}
.button-secondary:hover {
background-color: rgba(74, 144, 226, 0.1);
}
.button-danger {
background-color: #e74c3c;
color: white;
}
.button-danger:hover {
background-color: #d62c1a;
}
Example 2: Alert components with shared styles
// Code Subtle's alert component system
%alert {
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
display: flex;
align-items: center;
.alert-icon {
margin-right: 0.75rem;
}
.alert-content {
flex: 1;
}
.alert-title {
font-weight: 600;
margin-bottom: 0.25rem;
}
}
.alert-info {
@extend %alert;
background-color: #ebf5ff;
border-left: 4px solid #4a90e2;
.alert-title {
color: #2171cd;
}
}
.alert-success {
@extend %alert;
background-color: #edfaf1;
border-left: 4px solid #27ae60;
.alert-title {
color: #1e8449;
}
}
.alert-warning {
@extend %alert;
background-color: #fff9eb;
border-left: 4px solid #f39c12;
.alert-title {
color: #c87f0a;
}
}
Output:
.alert-info, .alert-success, .alert-warning {
padding: 1rem;
border-radius: 4px;
margin-bottom: 1rem;
display: flex;
align-items: center;
}
.alert-info .alert-icon, .alert-success .alert-icon, .alert-warning .alert-icon {
margin-right: 0.75rem;
}
.alert-info .alert-content, .alert-success .alert-content, .alert-warning .alert-content {
flex: 1;
}
.alert-info .alert-title, .alert-success .alert-title, .alert-warning .alert-title {
font-weight: 600;
margin-bottom: 0.25rem;
}
.alert-info {
background-color: #ebf5ff;
border-left: 4px solid #4a90e2;
}
.alert-info .alert-title {
color: #2171cd;
}
.alert-success {
background-color: #edfaf1;
border-left: 4px solid #27ae60;
}
.alert-success .alert-title {
color: #1e8449;
}
.alert-warning {
background-color: #fff9eb;
border-left: 4px solid #f39c12;
}
.alert-warning .alert-title {
color: #c87f0a;
}
Tips:
Use placeholder selectors (%) for styles that will only be extended and never used directly
Be cautious with extends in media queries as they can lead to unexpected output
Consider using mixins instead of extends for complex components with many variations
Functions
SASS functions allow you to perform calculations and manipulations on values, returning a single value that can be used in your stylesheets. They're useful for complex calculations, color manipulations, and creating dynamic values.
Syntax:
@function function-name($param1, $param2: default) {
// Logic...
@return result;
}
Example 1: Grid calculation function
// Code Subtle's grid system
$grid-columns: 12;
$grid-gutter: 30px;
$container-width: 1200px;
@function column-width($columns) {
@if $columns == $grid-columns {
@return 100%;
}
$width: $columns / $grid-columns * 100%;
@return calc(#{$width} - #{$grid-gutter});
}
// Usage
.col-4 {
width: column-width(4);
margin-right: $grid-gutter / 2;
margin-left: $grid-gutter / 2;
}
.col-8 {
width: column-width(8);
margin-right: $grid-gutter / 2;
margin-left: $grid-gutter / 2;
}
Let me continue with the rest of the SASS article:
Example 1: Grid calculation function (continued)
// Code Subtle's grid system
$grid-columns: 12;
$grid-gutter: 30px;
$container-width: 1200px;
@function column-width($columns) {
@if $columns == $grid-columns {
@return 100%;
}
$width: $columns / $grid-columns * 100%;
@return calc(#{$width} - #{$grid-gutter});
}
// Usage
.col-4 {
width: column-width(4);
margin-right: $grid-gutter / 2;
margin-left: $grid-gutter / 2;
}
.col-8 {
width: column-width(8);
margin-right: $grid-gutter / 2;
margin-left: $grid-gutter / 2;
}
Output:
.col-4 {
width: calc(33.33333% - 30px);
margin-right: 15px;
margin-left: 15px;
}
.col-8 {
width: calc(66.66667% - 30px);
margin-right: 15px;
margin-left: 15px;
}
Example 2: Color manipulation functions
// Code Subtle's color toolkit
$brand-blue: #4a90e2;
@function tint($color, $percentage) {
@return mix(white, $color, $percentage);
}
@function shade($color, $percentage) {
@return mix(black, $color, $percentage);
}
@function contrast-color($color) {
$brightness: (red($color) * 299 + green($color) * 587 + blue($color) * 114) / 1000;
@return if($brightness > 128, #000, #fff);
}
// Usage
.button {
background-color: $brand-blue;
color: contrast-color($brand-blue);
&:hover {
background-color: shade($brand-blue, 15%);
}
&.disabled {
background-color: tint($brand-blue, 50%);
}
}
Output:
.button {
background-color: #4a90e2;
color: #fff;
}
.button:hover {
background-color: #3f7ac0;
}
.button.disabled {
background-color: #a4c7f1;
}
Tips:
Create and document custom functions for repetitive calculations
Use SASS's built-in math and color functions when possible
Keep functions focused on a single responsibility for better reusability
Operators
SASS operators allow you to perform mathematical calculations, string operations, and boolean logic directly in your stylesheets. They enable dynamic and responsive designs based on mathematical relationships rather than hardcoded values.
Syntax:
// Mathematical: +, -, *, /, %
// Comparison: ==, !=, <, >, <=, >=
// Boolean: and, or, not
result: $value1 operator $value2;
Example 1: Layout calculations with operators
// Code Subtle's responsive layout
$container-padding: 20px;
$card-margin: 15px;
$card-count: 3;
.container {
padding: $container-padding;
max-width: 1200px;
margin: 0 auto;
}
.card {
width: calc((100% - (#{$card-margin} * 2 * #{$card-count})) / #{$card-count});
margin: $card-margin;
float: left;
@media (max-width: 768px) {
$mobile-card-count: 2;
width: calc((100% - (#{$card-margin} * 2 * #{$mobile-card-count})) / #{$mobile-card-count});
}
@media (max-width: 480px) {
width: 100%;
margin: $card-margin 0;
}
}
.sidebar {
width: 30%;
}
.main-content {
width: 70% - $container-padding;
}
Output:
.container {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
}
.card {
width: calc((100% - (15px * 2 * 3)) / 3);
margin: 15px;
float: left;
}
@media (max-width: 768px) {
.card {
width: calc((100% - (15px * 2 * 2)) / 2);
}
}
@media (max-width: 480px) {
.card {
width: 100%;
margin: 15px 0;
}
}
.sidebar {
width: 30%;
}
.main-content {
width: 50%;
}
Example 2: Dynamic typography scale
// Code Subtle's typography system
$base-font-size: 16px;
$heading-scale-ratio: 1.2;
html {
font-size: $base-font-size;
}
h6 {
font-size: $base-font-size;
}
h5 {
font-size: $base-font-size * $heading-scale-ratio;
}
h4 {
font-size: $base-font-size * $heading-scale-ratio * $heading-scale-ratio;
}
h3 {
font-size: $base-font-size * $heading-scale-ratio * $heading-scale-ratio * $heading-scale-ratio;
}
h2 {
font-size: $base-font-size * $heading-scale-ratio * $heading-scale-ratio * $heading-scale-ratio * $heading-scale-ratio;
}
h1 {
font-size: $base-font-size * $heading-scale-ratio * $heading-scale-ratio * $heading-scale-ratio * $heading-scale-ratio * $heading-scale-ratio;
}
// Using the modular scale with different scale ratios
$mobile-base-font: $base-font-size - 2px;
$mobile-scale-ratio: 1.15;
@media (max-width: 768px) {
html {
font-size: $mobile-base-font;
}
h1 {
font-size: $mobile-base-font * pow($mobile-scale-ratio, 5);
}
// ... other headings
}
Output:
html {
font-size: 16px;
}
h6 {
font-size: 16px;
}
h5 {
font-size: 19.2px;
}
h4 {
font-size: 23.04px;
}
h3 {
font-size: 27.648px;
}
h2 {
font-size: 33.1776px;
}
h1 {
font-size: 39.81312px;
}
@media (max-width: 768px) {
html {
font-size: 14px;
}
h1 {
font-size: 28.34259px;
}
}
Tips:
Use parentheses to clarify the order of operations
Be cautious when mixing units in calculations
Consider creating functions for complex calculations to improve readability
Advanced Concepts
Control Directives
Control directives in SASS provide programming-like constructs for conditional logic and iteration. They include @if
, @else
, @for
, @each
, and @while
, allowing for more dynamic and flexible stylesheets.
Syntax:
// Conditionals
@if condition {
// styles if true
} @else if another-condition {
// styles if another-condition is true
} @else {
// fallback styles
}
// Loops
@for $var from start through end {
// repeated styles
}
@each $var in list {
// repeated styles for each item
}
@while condition {
// styles until condition is false
}
Example 1: Creating a responsive grid system
// Code Subtle's responsive grid system
$grid-breakpoints: (
xs: 0,
sm: 576px,
md: 768px,
lg: 992px,
xl: 1200px
);
$columns: 12;
@mixin make-grid-columns() {
@for $i from 1 through $columns {
.col-#{$i} {
flex: 0 0 percentage($i / $columns);
max-width: percentage($i / $columns);
}
}
@each $breakpoint, $width in $grid-breakpoints {
@if $width > 0 {
@media (min-width: $width) {
@for $i from 1 through $columns {
.col-#{$breakpoint}-#{$i} {
flex: 0 0 percentage($i / $columns);
max-width: percentage($i / $columns);
}
}
}
}
}
}
.row {
display: flex;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
[class^="col-"] {
position: relative;
width: 100%;
padding-right: 15px;
padding-left: 15px;
}
@include make-grid-columns();
Output (partial):
.row {
display: flex;
flex-wrap: wrap;
margin-right: -15px;
margin-left: -15px;
}
[class^="col-"] {
position: relative;
width: 100%;
padding-right: 15px;
padding-left: 15px;
}
.col-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
.col-2 {
flex: 0 0 16.66667%;
max-width: 16.66667%;
}
/* ... other columns ... */
@media (min-width: 576px) {
.col-sm-1 {
flex: 0 0 8.33333%;
max-width: 8.33333%;
}
/* ... other columns ... */
}
/* ... other breakpoints ... */
Example 2: Theme generator with conditionals
// Code Subtle's theme generator
$themes: (
light: (
bg-color: #ffffff,
text-color: #333333,
border-color: #dddddd,
accent-color: #4a90e2
),
dark: (
bg-color: #1a1a1a,
text-color: #f0f0f0,
border-color: #444444,
accent-color: #5a9aed
)
);
@mixin themed() {
@each $theme, $map in $themes {
.theme-#{$theme} & {
$theme-map: () !global;
@each $key, $value in $map {
$theme-map: map-merge($theme-map, ($key: $value)) !global;
}
@content;
$theme-map: null !global;
}
}
}
@function themed($key) {
@return map-get($theme-map, $key);
}
// Usage
.card {
@include themed() {
background-color: themed('bg-color');
color: themed('text-color');
border: 1px solid themed('border-color');
.card-title {
color: themed('accent-color');
}
@if themed('bg-color') == #ffffff {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
} @else {
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
}
}
}
Output:
.theme-light .card {
background-color: #ffffff;
color: #333333;
border: 1px solid #dddddd;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
}
.theme-light .card .card-title {
color: #4a90e2;
}
.theme-dark .card {
background-color: #1a1a1a;
color: #f0f0f0;
border: 1px solid #444444;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4);
}
.theme-dark .card .card-title {
color: #5a9aed;
}
Tips:
Use control directives for generating responsive utilities and complex component variations
Combine with maps and lists for more dynamic and maintainable code
Create mixins that use control directives to make your code more reusable
Color Functions
SASS provides powerful color functions that allow you to manipulate and transform colors programmatically. These functions are essential for creating consistent color schemes, accessible color variants, and dynamic theming systems.
Syntax:
// Lightness functions
lighten($color, $amount)
darken($color, $amount)
// Saturation functions
saturate($color, $amount)
desaturate($color, $amount)
// Opacity functions
rgba($color, $alpha)
transparentize($color, $amount)
opacify($color, $amount)
// Mixing colors
mix($color1, $color2, $weight: 50%)
// Getting components
red($color)
green($color)
blue($color)
hue($color)
saturation($color)
lightness($color)
Example 1: Creating a comprehensive color palette
// Code Subtle's color system
$brand-primary: #4a90e2;
// Creating shades and tints
$primary-light-10: lighten($brand-primary, 10%);
$primary-light-20: lighten($brand-primary, 20%);
$primary-dark-10: darken($brand-primary, 10%);
$primary-dark-20: darken($brand-primary, 20%);
// Creating focus/hover states
$primary-hover: darken($brand-primary, 7%);
$primary-focus: rgba($brand-primary, 0.4);
// Creating accessible text colors
@function get-text-color($background) {
@if (lightness($background) > 60) {
@return #333333;
} @else {
@return #ffffff;
}
}
// Using the colors
.btn-primary {
background-color: $brand-primary;
color: get-text-color($brand-primary);
&:hover {
background-color: $primary-hover;
}
&:focus {
box-shadow: 0 0 0 3px $primary-focus;
}
}
.card-primary {
border-color: $brand-primary;
.card-header {
background-color: $primary-light-20;
color: get-text-color($primary-light-20);
}
.card-footer {
background-color: $primary-light-10;
}
}
Output:
.btn-primary {
background-color: #4a90e2;
color: #ffffff;
}
.btn-primary:hover {
background-color: #2275d7;
}
.btn-primary:focus {
box-shadow: 0 0 0 3px rgba(74, 144, 226, 0.4);
}
.card-primary {
border-color: #4a90e2;
}
.card-primary .card-header {
background-color: #a3c9f1;
color: #333333;
}
.card-primary .card-footer {
background-color: #77aced;
}
Example 2: Creating an accessible color system
// Code Subtle's accessible color system
$colors: (
'blue': #4a90e2,
'green': #27ae60,
'red': #e74c3c,
'orange': #f39c12,
'purple': #9b59b6
);
@function contrast-ratio($background, $foreground: white) {
$l1: luminance($background);
$l2: luminance($foreground);
@if $l1 > $l2 {
@return ($l1 + 0.05) / ($l2 + 0.05);
} @else {
@return ($l2 + 0.05) / ($l1 + 0.05);
}
}
@function luminance($color) {
$rgb: (
'red': red($color),
'green': green($color),
'blue': blue($color)
);
@each $name, $value in $rgb {
$value: $value / 255;
$value: if($value < 0.03928, $value / 12.92, pow(($value + 0.055) / 1.055, 2.4));
$rgb: map-merge($rgb, ($name: $value));
}
@return 0.2126 * map-get($rgb, 'red') + 0.7152 * map-get($rgb, 'green') + 0.0722 * map-get($rgb, 'blue');
}
@function get-accessible-variant($color, $min-contrast: 4.5) {
$color-lum: luminance($color);
$direction: if($color-lum > 0.5, 'darken', 'lighten');
$amount: 0;
$result: $color;
$contrast: contrast-ratio($result);
@while $contrast < $min-contrast and $amount < 100 {
$amount: $amount + 5;
$result: if($direction == 'darken', darken($color, $amount), lighten($color, $amount));
$contrast: contrast-ratio($result);
}
@return $result;
}
// Generate accessible button classes
@each $name, $color in $colors {
.btn-#{$name} {
background-color: $color;
color: if(lightness($color) > 60, #333333, #ffffff);
&.btn-accessible {
background-color: get-accessible-variant($color);
color: #ffffff;
}
}
}
Output (partial):
.btn-blue {
background-color: #4a90e2;
color: #ffffff;
}
.btn-blue.btn-accessible {
background-color: #1b5daa;
color: #ffffff;
}
.btn-green {
background-color: #27ae60;
color: #ffffff;
}
.btn-green.btn-accessible {
background-color: #1a7943;
color: #ffffff;
}
/* Other buttons... */