Files
bqkc/_sass/_theme-colors.scss
Nasir Anthony Montalvo 526096840e Initial commit
2025-11-13 14:48:58 -06:00

291 lines
10 KiB
SCSS

/*
This is a modified version of Bootstrap Sass for customizing theme colors.
It is intended to create an easy means to override basic theme colors for btn-, btn-outline-, text-, and bg- classes.
It is not complete Bootstrap theming!
*/
/* sass needed */
@use 'sass:meta';
@use 'sass:color';
@use "sass:map";
@use 'sass:math';
@use 'sass:list';
/* set up base variable to override from cb.scss */
$link-color-base: "" !default;
$theme-colors: () !default;
/* variables needed from Bootstrap */
$white: #fff !default;
$black: #000 !default;
$primary: #0d6efd !default;
$min-contrast-ratio: 4.5 !default;
$color-contrast-dark: $black !default;
$color-contrast-light: $white !default;
$component-active-color: $white !default;
$component-active-bg: $primary !default;
$link-shade-percentage: 20% !default;
$link-hover-color: shift-color($link-color-base, $link-shade-percentage) !default;
$btn-focus-width: .25rem !default;
$btn-transition: color .15s ease-in-out, background-color .15s ease-in-out, border-color .15s ease-in-out, box-shadow .15s ease-in-out !default;
$btn-hover-bg-shade-amount: 15% !default;
$btn-hover-bg-tint-amount: 15% !default;
$btn-hover-border-shade-amount: 20% !default;
$btn-hover-border-tint-amount: 10% !default;
$btn-active-bg-shade-amount: 20% !default;
$btn-active-bg-tint-amount: 20% !default;
$btn-active-border-shade-amount: 25% !default;
$btn-active-border-tint-amount: 10% !default;
/* functions needed from Bootstrap */
// Tint a color: mix a color with white
@function tint-color($color, $weight) {
@return color.mix(white, $color, $weight);
}
// Shade a color: mix a color with black
@function shade-color($color, $weight) {
@return color.mix(black, $color, $weight);
}
// divide
@function divide($dividend, $divisor, $precision: 10) {
$sign: if($dividend > 0 and $divisor > 0 or $dividend < 0 and $divisor < 0, 1, -1);
$dividend: abs($dividend);
$divisor: abs($divisor);
@if $dividend == 0 {
@return 0;
}
@if $divisor == 0 {
@error "Cannot divide by 0";
}
$remainder: $dividend;
$result: 0;
$factor: 10;
@while ($remainder > 0 and $precision >= 0) {
$quotient: 0;
@while ($remainder >= $divisor) {
$remainder: $remainder - $divisor;
$quotient: $quotient + 1;
}
$result: $result * 10 + $quotient;
$factor: $factor * .1;
$remainder: $remainder * 10;
$precision: $precision - 1;
@if ($precision < 0 and $remainder >= $divisor * 5) {
$result: $result + 1;
}
}
$result: $result * $factor * $sign;
$dividend-unit: math.unit($dividend);
$divisor-unit: math.unit($divisor);
$unit-map: (
"px": 1px,
"rem": 1rem,
"em": 1em,
"%": 1%
);
@if ($dividend-unit != $divisor-unit and map-has-key($unit-map, $dividend-unit)) {
$result: $result * map.get($unit-map, $dividend-unit);
}
@return $result;
}
// A list of pre-calculated numbers of pow(divide((divide($value, 255) + .055), 1.055), 2.4). (from 0 to 255)
$_luminance-list: .0008 .001 .0011 .0013 .0015 .0017 .002 .0022 .0025 .0027 .003 .0033 .0037 .004 .0044 .0048 .0052 .0056 .006 .0065 .007 .0075 .008 .0086 .0091 .0097 .0103 .011 .0116 .0123 .013 .0137 .0144 .0152 .016 .0168 .0176 .0185 .0194 .0203 .0212 .0222 .0232 .0242 .0252 .0262 .0273 .0284 .0296 .0307 .0319 .0331 .0343 .0356 .0369 .0382 .0395 .0409 .0423 .0437 .0452 .0467 .0482 .0497 .0513 .0529 .0545 .0561 .0578 .0595 .0612 .063 .0648 .0666 .0685 .0704 .0723 .0742 .0762 .0782 .0802 .0823 .0844 .0865 .0887 .0908 .0931 .0953 .0976 .0999 .1022 .1046 .107 .1095 .1119 .1144 .117 .1195 .1221 .1248 .1274 .1301 .1329 .1356 .1384 .1413 .1441 .147 .15 .1529 .1559 .159 .162 .1651 .1683 .1714 .1746 .1779 .1812 .1845 .1878 .1912 .1946 .1981 .2016 .2051 .2086 .2122 .2159 .2195 .2232 .227 .2307 .2346 .2384 .2423 .2462 .2502 .2542 .2582 .2623 .2664 .2705 .2747 .2789 .2831 .2874 .2918 .2961 .3005 .305 .3095 .314 .3185 .3231 .3278 .3325 .3372 .3419 .3467 .3515 .3564 .3613 .3663 .3712 .3763 .3813 .3864 .3916 .3968 .402 .4072 .4125 .4179 .4233 .4287 .4342 .4397 .4452 .4508 .4564 .4621 .4678 .4735 .4793 .4851 .491 .4969 .5029 .5089 .5149 .521 .5271 .5333 .5395 .5457 .552 .5583 .5647 .5711 .5776 .5841 .5906 .5972 .6038 .6105 .6172 .624 .6308 .6376 .6445 .6514 .6584 .6654 .6724 .6795 .6867 .6939 .7011 .7084 .7157 .7231 .7305 .7379 .7454 .7529 .7605 .7682 .7758 .7835 .7913 .7991 .807 .8148 .8228 .8308 .8388 .8469 .855 .8632 .8714 .8796 .8879 .8963 .9047 .9131 .9216 .9301 .9387 .9473 .956 .9647 .9734 .9823 .9911 1;
@function color-contrast(
$background, $color-contrast-dark: $color-contrast-dark, $color-contrast-light: $color-contrast-light, $min-contrast-ratio: $min-contrast-ratio
) {
$foregrounds: $color-contrast-light, $color-contrast-dark, $white, $black;
$max-ratio: 0;
$max-ratio-color: null;
@each $color in $foregrounds {
$contrast-ratio: contrast-ratio($background, $color);
@if $contrast-ratio > $min-contrast-ratio {
@return $color;
} @else if $contrast-ratio > $max-ratio {
$max-ratio: $contrast-ratio;
$max-ratio-color: $color;
}
}
@warn "Found no color leading to #{$min-contrast-ratio}:1 contrast ratio against #{$background}...";
@return $max-ratio-color;
}
@function contrast-ratio($background, $foreground: $color-contrast-light) {
$l1: luminance($background);
$l2: luminance(opaque($background, $foreground));
@return if($l1 > $l2, divide($l1 + .05, $l2 + .05), divide($l2 + .05, $l1 + .05));
}
// Return opaque color
@function opaque($background, $foreground) {
@return color.mix(rgba($foreground, 1), $background, color.opacity($foreground) * 100%);
}
// Return WCAG2.0 relative luminance
@function luminance($color) {
$rgb: (
"r": color.channel($color, "red", $space: rgb),
"g": color.channel($color, "green", $space: rgb),
"b": color.channel($color, "blue", $space: rgb)
);
@each $name, $value in $rgb {
$value: if(divide($value, 255) < .03928, divide(divide($value, 255), 12.92), list.nth($_luminance-list, math.floor($value) + 1));
$rgb: map.merge($rgb, ($name: $value));
}
@return (map.get($rgb, "r") * .2126) + (map.get($rgb, "g") * .7152) + (map.get($rgb, "b") * .0722);
}
/*
custom mixin to generate buttons
*/
@mixin theme-button(
$background,
$border,
$color: color-contrast($background),
$hover-background: if($color == $color-contrast-light, shade-color($background, $btn-hover-bg-shade-amount), tint-color($background, $btn-hover-bg-tint-amount)),
$hover-border: if($color == $color-contrast-light, shade-color($border, $btn-hover-border-shade-amount), tint-color($border, $btn-hover-border-tint-amount)),
$hover-color: color-contrast($hover-background),
$active-background: if($color == $color-contrast-light, shade-color($background, $btn-active-bg-shade-amount), tint-color($background, $btn-active-bg-tint-amount)),
$active-border: if($color == $color-contrast-light, shade-color($border, $btn-active-border-shade-amount), tint-color($border, $btn-active-border-tint-amount)),
$active-color: color-contrast($active-background)
) {
color: $color;
background-color: $background;
border-color: $border;
&:hover {
color: $hover-color;
background-color: $hover-background;
border-color: $hover-border;
}
.btn-check:focus + &,
&:focus {
color: $hover-color;
background-color: $hover-background;
border-color: $hover-border;
box-shadow: 0 0 0 $btn-focus-width rgba(color.mix($color, $border, 15%), .5);
}
.btn-check:checked + &,
.btn-check:active + &,
&:active,
&.active,
.show > &.dropdown-toggle {
color: $active-color;
background-color: $active-background;
border-color: $active-border;
&:focus {
box-shadow: 0 0 0 $btn-focus-width rgba(color.mix($color, $border, 15%), .5);
}
}
}
/* custom mixin to create btn-outline */
@mixin theme-button-outline(
$color,
$color-hover: color-contrast($color),
$active-background: $color,
$active-border: $color,
$active-color: color-contrast($active-background)
) {
color: $color;
border-color: $color;
&:hover {
color: $color-hover;
background-color: $active-background;
border-color: $active-border;
}
.btn-check:focus + &,
&:focus {
box-shadow: 0 0 0 $btn-focus-width rgba($color, .5);
}
.btn-check:checked + &,
.btn-check:active + &,
&:active,
&.active,
&.dropdown-toggle.show {
color: $active-color;
background-color: $active-background;
border-color: $active-border;
&:focus {
box-shadow: 0 0 0 $btn-focus-width rgba($color, .5);
}
}
}
/* custom mixin to create text color variants */
@mixin theme-text($parent, $color) {
#{$parent} {
color: $color !important;
}
a#{$parent} {
&:hover, &:focus {
color: color.scale($color, $lightness: -30%) !important;
}
}
}
/* custom mixin to create background variants */
@mixin theme-bg($parent, $color) {
#{$parent} {
--bs-bg-opacity: 1;
background-color: rgba($color, var(--bs-bg-opacity))!important;
}
a#{$parent},
button#{$parent} {
&:hover, &:focus {
background-color: color.scale($color, $lightness: -20%) !important;
}
}
}
/* bg-opacity classes needed to override */
.bg-opacity-75 {
--bs-bg-opacity: 0.75 !important;
}
.bg-opacity-50 {
--bs-bg-opacity: 0.50 !important;
}
.bg-opacity-25 {
--bs-bg-opacity: 0.25 !important;
}
.bg-opacity-10 {
--bs-bg-opacity: 0.10 !important;
}
/*
each function to create all the buttons from the values passed by cb.scss
*/
@each $color, $value in $theme-colors {
/* check if value is valid color to avoid errors */
@if meta.type-of($value) == color {
/* btn classes */
.btn-#{$color} {
@include theme-button($value, $value);
}
.btn-outline-#{$color} {
@include theme-button-outline($value);
}
/* text classes */
@include theme-text(".text-#{$color}", $value);
/* bg classes */
@include theme-bg(".bg-#{$color}", $value);
}
}