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.

Functions vs. Mixins: Functions in SCSS return values and are used within property values, while mixins generate CSS code blocks. Use functions when you need to compute values, and mixins when you need to generate reusable CSS.

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

Testing Tools

  • True - Testing framework for Sass
  • Sassaby - JavaScript testing helpers for Sass

Documentation Tools

  • SassDoc - Documentation generator for Sass