Last active
March 14, 2024 00:05
-
-
Save tommydunn/dbac4512b139de0be4d78638375b72fb to your computer and use it in GitHub Desktop.
Sass Mixin's for Fluid Type
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@mixin interpolate($properties, $min-screen, $max-screen, $min-value, $max-value) { | |
& { | |
@each $property in $properties { | |
#{$property}: $min-value; | |
} | |
@media screen and (min-width: $min-screen) { | |
@each $property in $properties { | |
#{$property}: calc-interpolation($min-screen, $min-value, $max-screen, $max-value); | |
} | |
} | |
@media screen and (min-width: $max-screen) { | |
@each $property in $properties { | |
#{$property}: $max-value; | |
} | |
} | |
} | |
} | |
// Requires the calc-interpolation function which can also be used independently | |
@function calc-interpolation($min-screen, $min-value, $max-screen, $max-value) { | |
$a: ($max-value - $min-value) / ($max-screen - $min-screen); | |
$b: $min-value - $a * $min-screen; | |
$sign: "+"; | |
@if ($b < 0) { | |
$sign: "-"; | |
$b: abs($b); | |
} | |
@return calc(#{$a*100}vw #{$sign} #{$b}); | |
} | |
// Indrek Pass also has a fantastic mixin here: http://sassmeister.com/gist/7f22e44ace49b5124eec | |
/* Single property */ | |
html { | |
@include interpolate(font-size, 320px, 1366px, 14px, 20px); | |
} | |
/* Multiple properties with same values */ | |
h1,h2,h3,h4,h5 { | |
@include interpolate((padding-top, padding-bottom), 20rem, 70rem, 0rem, .5rem); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Interpolate v1.0 | |
// This mixin generates CSS for interpolation of length properties. | |
// It has 5 required values, including the target property, initial | |
// screen size, initial value, final screen size and final value. | |
// It has two optional values which include an easing property, | |
// which is a string, representing a CSS animation-timing-function | |
// and finally a number of bending-points, that determines how many | |
// interpolations steps are applied along the easing function. | |
// Author: Mike Riethmuller - @MikeRiethmuller | |
// More information: http://codepen.io/MadeByMike/pen/a2249946658b139b7625b2a58cf03a65?editors=0100 | |
/// | |
/// @param {String} $property - The CSS property to interpolate | |
/// @param {Unit} $min-screen - A CSS length unit | |
/// @param {Unit} $min-value - A CSS length unit | |
/// @param {Unit} $max-screen - Value to be parsed | |
/// @param {Unit} $max-value - Value to be parsed | |
/// @param {String} $easing - Value to be parsed | |
/// @param {Integer} $bending-points - Value to be parsed | |
/// | |
// Examples on line 258 | |
// Issues: | |
// - cubic-bezier requires whitespace | |
// - cubic-bezier cannot parse negative values | |
@mixin interpolate($property, $min-screen, $min-value, $max-screen, $max-value, $easing: 'linear', $bending-points: 2) { | |
// Default Easing 'Linear' | |
$p0: 0; | |
$p1: 0; | |
$p2: 1; | |
$p3: 1; | |
// Parse Cubic Bezier string | |
@if(str-slice($easing, 1, 12) == 'cubic-bezier') { | |
// Get the values between the brackets | |
// TODO: Deal with different whitespace | |
$i: str-index($easing,')'); // Get index of closing bracket | |
$values: str-slice($easing, 14, $i - 1); // Extract values between brackts | |
$list: explode($values, ', '); // Split the values into a list | |
@debug($list); | |
// Cast values to numebrs | |
$p0: number(nth($list, 1)); | |
$p1: number(nth($list, 2)); | |
$p2: number(nth($list, 3)); | |
$p3: number(nth($list, 4)); | |
} | |
@if($easing == 'ease') { | |
$p0: 0.25; | |
$p1: 1; | |
$p2: 0.25; | |
$p3: 1; | |
} | |
@if($easing == 'ease-in-out') { | |
$p0: 0.42; | |
$p1: 0; | |
$p2: 0.58; | |
$p3: 1; | |
} | |
@if($easing == 'ease-in') { | |
$p0: 0.42; | |
$p1: 0; | |
$p2: 1; | |
$p3: 1; | |
} | |
@if($easing == 'ease-out') { | |
$p0: 0; | |
$p1: 0; | |
$p2: 0.58; | |
$p3: 1; | |
} | |
#{$property}: $min-value; | |
@if($easing == 'linear' or $bending-points < 1) { | |
@media screen and (min-width: $min-screen) { | |
#{$property}: calc-interpolation($min-screen, $min-value, $max-screen, $max-value); | |
} | |
} @else { | |
// Loop through bending points | |
$t: 1 / ($bending-points + 1); | |
$i:1; | |
$prev-screen: $min-screen; | |
$prev-value: $min-value; | |
@while $t*$i <= 1 { | |
$bending-point: $t*$i; | |
$value: cubic-bezier($p0,$p1,$p2,$p3, $bending-point); | |
$screen-int: lerp($min-screen, $max-screen, $bending-point); | |
$value-int: lerp($min-value, $max-value, $value); | |
@media screen and (min-width: $prev-screen) { | |
#{$property}: calc-interpolation($prev-screen, $prev-value, $screen-int, $value-int); | |
} | |
$prev-screen: $screen-int; | |
$prev-value: $value-int; | |
$i: $i+1; | |
} | |
} | |
@media screen and (min-width:$max-screen) { | |
#{$property}: $max-value; | |
} | |
} | |
// Requires several helper functions including: pow, calc-interpolation, cubic-bezier, number and explode | |
// Math functions: | |
// Linear interpolations in CSS as a Sass function | |
// Author: Mike Riethmuller | https://madebymike.com.au/writing/precise-control-responsive-typography/ I | |
@function calc-interpolation($min-screen, $min-value, $max-screen, $max-value) { | |
$a: ($max-value - $min-value) / ($max-screen - $min-screen); | |
$b: $min-value - $a * $min-screen; | |
$sign: "+"; | |
@if ($b < 0) { | |
$sign: "-"; | |
$b: abs($b); | |
} | |
@return calc(#{$a*100}vw #{$sign} #{$b}); | |
} | |
// This is a crude Sass port webkits cubic-bezier function. Looking to simplify this if you can help. | |
@function solve-bexier-x($p1x, $p1y, $p2x, $p2y, $x) { | |
$cx: 3.0 * $p1x; | |
$bx: 3.0 * ($p2x - $p1x) - $cx; | |
$ax: 1.0 - $cx -$bx; | |
$t0: 0.0; | |
$t1: 1.0; | |
$t2: $x; | |
$x2: 0; | |
$res: 1000; | |
@while ($t0 < $t1 or $break) { | |
$x2: (($ax * $t2 + $bx) * $t2 + $cx) * $t2; | |
@if (abs($x2 - $x) < $res) { | |
@return $t2; | |
} | |
@if ($x > $x2) { | |
$t0: $t2; | |
} @else { | |
$t1: $t2; | |
} | |
$t2: ($t1 - $t0) * 0.5 + $t0; | |
} | |
@return $t2; | |
} | |
@function cubic-bezier($p1x, $p1y, $p2x, $p2y, $x) { | |
$cy: 3.0 * $p1y; | |
$by: 3.0 * ($p2y - $p1y) - $cy; | |
$ay: 1.0 - $cy - $by; | |
$t: solve-bexier-x($p1x, $p1y, $p2x, $p2y, $x); | |
@return (($ay * $t + $by) * $t + $cy) * $t; | |
} | |
// A stright up lerp | |
// Credit: Ancient Greeks possibly Hipparchus of Rhodes | |
@function lerp($a, $b, $t) { | |
@return $a + ($b - $a) * $t; | |
} | |
// String functions: | |
// Cast string to number | |
// Credit: Hugo Giraudel | https://www.sassmeister.com/gist/9fa19d254864f33d4a80 | |
@function number($value) { | |
@if type-of($value) == 'number' { | |
@return $value; | |
} @else if type-of($value) != 'string' { | |
$_: log('Value for `to-number` should be a number or a string.'); | |
} | |
$result: 0; | |
$digits: 0; | |
$minus: str-slice($value, 1, 1) == '-'; | |
$numbers: ('0': 0, '1': 1, '2': 2, '3': 3, '4': 4, '5': 5, '6': 6, '7': 7, '8': 8, '9': 9); | |
@for $i from if($minus, 2, 1) through str-length($value) { | |
$character: str-slice($value, $i, $i); | |
@if not (index(map-keys($numbers), $character) or $character == '.') { | |
@return to-length(if($minus, -$result, $result), str-slice($value, $i)) | |
} | |
@if $character == '.' { | |
$digits: 1; | |
} @else if $digits == 0 { | |
$result: $result * 10 + map-get($numbers, $character); | |
} @else { | |
$digits: $digits * 10; | |
$result: $result + map-get($numbers, $character) / $digits; | |
} | |
} | |
@return if($minus, -$result, $result);; | |
} | |
// Explode a string by a delimiter | |
// Credit: https://gist.github.com/danielpchen/3677421ea15dcf2579ff | |
@function explode($string, $delimiter) { | |
$result: (); | |
@if $delimiter == "" { | |
@for $i from 1 through str-length($string) { | |
$result: append($result, str-slice($string, $i, $i)); | |
} | |
@return $result; | |
} | |
$exploding: true; | |
@while $exploding { | |
$d-index: str-index($string, $delimiter); | |
@if $d-index { | |
@if $d-index > 1 { | |
$result: append($result, str-slice($string, 1, $d-index - 1)); | |
$string: str-slice($string, $d-index + str-length($delimiter)); | |
} @else if $d-index == 1 { | |
$string: str-slice($string, 1, $d-index + str-length($delimiter)); | |
} @else { | |
$result: append($result, $string); | |
$exploding: false; | |
} | |
} @else { | |
$result: append($result, $string); | |
$exploding: false; | |
} | |
} | |
@return $result; | |
} | |
@function str-replace($string, $search, $replace: '') { | |
$index: str-index($string, $search); | |
@if $index { | |
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace); | |
} | |
@return $string; | |
} | |
// Example | |
.h1-classic-linear { | |
@include interpolate('font-size', 600px, 12px, 1000px, 24px); | |
} | |
.h1-easy-peasy { | |
@include interpolate('font-size', 600px, 12px, 1000px, 24px, 'ease-in'); | |
} | |
.h1-cubic-bezier { | |
@include interpolate('font-size', 600px, 12px, 1000px, 24px, 'cubic-bezier(0.75, 0.05, 0.85, 0.06)'); | |
} | |
.h1-bloat-my-css { | |
@include interpolate('font-size', 600px, 12px, 1000px, 24px, 'ease-in-ease-out', 10); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment