cheatsheets/_sass/2017/utils/_modularscale.scss

313 lines
9.1 KiB
SCSS

// https://github.com/modularscale/modularscale-sass v3.0.3
// Ratios
$double-octave : 4 ;
$pi : 3.14159265359 ;
$major-twelfth : 3 ;
$major-eleventh : 2.666666667 ;
$major-tenth : 2.5 ;
$octave : 2 ;
$major-seventh : 1.875 ;
$minor-seventh : 1.777777778 ;
$major-sixth : 1.666666667 ;
$phi : 1.618034 ;
$golden : $phi ;
$minor-sixth : 1.6 ;
$fifth : 1.5 ;
$augmented-fourth : 1.41421 ;
$fourth : 1.333333333 ;
$major-third : 1.25 ;
$minor-third : 1.2 ;
$major-second : 1.125 ;
$minor-second : 1.066666667 ;
// Base config
$ms-base : 1em !default;
$ms-ratio : $fifth !default;
$modularscale : () !default;// Parse settings starting with defaults.
// Settings should cascade down like you would expect in CSS.
// More specific overrides previous settings.
@function ms-settings($b: false, $r: false, $t: false, $m: $modularscale) {
$base: $ms-base;
$ratio: $ms-ratio;
$thread: map-get($m, $t);
// Override with user settings
@if map-get($m, base) {
$base: map-get($m, base);
}
@if map-get($m, ratio) {
$ratio: map-get($m, ratio);
}
// Override with thread settings
@if $thread {
@if map-get($thread, base) {
$base: map-get($thread, base);
}
@if map-get($thread, ratio) {
$ratio: map-get($thread, ratio);
}
}
// Override with inline settings
@if $b {
$base: $b;
}
@if $r {
$ratio: $r;
}
@return $base $ratio;
}// Sass does not have native pow() support so this needs to be added.
// Compass and other libs implement this more extensively.
// In order to keep this simple, use those when they are avalible.
// Issue for pow() support in Sass: https://github.com/sass/sass/issues/684
@function ms-pow($b,$e) {
// Return 1 if exponent is 0
@if $e == 0 {
@return 1;
}
// If pow() exists (compass or mathsass) use that.
@if function-exists('pow') {
@return pow($b,$e);
}
// This does not support non-integer exponents,
// Check and return an error if a non-integer exponent is passed.
@if (floor($e) != $e) {
@error '
======================================================================
Non-integer values are not supported in modularscale by default.
Try using mathsass in your project to add non-integer scale support.
https://github.com/terkel/mathsass
======================================================================
'
}
// Seed the return.
$ms-return: $b;
// Multiply or divide by the specified number of times.
@if $e > 0 {
@for $i from 1 to $e {
$ms-return: $ms-return * $b;
}
}
@if $e < 0 {
@for $i from $e through 0 {
$ms-return: $ms-return / $b;
}
}
@return $ms-return;
}// Stripping units is not a best practice
// This function should not be used elsewhere
// It is used here because calc() doesn't do unit logic
// AND target ratios use units as a hack to get a number.
@function ms-unitless($val) {
@return ($val / ($val - $val + 1));
}// Basic list sorting
// Would like to replace with http://sassmeister.com/gist/30e4863bd03ce0e1617c
// Unfortunately libsass has a bug with passing arguments into the min() funciton.
@function ms-sort($l) {
// loop until the list is confirmed to be sorted
$sorted: false;
@while $sorted == false {
// Start with the assumption that the lists are sorted.
$sorted: true;
// Loop through the list, checking each value with the one next to it.
// Swap the values if they need to be swapped.
// Not super fast but simple and modular scale doesn't lean hard on sorting.
@for $i from 2 through length($l) {
$n1: nth($l,$i - 1);
$n2: nth($l,$i);
// If the first value is greater than the 2nd, swap them.
@if $n1 > $n2 {
$l: set-nth($l, $i, $n1);
$l: set-nth($l, $i - 1, $n2);
// The list isn't sorted and needs to be looped through again.
$sorted: false;
}
}
}
// Return the sorted list.
@return $l;
}// No reason to have decimal pixel values,
// normalize them to whole numbers.
@function ms-round-px($r) {
@if unit($r) == 'px' {
@return round($r);
}
@return $r;
}// Convert number string to number
@function ms-to-num($n) {
$l: str-length($n);
$r: 0;
$m: str-index($n,'.');
@if $m == null {
$m: $l + 1;
}
// Loop through digits and convert to numbers
@for $i from 1 through $l {
$v: str-slice($n,$i,$i);
@if $v == '1' { $v: 1; }
@elseif $v == '2' { $v: 2; }
@elseif $v == '3' { $v: 3; }
@elseif $v == '4' { $v: 4; }
@elseif $v == '5' { $v: 5; }
@elseif $v == '6' { $v: 6; }
@elseif $v == '7' { $v: 7; }
@elseif $v == '8' { $v: 8; }
@elseif $v == '9' { $v: 9; }
@elseif $v == '0' { $v: 0; }
@else { $v: null; }
@if $v != null {
$m: $m - 1;
$r: $r + ms-pow(10,$m - 1) * $v;
} @else {
$l: $l - 1;
}
}
@return $r;
}
// Find a ratio based on a target value
@function ms-target($t,$b) {
// Convert to string
$t: $t + '';
// Remove base units to calulate ratio
$b: ms-unitless(nth($b,1));
// Find where 'at' is in the string
$at: str-index($t,'at');
// Slice the value and target out
// and convert strings to numbers
$v: ms-to-num(str-slice($t,0,$at - 1));
$t: ms-to-num(str-slice($t,$at + 2));
// Solve the modular scale function for the ratio.
@return ms-pow(($v/$b),(1/$t));
}@function ms-function($v: 0, $base: false, $ratio: false, $thread: false, $settings: $modularscale) {
// Parse settings
$ms-settings: ms-settings($base,$ratio,$thread,$settings);
$base: nth($ms-settings, 1);
$ratio: nth($ms-settings, 2);
// Render target values from settings.
@if unit($ratio) != '' {
$ratio: ms-target($ratio,$base)
}
// Fast calc if not multi stranded
@if(length($base) == 1) {
@return ms-round-px(ms-pow($ratio, $v) * $base);
}
// Create new base array
$ms-bases: nth($base,1);
// Normalize base values
@for $i from 2 through length($base) {
// initial base value
$ms-base: nth($base,$i);
// If the base is bigger than the main base
@if($ms-base > nth($base,1)) {
// divide the value until it aligns with main base.
@while($ms-base > nth($base,1)) {
$ms-base: $ms-base / $ratio;
}
$ms-base: $ms-base * $ratio;
}
// If the base is smaller than the main base.
@elseif ($ms-base < nth($base,1)) {
// pump up the value until it aligns with main base.
@while $ms-base < nth($base,1) {
$ms-base: $ms-base * $ratio;
}
}
// Push into new array
$ms-bases: append($ms-bases,$ms-base);
}
// Sort array from smallest to largest.
$ms-bases: ms-sort($ms-bases);
// Find step to use in calculation
$vtep: floor($v / length($ms-bases));
// Find base to use in calculation
$ms-base: round(($v / length($ms-bases) - $vtep) * length($ms-bases)) + 1;
@return ms-round-px(ms-pow($ratio, $vtep) * nth($ms-bases,$ms-base));
}// Generate calc() function
// based on Mike Riethmuller's Precise control over responsive typography
// http://madebymike.com.au/writing/precise-control-responsive-typography/
@function ms-fluid($val1: 1em, $val2: 1em, $break1: 0, $break2: 0) {
$diff: ms-unitless($val2) - ms-unitless($val1);
// v1 + (v2 - v1) * ( (100vw - b1) / b2 - b1 )
@return calc( #{$val1} + #{ms-unitless($val2) - ms-unitless($val1)} * ( ( 100vw - #{$break1}) / #{ms-unitless($break2) - ms-unitless($break1)} ) );
}
// Main responsive mixin
@mixin ms-respond($prop, $val, $map: $modularscale) {
$base: $ms-base;
$ratio: $ms-ratio;
$first-write: true;
$last-break: null;
// loop through all settings with a breakpoint type value
@each $v, $s in $map {
@if type-of($v) == number {
@if unit($v) != '' {
// Write out the first value without a media query.
@if $first-write {
#{$prop}: ms-function($val, $thread: $v, $settings: $map);
// Not the first write anymore, reset to false to move on.
$first-write: false;
$last-break: $v;
}
// Write intermediate breakpoints.
@else {
@media (min-width: $last-break) and (max-width: $v) {
$val1: ms-function($val, $thread: $last-break, $settings: $map);
$val2: ms-function($val, $thread: $v, $settings: $map);
#{$prop}: ms-fluid($val1,$val2,$last-break,$v);
}
$last-break: $v;
}
}
}
}
// Write the last breakpoint.
@if $last-break {
@media (min-width: $last-break) {
#{$prop}: ms-function($val, $thread: $last-break, $settings: $map);
}
}
}// To attempt to avoid conflicts with other libraries
// all funcitons are namespaced with `ms-`.
// However, to increase usability, a shorthand function is included here.
@function ms($v: 0, $base: false, $ratio: false, $thread: false, $settings: $modularscale) {
@return ms-function($v, $base, $ratio, $thread, $settings);
}