Table of Contents
Browser Support
As of this writing, Firefox does not support relative oklch()
and rgb()
functions, while Safari has partial support. The color-mix()
function is supported in all browsers.
The Code
scss
@use "sass:math";
@function oklch-gray-shade($shade, $opacity: 1) {
$lightness: math.percentage(math.div(1000 - $shade, 1000));
@return oklch(from gray $lightness 0 0 / $opacity);
}
/* Based on https://blog.logrocket.com/oklch-css-consistent-accessible-color-palettes/#creating-color-palettes-with-oklch */
@function oklch-color-shade($color, $shade, $opacity: 1) {
$lightness: math.percentage(math.div(1000 - $shade, 1000));
@if $shade >= 500 {
$chroma: (0.17 - 0.05) * math.div(900 - $shade, 400) + 0.05;
@return oklch(from $color $lightness $chroma h / $opacity);
} @else {
$chroma: (0.05 - 0.17) * math.div(500 - $shade, 400) + 0.17;
@return oklch(from $color $lightness $chroma h / $opacity);
}
}
@function rgb-color-shade($color, $shade, $opacity: 1) {
@if $shade >= 500 {
$calc: math.div(1000 - $shade, 500);
@return rgb(from $color calc(r * $calc) calc(g * $calc) calc(b * $calc) / $opacity);
} @else {
$calc: math.div(500 - $shade, 500);
@return rgb(from $color calc((255 - r) * $calc + r) calc((255 - g) * $calc + g) calc((255 - b) * $calc + b) / $opacity);
}
}
@function srgb-color-mix($color, $shade) {
@if $shade >= 500 {
$percentage: math.percentage(math.div(1000 - $shade, 500));
@return color-mix(in srgb, $color $percentage, black);
} @else {
$percentage: math.percentage(math.div(500 - $shade, 500));
@return color-mix(in srgb, $color, white $percentage);
}
}
@mixin alert-box() {
.alert-box {
position: relative;
margin-bottom: 1rem;
border-radius: 0.25rem;
border-width: 1px;
border-style: solid;
border-color: grey;
color: grey;
padding: 0.5rem;
padding-left: 3.5rem;
a {
color: white !important;
}
&.success {
--color: limegreen;
&::before {
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10s10-4.5 10-10S17.5 2 12 2m0 18c-4.41 0-8-3.59-8-8s3.59-8 8-8s8 3.59 8 8s-3.59 8-8 8m4.59-12.42L10 14.17l-2.59-2.58L6 13l4 4l8-8z'/%3E%3C/svg%3E");
}
}
&.error {
--color: red;
&::before {
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath fill='red' d='M8.27 3L3 8.27v7.46L8.27 21h7.46C17.5 19.24 21 15.73 21 15.73V8.27L15.73 3M9.1 5h5.8L19 9.1v5.8L14.9 19H9.1L5 14.9V9.1m4.12-1.39L7.71 9.12L10.59 12l-2.88 2.88l1.41 1.41L12 13.41l2.88 2.88l1.41-1.41L13.41 12l2.88-2.88l-1.41-1.41L12 10.59'/%3E%3C/svg%3E");
}
}
&.warning {
--color: gold;
&::before {
--svg: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24'%3E%3Cpath d='M12 2L1 21h22M12 6l7.53 13H4.47M11 10v4h2v-4m-2 6v2h2v-2'/%3E%3C/svg%3E");
}
}
&.info {
--color: deepskyblue;
}
&::before {
content: "";
position: absolute;
top: 0.5rem;
left: 1rem;
width: 24px;
height: 24px;
background-color: currentColor;
mask-image: var(
--svg,
url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23000' d='M11 7v2h2V7zm3 10v-2h-1v-4h-3v2h1v2h-1v2zm8-5c0 5.5-4.5 10-10 10S2 17.5 2 12S6.5 2 12 2s10 4.5 10 10m-2 0c0-4.42-3.58-8-8-8s-8 3.58-8 8s3.58 8 8 8s8-3.58 8-8'/%3E%3C/svg%3E")
);
mask-repeat: no-repeat;
mask-size: 100% 100%;
}
@supports (color: oklch(from white l c h)) {
&:is(&, p):is(.oklch) {
color: oklch-color-shade(var(--color, black), 100);
border-color: oklch-color-shade(var(--color, black), 500);
background: oklch-color-shade(var(--color, black), 600);
}
&:is(.oklch) ::selection {
color: oklch-color-shade(var(--color, black), 100);
background: oklch-color-shade(var(--color, black), 500);
}
}
@supports (color: rgb(from white r g b)) {
&:is(&, p):is(.rgb) {
color: rgb-color-shade(var(--color, black), 100);
border-color: rgb-color-shade(var(--color, black), 700);
background: rgb-color-shade(var(--color, black), 800);
}
&:is(.rgb) ::selection {
color: rgb-color-shade(var(--color, black), 100);
background: rgb-color-shade(var(--color, black), 700);
}
}
@supports (color: color-mix(in srgb, red 0%, white)) {
&:is(&, p):is(.color-mix) {
color: srgb-color-mix(var(--color, black), 100);
border-color: srgb-color-mix(var(--color, black), 700);
background: srgb-color-mix(var(--color, black), 800);
}
&:is(.color-mix) ::selection {
color: srgb-color-mix(var(--color, black), 100);
background: srgb-color-mix(var(--color, black), 700);
}
}
}
}
@include alert-box();
Light Mode Example
scss
@supports (color: oklch(from white l c h)) {
&:is(&, p):is(.oklch) {
color: oklch-color-shade(var(--color, black), 600);
border-color: oklch-color-shade(var(--color, black), 250);
background: oklch-color-shade(var(--color, black), 50);
}
&:is(.oklch) ::selection {
color: oklch-color-shade(var(--color, black), 600);
background: oklch-color-shade(var(--color, black), 150);
}
}
@supports (color: rgb(from white r g b)) {
&:is(&, p):is(.rgb) {
color: rgb-color-shade(var(--color, black), 800);
border-color: rgb-color-shade(var(--color, black), 300);
background: rgb-color-shade(var(--color, black), 100);
}
&:is(.rgb) ::selection {
color: rgb-color-shade(var(--color, black), 900);
background: rgb-color-shade(var(--color, black), 300);
}
}
@supports (color: color-mix(in srgb, red 0%, white)) {
&:is(&, p):is(.color-mix) {
color: srgb-color-mix(var(--color, black), 800);
border-color: srgb-color-mix(var(--color, black), 300);
background: srgb-color-mix(var(--color, black), 100);
}
&:is(.color-mix) ::selection {
color: srgb-color-mix(var(--color, black), 900);
background: srgb-color-mix(var(--color, black), 300);
}
}