244 lines
8.5 KiB
SCSS
Vendored
244 lines
8.5 KiB
SCSS
Vendored
// @function strip-unit($value)
|
|
// @param {number with unit} $value
|
|
@function strip-unit($value) {
|
|
@return ($value / ($value * 0 + 1));
|
|
}
|
|
|
|
// @function pow($base, $exponents)
|
|
// Helper: Raise a number the power of another number
|
|
// Adapted from: https://gist.github.com/hail2u/1964056
|
|
//
|
|
// @param {number} $base — The base number
|
|
// @param {number} $exponents — The exponent to which to raise $base
|
|
@function pow($base, $exponents) {
|
|
$raised: 1;
|
|
@for $i from 1 through $exponents {
|
|
$raised: $raised * $base;
|
|
}
|
|
@return $raised;
|
|
}
|
|
|
|
// @function luminance($color)
|
|
// Helper: Calculate Luminance of a single color
|
|
// Adapted from: https://medium.com/dev-channel/using-sass-to-automatically-pick-text-colors-4ba7645d2796
|
|
// White luminance is 1, Black luminance is 0
|
|
// To be used in other functions or mixins — creates non-standard CSS output:
|
|
// Usage:
|
|
// .sample { luminance: luminance(#c00); }
|
|
// Output:
|
|
// .sample { luminance: 0.1283736922; }
|
|
//
|
|
@function luminance($color) {
|
|
// Thanks voxpelli for a very concise implementation of luminance measure in sass
|
|
// Adapted from: https://gist.github.com/voxpelli/6304812
|
|
$rgba: red($color), green($color), blue($color);
|
|
$rgba2: ();
|
|
@for $i from 1 through 3 {
|
|
$rgb: nth($rgba, $i);
|
|
$rgb: calc($rgb / 255);
|
|
$rgb: if($rgb < 0.03928, calc($rgb / 12.92), pow(calc(($rgb + 0.055) / 1.055), 2));
|
|
$rgba2: append($rgba2, $rgb);
|
|
}
|
|
@return (0.2126 * nth($rgba2, 1) + 0.7152 * nth($rgba2, 2) + 0.0722 * nth($rgba2, 3)) * 100;
|
|
}
|
|
|
|
// @function contrast-ratio($fg, $bg)
|
|
// Helper: Calculate "readability" as defined by WCAG 2.1
|
|
// Adapted from: https://github.com/LeaVerou/contrast-ratio/blob/gh-pages/color.js
|
|
// Formula: http://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef
|
|
// To be used in other functions or mixins — creates non-standard CSS output:
|
|
// Usage:
|
|
// .sample { color-contrast: contrast-ratio(#c00, #fff); }
|
|
// Output:
|
|
// .sample { color-contrast: 5.89; }
|
|
//
|
|
@function contrast-ratio($fg, $bg) {
|
|
$luminance1: luminance($fg) + 0.05;
|
|
$luminance2: luminance($bg) + 0.05;
|
|
$ratio: calc($luminance1 / $luminance2);
|
|
@if $luminance2 > $luminance1 {
|
|
$ratio: calc(1 / $ratio);
|
|
}
|
|
// Round to a hundreth because 6.96 should not pass a ratio of 7.0
|
|
$ratio: calc(round($ratio * 100) / 100);
|
|
@return $ratio;
|
|
}
|
|
|
|
// @function validate-font-size($size)
|
|
// Helper: Depending on the unit recalculate a font size value into pixels if possible
|
|
// To be used in other functions or mixins — creates non-standard CSS output:
|
|
// Usage:
|
|
// .sample { validate-font-size: validate-font-size(1em); }
|
|
// Output:
|
|
// .sample { validate-font-size: 16; }
|
|
//
|
|
@function validate-font-size($size) {
|
|
@if unit($size) == "em" or unit($size) == "rem" or unit($size) == "px" or unit($size) == "" {
|
|
// Check if a flexible unit
|
|
@if unit($size) == "em" or unit($size) == "rem" {
|
|
// Need to convert to a pixel value. Let's not overcomplicate it with possible EM inheritence scale factors
|
|
@return strip-unit($size * 16);
|
|
}
|
|
@if unit($size) == "px" {
|
|
// We expect PX, so strip the value and return it
|
|
@return strip-unit($size);
|
|
}
|
|
@if unit($size) == "" {
|
|
@return $size;
|
|
}
|
|
} @else {
|
|
@error "validate-font-size(): An unexpected font size unit was supplied.";
|
|
}
|
|
}
|
|
|
|
// @function get-ratio($level: 'AA', $size: 16, $bold: false)
|
|
// Helper: Determine the correct ratio value to use based on font-size and WCAG Level
|
|
// To be used in other functions or mixins — creates non-standard CSS output:
|
|
// Usage:
|
|
// .sample { get-ratio: get-ratio('AAA', 19, true); }
|
|
// Output:
|
|
// .sample { get-ratio: 4.5; }
|
|
//
|
|
@function get-ratio($level: "AA", $size: 16, $bold: false) {
|
|
// Default ratio
|
|
$ratio: 4.5;
|
|
@if $level == "AAA" {
|
|
$ratio: 7;
|
|
}
|
|
|
|
// Make sure the size is valid. If the value is not EM, REM, or PX (preferred), we can't help
|
|
$size: validate-font-size($size);
|
|
|
|
// Check font size
|
|
@if $size < 24 {
|
|
// Small text, use defaults
|
|
// But:
|
|
@if $size >= 19 and $bold == true {
|
|
// Special case: Small text but also bold
|
|
@if $level == "AAA" {
|
|
$ratio: 4.5;
|
|
} @else {
|
|
$ratio: 3;
|
|
}
|
|
}
|
|
} @else {
|
|
// Larger than 24
|
|
$ratio: 3;
|
|
@if $level == "AAA" {
|
|
$ratio: 4.5;
|
|
}
|
|
}
|
|
@return $ratio;
|
|
}
|
|
|
|
// @function light-or-dark($color)
|
|
// Helper: Use contrast against white or black to determine if a color is "light" or "dark"
|
|
// Adapted from: https://medium.com/dev-channel/using-sass-to-automatically-pick-text-colors-4ba7645d2796
|
|
// To be used in other functions or mixins — creates non-standard CSS output:
|
|
// Usage:
|
|
// .sample { light-or-dark: light-or-dark(#c00); }
|
|
// Output:
|
|
// .sample { light-or-dark: "light"; }
|
|
//
|
|
@function light-or-dark($color) {
|
|
$light-contrast: contrast-ratio($color, $white);
|
|
$dark-contrast: contrast-ratio($color, $black);
|
|
|
|
@if $light-contrast > $dark-contrast {
|
|
// Contrast against white is higher than against black, so, this is a dark color
|
|
@return "dark";
|
|
} @else {
|
|
@return "light";
|
|
}
|
|
}
|
|
|
|
// @function color-contrast($color)
|
|
// Helper: Returns black or white compared to a color, whichever produces the highest contrast
|
|
// Usage:
|
|
// .sample {
|
|
// background-color: #c00;
|
|
// color: color-contrast(#c00);
|
|
// }
|
|
// Output:
|
|
// .sample {
|
|
// background-color: #c00;
|
|
// color: #fff;
|
|
// }
|
|
//
|
|
// Named for the forthcoming CSS Module 5 spec function color-contrast();
|
|
// See https://developer.mozilla.org/en-US/docs/Web/CSS/color_value/color-contrast()
|
|
//
|
|
@function color-contrast($color) {
|
|
$color-lod: light-or-dark($color);
|
|
|
|
@if ($color-lod == "dark") {
|
|
@return $white;
|
|
} @else {
|
|
@return $black;
|
|
}
|
|
}
|
|
|
|
// @function a11y-color($fg, $bg, $level: 'AA', $size: 16, $bold: false)
|
|
// Goal: Return a color that passes for the chosen WCAG level without changing the Hue of the color
|
|
// Usage:
|
|
// .sample {
|
|
// background-color: #000;
|
|
// color: a11y-color(#c0c, #000);
|
|
// }
|
|
// Output:
|
|
// .sample {
|
|
// background-color: #000;
|
|
// color: #d200d2;
|
|
// }
|
|
//
|
|
@function a11y-color($fg, $bg, $level: "AA", $size: 16, $bold: false) {
|
|
// Helper: make sure the font size value is acceptable
|
|
$font-size: validate-font-size($size);
|
|
// Helper: With the level, font size, and bold boolean, return the proper target ratio. 3.0, 4.5, or 7.0 expected
|
|
$ratio: get-ratio($level, $font-size, $bold);
|
|
// Calculate the first contrast ratio of the given pair
|
|
$original-contrast: contrast-ratio($fg, $bg);
|
|
|
|
@if $original-contrast >= $ratio {
|
|
// If we pass the ratio already, return the original color
|
|
@return $fg;
|
|
} @else {
|
|
// Doesn't pass. Time to get to work
|
|
// Should the color be lightened or darkened?
|
|
$fg-lod: light-or-dark($fg);
|
|
$bg-lod: light-or-dark($bg);
|
|
|
|
// Set a "step" value to lighten or darken a color
|
|
// Note: Higher percentage steps means faster compile time, but we might overstep the required threshold too far with something higher than 4%
|
|
$step: 1%;
|
|
|
|
// Run through some cases where we want to darken, or use a negative step value
|
|
@if $fg-lod == "light" and $bg-lod == "light" {
|
|
// Both are light colors, darken the fg
|
|
$step: -$step;
|
|
} @else if $fg-lod == "dark" and $bg-lod == "light" {
|
|
// bg is light, fg is dark but does not pass, darken more
|
|
$step: -$step;
|
|
}
|
|
// Keeping the rest of the logic here, but our default values do not change, so this logic is not needed
|
|
//@else if $fg-lod == 'light' and $bg-lod == 'dark' {
|
|
// // bg is dark, fg is light but does not pass, lighten further
|
|
// $step: $step;
|
|
//} @else if $fg-lod == 'dark' and $bg-lod == 'dark' {
|
|
// // Both are dark, so lighten the fg
|
|
// $step: $step;
|
|
//}
|
|
|
|
// The magic happens here
|
|
// Loop through with a @while statement until the color combination passes our required ratio. Scale the color by our step value until the expression is false
|
|
// This might loop 100 times or more depending on the colors
|
|
@while contrast-ratio($fg, $bg) < $ratio {
|
|
$sat-step: 0%;
|
|
@if saturation($fg) > 10 {
|
|
$sat-step: $step;
|
|
}
|
|
$fg: scale-color($fg, $lightness: $step, $saturation: $sat-step);
|
|
}
|
|
@return $fg;
|
|
}
|
|
}
|