|
@function is-color($color) { |
|
@if (type-of($color) == color) { |
|
@return true; |
|
} |
|
@return false; |
|
} |
|
|
|
@function count-occurrences($string, $search) { |
|
$searchIndex: str-index($string, $search); |
|
$searchCount: 0; |
|
@while $searchIndex { |
|
$searchCount: $searchCount + 1; |
|
$string: str-slice($string, $searchIndex + 1); |
|
$searchIndex: str-index($string, $search); |
|
} |
|
@return $searchCount; |
|
} |
|
|
|
@function str-is-between($string, $first, $last) { |
|
$firstCount: count-occurrences($string, $first); |
|
$lastCount: count-occurrences($string, $last); |
|
@return $firstCount == $lastCount; |
|
} |
|
|
|
@function recursive-color($color, $index: 0) { |
|
$indices: ( |
|
0: h, |
|
1: s, |
|
2: l, |
|
3: a |
|
); |
|
// find end of part |
|
$end: str-index($color, ','); |
|
@while ($end and not str-is-between(str-slice($color, 0, $end - 1), '(', ')')) { |
|
$newEnd: str-index(str-slice($color, $end + 1), ','); |
|
@if (not $newEnd) { |
|
$newEnd: 0; |
|
} |
|
$end: 2 + $end + $newEnd; |
|
} |
|
@if ($end) { |
|
$part: str-slice($color, 0, $end - 1); |
|
$value: map-merge( |
|
( |
|
map-get($indices, $index): $part |
|
), |
|
recursive-color(str-slice($color, $end + 1), $index + 1) |
|
); |
|
@return $value; |
|
} |
|
@return (); |
|
} |
|
|
|
@function to-hsl($color) { |
|
$c: inspect($color); |
|
|
|
$h: 0; |
|
$s: 0; |
|
$l: 0; |
|
$a: 1; |
|
|
|
@if (is-color($color)) { |
|
// std color |
|
$h: hue($color); |
|
$s: saturation($color); |
|
$l: lightness($color); |
|
$a: alpha($color); |
|
|
|
@return (h: $h, s: $s, l: $l, a: $a); |
|
} |
|
|
|
@if (str-slice($c, 0, 3) == 'var') { |
|
// var(--color) |
|
$commaPos: str-index($c, ','); |
|
$end: -2; |
|
@if ($commaPos) { |
|
$end: $commaPos - 1; |
|
} |
|
$var: str-slice($c, 7, $end); |
|
|
|
$h: var(--#{$var}-h); |
|
$s: var(--#{$var}-s); |
|
$l: var(--#{$var}-l); |
|
$a: var(--#{$var}-a, 1); |
|
|
|
@return (h: $h, s: $s, l: $l, a: $a); |
|
} |
|
|
|
@if ($c == '0') { |
|
@return (h: $h, s: $s, l: $l, a: $a); |
|
} |
|
|
|
// color is (maybe complex) calculated color |
|
// e.g.: hsla(calc((var(--white-h) + var(--primary-h)) / 2), calc((var(--white-s) + var(--primary-s)) / 2), calc((var(--white-l) + var(--primary-l)) / 2), calc((var(--white-a, 1) + var(--primary-a, 1)) / 2)), hsla(calc((var(--white-h) + var(--primary-h)) / 2), calc((var(--white-s) + var(--primary-s)) / 2), calc((var(--white-l) + var(--primary-l)) / 2), calc((var(--white-a, 1) + var(--primary-a, 1)) / 2)) |
|
$startPos: str-index($c, '('); |
|
$c: str-slice($c, $startPos + 1, -2); // 3 or 4 comma-separated vomplex values |
|
@return recursive-color($c); |
|
// $hEnd: str-index($c, ','); |
|
// @if ($hEnd) { |
|
// $h: str-slice($c, 0, $hEnd - 1); |
|
// $c: str-slice($c, $hEnd + 1); |
|
// $sEnd: str-index($c, ','); |
|
// @if ($hEnd) { |
|
// $h: str-slice($c, 0, $hEnd - 1); |
|
// $c: str-slice($c, $hEnd + 1); |
|
// $sEnd: str-index($c, ','); |
|
// } |
|
// } |
|
|
|
// @return (h: $h, s: $s, l: $l, a: $a); |
|
} |
|
|
|
@function render-hsla($h, $s, $l, $a: 1) { |
|
@return hsla($h, $s, $l, $a); |
|
} |
|
|
|
@function lighten($color, $amount) { |
|
@if (is-color($color)) { |
|
@return scale-color($color: $color, $lightness: $amount); |
|
} |
|
|
|
$c: to-hsl($color); |
|
$h: map-get($c, h); |
|
$s: map-get($c, s); |
|
$l: map-get($c, l); |
|
$a: map-get($c, a); |
|
@return render-hsla($h, $s, calc(#{$l} + #{$amount}), $a); |
|
} |
|
|
|
@function darken($color, $amount) { |
|
@return lighten($color, $amount * -1); |
|
} |
|
|
|
@function rgba($red, $green, $blue: false, $alpha: false) { |
|
$color: $red; |
|
|
|
@if (not $blue and not $alpha) { |
|
$alpha: $green; |
|
$color: $red; |
|
} |
|
|
|
$c: to-hsl($color); |
|
$h: map-get($c, h); |
|
$s: map-get($c, s); |
|
$l: map-get($c, l); |
|
@return render-hsla($h, $s, $l, $alpha); |
|
} |
|
|
|
@function rgb($red, $green, $blue) { |
|
@return rgba($red, $green, $blue, 1); |
|
} |
|
|
|
@function mix($color-1, $color-2, $weight: 50%) { |
|
$c1: to-hsl($color-1); |
|
$c2: to-hsl($color-2); |
|
|
|
$h1: map-get($c1, h); |
|
$s1: map-get($c1, s); |
|
$l1: map-get($c1, l); |
|
$a1: map-get($c1, a); |
|
|
|
$h2: map-get($c2, h); |
|
$s2: map-get($c2, s); |
|
$l2: map-get($c2, l); |
|
$a2: map-get($c2, a); |
|
|
|
$h: calc((#{$h1} + #{$h2}) / 2); |
|
$s: calc((#{$s1} + #{$s2}) / 2); |
|
$l: calc((#{$l1} + #{$l2}) / 2); |
|
$a: calc((#{$a1} + #{$a2}) / 2); |
|
|
|
@return render-hsla($h, $s, $l, $a); |
|
} |
|
|
|
@function fade-in($color, $amount) { |
|
$c: to-hsl($color); |
|
|
|
$h: map-get($c, h); |
|
$s: map-get($c, s); |
|
$l: map-get($c, l); |
|
$a: map-get($c, a); |
|
@if (not $a) { |
|
$a: 1; |
|
} |
|
|
|
@return render-hsla($h, $s, $l, $a + $amount); |
|
} |
|
|
|
@function color-yiq($color, $dark: $yiq-text-dark, $light: $yiq-text-light) { |
|
@if (is-color($color)) { |
|
$r: red($color); |
|
$g: green($color); |
|
$b: blue($color); |
|
|
|
$yiq: (($r * 299) + ($g * 587) + ($b * 114)) / 1000; |
|
|
|
@if ($yiq >= $yiq-contrasted-threshold) { |
|
@return $dark; |
|
} @else { |
|
@return $light; |
|
} |
|
} @else { |
|
$c: to-hsl($color); |
|
$l: map-get($c, l); |
|
|
|
$th: $yiq-contrasted-threshold / 2.56; // convert hex to dec |
|
$lightness: calc(-100 * calc(#{$l} - #{$th * 1%})); |
|
|
|
// ignoring hue and saturation, just a light or dark gray |
|
@return render-hsla(0, 0%, $lightness, 1); |
|
} |
|
} |
Nah, no good. My variables file has, for example
With the css variables being set on
:root
and:root.theme-x
differently. Our theme switcher just changes the class at the root of the page, variables get new values, theme changed. But, it relies on css variables for runtime value changes. It's not the only way to do this, sure; but it is the road we've already headed down.With bootstrap/scss not really making things easy with the update to bs5 (worked great for us with react-bootstrap for bs4), I'm thinking it's time to find a new solution. :-/