Advanced SCSS Functions and Mixins
Understanding Advanced SCSS Functions
SCSS functions allow you to define complex operations that return values, making your stylesheets more dynamic and maintainable.
Function Syntax
@function function-name($parameter1, $parameter2: default-value) {
// Function logic
@return $value;
}
// Usage
.element {
property: function-name(argument1, argument2);
}
Advanced Color Functions
1. Color Palette Generator
Create a function to generate color palettes from a base color:
// Generate a color palette from a base color
@function generate-palette($base-color) {
@return (
'base': $base-color,
'light': lighten($base-color, 15%),
'lighter': lighten($base-color, 30%),
'dark': darken($base-color, 15%),
'darker': darken($base-color, 30%),
'contrast': if(lightness($base-color) > 50%, #000, #fff)
);
}
// Usage
$primary-palette: generate-palette(#0066cc);
.button {
background-color: map-get($primary-palette, 'base');
color: map-get($primary-palette, 'contrast');
&:hover {
background-color: map-get($primary-palette, 'dark');
}
&:disabled {
background-color: map-get($primary-palette, 'light');
}
}
2. Color Accessibility Checker
Function to ensure color combinations meet WCAG contrast requirements:
$luminance2 {
@return ($luminance1 + 0.05) / ($luminance2 + 0.05);
} @else {
@return ($luminance2 + 0.05) / ($luminance1 + 0.05);
}
}
// Calculate luminance of a color
@function luminance($color) {
$r: red($color) / 255;
$g: green($color) / 255;
$b: blue($color) / 255;
$r: if($r <= 0.03928, $r / 12.92, pow(($r + 0.055) / 1.055, 2.4));
$g: if($g <= 0.03928, $g / 12.92, pow(($g + 0.055) / 1.055, 2.4));
$b: if($b <= 0.03928, $b / 12.92, pow(($b + 0.055) / 1.055, 2.4));
@return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
}
// Check if color combination passes WCAG AA
@function is-accessible($text-color, $bg-color, $level: 'AA') {
$ratio: contrast-ratio($text-color, $bg-color);
@if $level == 'AA' {
@return $ratio >= 4.5;
} @else if $level == 'AAA' {
@return $ratio >= 7;
}
}
// Get accessible text color for background
@function accessible-text-color($bg-color, $light-color: #fff, $dark-color: #000) {
@if contrast-ratio($bg-color, $light-color) >= contrast-ratio($bg-color, $dark-color) {
@return $light-color;
} @else {
@return $dark-color;
}
}
// Usage
.button {
$bg-color: #0066cc;
background-color: $bg-color;
color: accessible-text-color($bg-color);
}]]>
3. Color Theme Generator
Generate an entire theme from a few base colors:
@function generate-theme($primary, $secondary, $neutral) {
$theme: (
'primary': generate-palette($primary),
'secondary': generate-palette($secondary),
'neutral': generate-palette($neutral),
'success': generate-palette(#28a745),
'warning': generate-palette(#ffc107),
'danger': generate-palette(#dc3545),
'info': generate-palette(#17a2b8)
);
@return $theme;
}
// Usage
$theme: generate-theme(#0066cc, #6c757d, #f8f9fa);
.alert {
&--primary {
background-color: map-get(map-get($theme, 'primary'), 'light');
color: map-get(map-get($theme, 'primary'), 'darker');
}
&--danger {
background-color: map-get(map-get($theme, 'danger'), 'light');
color: map-get(map-get($theme, 'danger'), 'darker');
}
}
Advanced Math and Unit Functions
1. Fluid Typography
Create responsive font sizes that scale smoothly between viewport sizes:
// Fluid typography function
@function fluid-type($min-vw, $max-vw, $min-font-size, $max-font-size) {
$u1: unit($min-vw);
$u2: unit($max-vw);
$u3: unit($min-font-size);
$u4: unit($max-font-size);
@if $u1 == $u2 and $u1 == $u3 and $u1 == $u4 {
// All units are the same, proceed with calculation
$slope: ($max-font-size - $min-font-size) / ($max-vw - $min-vw);
$y-intercept: $min-font-size - $slope * $min-vw;
@return calc(#{$y-intercept} + #{$slope} * 100vw);
} @else {
@error "Incompatible units: #{$u1}, #{$u2}, #{$u3}, #{$u4}";
}
}
// Usage
html {
font-size: fluid-type(320px, 1200px, 16px, 20px);
}
h1 {
font-size: fluid-type(320px, 1200px, 24px, 48px);
}
2. Unit Conversion Functions
Convert between different CSS units:
// Convert px to rem
@function px-to-rem($px, $base-font-size: 16px) {
@return ($px / $base-font-size) * 1rem;
}
// Convert px to em
@function px-to-em($px, $context: 16px) {
@return ($px / $context) * 1em;
}
// Convert rem to px
@function rem-to-px($rem, $base-font-size: 16px) {
@return $rem * $base-font-size;
}
// Strip unit from a value
@function strip-unit($value) {
@return $value / ($value * 0 + 1);
}
// Usage
.element {
padding: px-to-rem(20px);
margin: px-to-em(16px, 20px);
border-width: strip-unit(2px) * 1vw;
}
3. Modular Scale Function
Create harmonious size relationships based on a ratio:
// Modular scale function
@function modular-scale($value, $increment, $ratio: 1.618) {
@return $value * pow($ratio, $increment);
}
// Usage with typography
$base-font-size: 1rem;
$type-scale: (
'xs': modular-scale($base-font-size, -2),
'sm': modular-scale($base-font-size, -1),
'md': $base-font-size,
'lg': modular-scale($base-font-size, 1),
'xl': modular-scale($base-font-size, 2),
'xxl': modular-scale($base-font-size, 3)
);
h1 { font-size: map-get($type-scale, 'xxl'); }
h2 { font-size: map-get($type-scale, 'xl'); }
h3 { font-size: map-get($type-scale, 'lg'); }
p { font-size: map-get($type-scale, 'md'); }
small { font-size: map-get($type-scale, 'sm'); }
Advanced Mixins
1. Responsive Grid System
Create a flexible grid system with breakpoints:
// Breakpoints map
$breakpoints: (
'xs': 0,
'sm': 576px,
'md': 768px,
'lg': 992px,
'xl': 1200px,
'xxl': 1400px
);
// Media query mixin
@mixin media-breakpoint-up($breakpoint) {
@if map-has-key($breakpoints, $breakpoint) {
$min-width: map-get($breakpoints, $breakpoint);
@media (min-width: $min-width) {
@content;
}
} @else {
@error "Unknown breakpoint: #{$breakpoint}";
}
}
// Grid container mixin
@mixin container($max-width: 1200px, $padding: 15px) {
width: 100%;
padding-right: $padding;
padding-left: $padding;
margin-right: auto;
margin-left: auto;
max-width: $max-width;
}
// Row mixin
@mixin row($gutter: 30px) {
display: flex;
flex-wrap: wrap;
margin-right: -$gutter / 2;
margin-left: -$gutter / 2;
}
// Column mixin
@mixin column($size, $columns: 12, $gutter: 30px) {
flex: 0 0 percentage($size / $columns);
max-width: percentage($size / $columns);
padding-right: $gutter / 2;
padding-left: $gutter / 2;
}
// Responsive column mixin
@mixin responsive-column($breakpoints-map, $columns: 12, $gutter: 30px) {
@each $breakpoint, $size in $breakpoints-map {
@include media-breakpoint-up($breakpoint) {
@include column($size, $columns, $gutter);
}
}
}
// Usage
.container {
@include container();
}
.row {
@include row();
}
.col-md-6-lg-4 {
@include responsive-column((
'xs': 12,
'md': 6,
'lg': 4
));
}
2. Advanced Animation Mixins
Create reusable, customizable animations:
// Keyframe animation mixin
@mixin keyframes($name) {
@keyframes #{$name} {
@content;
}
}
// Animation mixin
@mixin animation($name, $duration: 1s, $timing-function: ease, $delay: 0s, $iteration-count: 1, $direction: normal, $fill-mode: forwards) {
animation-name: $name;
animation-duration: $duration;
animation-timing-function: $timing-function;
animation-delay: $delay;
animation-iteration-count: $iteration-count;
animation-direction: $direction;
animation-fill-mode: $fill-mode;
}
// Fade in animation
@include keyframes(fade-in) {
from { opacity: 0; }
to { opacity: 1; }
}
// Slide in animation
@include keyframes(slide-in) {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
// Usage
.element {
@include animation(fade-in, 0.5s, ease-out);
}
.element-with-entrance {
@include animation(slide-in, 0.7s, cubic-bezier(0.175, 0.885, 0.32, 1.275));
}
3. Advanced Typography Mixins
Create consistent typography across your project:
// Typography presets mixin
@mixin typography-preset($preset) {
@if $preset == 'heading-1' {
font-size: 2.5rem;
line-height: 1.2;
font-weight: 700;
margin-bottom: 1rem;
} @else if $preset == 'heading-2' {
font-size: 2rem;
line-height: 1.3;
font-weight: 600;
margin-bottom: 0.875rem;
} @else if $preset == 'body' {
font-size: 1rem;
line-height: 1.5;
font-weight: 400;
} @else if $preset == 'caption' {
font-size: 0.875rem;
line-height: 1.4;
font-weight: 400;
color: rgba(0, 0, 0, 0.6);
} @else {
@error "Unknown typography preset: #{$preset}";
}
}
// Text truncation mixin
@mixin truncate($lines: 1) {
@if $lines == 1 {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} @else {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
}
}
// Font smoothing mixin
@mixin font-smoothing($value: antialiased) {
@if $value == antialiased {
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
} @else if $value == subpixel-antialiased {
-webkit-font-smoothing: subpixel-antialiased;
-moz-osx-font-smoothing: auto;
} @else if $value == none {
-webkit-font-smoothing: auto;
-moz-osx-font-smoothing: auto;
}
}
// Usage
h1 {
@include typography-preset('heading-1');
@include font-smoothing(antialiased);
}
.card-title {
@include typography-preset('heading-2');
@include truncate(2);
}
Advanced Utility Mixins
1. CSS Grid Utilities
Simplify CSS Grid layout creation:
// Grid container mixin
@mixin grid($columns: 12, $gap: 20px) {
display: grid;
grid-template-columns: repeat($columns, 1fr);
gap: $gap;
}
// Grid column span mixin
@mixin grid-column($start, $end: null) {
@if $end {
grid-column: #{$start} / #{$end};
} @else {
grid-column: #{$start} / span 1;
}
}
// Grid area mixin
@mixin grid-area($row-start, $column-start, $row-end, $column-end) {
grid-area: #{$row-start} / #{$column-start} / #{$row-end} / #{$column-end};
}
// Responsive grid columns mixin
@mixin responsive-grid-columns($columns-map) {
@each $breakpoint, $columns in $columns-map {
@include media-breakpoint-up($breakpoint) {
grid-template-columns: repeat($columns, 1fr);
}
}
}
// Usage
.grid-container {
@include grid(12, 20px);
@include responsive-grid-columns((
'xs': 1,
'sm': 2,
'md': 3,
'lg': 4
));
}
.grid-item-featured {
@include grid-column(1, 3);
@include media-breakpoint-up('md') {
@include grid-area(1, 1, 3, 3);
}
}
2. Accessibility Mixins
Improve accessibility with reusable mixins:
// Visually hidden content (screen reader only)
@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;
}
// Focus styles
@mixin focus-outline($color: #0066cc, $width: 2px, $offset: 2px) {
&:focus {
outline: $width solid $color;
outline-offset: $offset;
}
&:focus:not(:focus-visible) {
outline: none;
}
&:focus-visible {
outline: $width solid $color;
outline-offset: $offset;
}
}
// Skip link
@mixin skip-link {
position: absolute;
top: -40px;
left: 0;
background: #000;
color: #fff;
padding: 8px;
z-index: 100;
&:focus {
top: 0;
}
}
// Usage
.sr-only {
@include visually-hidden;
}
.button {
@include focus-outline;
}
.skip-to-content {
@include skip-link;
}
3. Performance Optimization Mixins
Optimize rendering performance:
// Hardware acceleration mixin
@mixin hardware-accelerate {
transform: translateZ(0);
backface-visibility: hidden;
perspective: 1000px;
}
// Content visibility mixin
@mixin content-visibility($value: auto) {
content-visibility: $value;
contain-intrinsic-size: auto;
}
// Will-change mixin with auto cleanup
@mixin will-change($properties...) {
will-change: $properties;
@at-root {
.no-hover &,
.touch-device & {
will-change: auto;
}
}
}
// Usage
.animated-element {
@include hardware-accelerate;
@include will-change(transform, opacity);
transition: transform 0.3s, opacity 0.3s;
}
.off-screen-content {
@include content-visibility(auto);
}
Testing and Debugging Functions and Mixins
1. Debug Mixin
Create a debug mixin to visualize elements:
// Debug outline mixin
@mixin debug-outline($color: red, $width: 1px) {
outline: $width solid $color;
}
// Debug grid mixin
@mixin debug-grid($columns: 12, $color: rgba(255, 0, 0, 0.1)) {
position: relative;
&::after {
content: '';
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
background: repeating-linear-gradient(
to right,
transparent,
transparent calc((100% / #{$columns}) - 1px),
$color calc((100% / #{$columns}) - 1px),
$color calc(100% / #{$columns})
);
pointer-events: none;
z-index: 9999;
}
}
// Usage
.container {
@include debug-grid;
}
.element {
@include debug-outline;
}
2. Unit Testing Functions
Test your SCSS functions with the True testing framework:
// _test.scss
@import 'true';
@import 'functions';
@include test-module('Color Functions') {
@include test('accessible-text-color returns white for dark backgrounds') {
$result: accessible-text-color(#000);
$expected: #fff;
@include assert-equal($result, $expected);
}
@include test('accessible-text-color returns black for light backgrounds') {
$result: accessible-text-color(#fff);
$expected: #000;
@include assert-equal($result, $expected);
}
}
// Run tests with True CLI
Best Practices
1. Documentation
Document your functions and mixins thoroughly:
/// Convert pixels to rem units
/// @param {Number} $px - Value in pixels
/// @param {Number} $base-font-size [16px] - Base font size in pixels
/// @return {Number} - Value in rem units
/// @example
/// font-size: px-to-rem(24px);
/// // Output: font-size: 1.5rem;
@function px-to-rem($px, $base-font-size: 16px) {
@return ($px / $base-font-size) * 1rem;
}
2. Organization
- Group related functions and mixins in separate files
- Use a consistent naming convention
- Create a library of reusable functions and mixins
- Consider publishing your most useful functions as a package
3. Performance
- Keep functions simple and focused
- Avoid deeply nested function calls
- Cache complex calculations in variables
- Use maps for lookup tables instead of complex logic
Resources and Libraries
Function and Mixin Libraries
- Bourbon - A lightweight Sass tool set
- Sass MQ - Media query manager for Sass
- Modular Scale - Modular scale calculator
- Accoutrement - Sass utilities for design systems
Testing Tools
Documentation Tools
- SassDoc - Documentation generator for Sass