<aside class="sliders">
<input type="range" id="red" min="0" max="255" value="128" step="1">
<input type="range" id="green" min="0" max="255" value="40" step="1">
<input type="range" id="blue" min="0" max="255" value="220" step="1">
</aside>
<main class="buttons">
<div>
<h2>sRBG Luma 方法</h2>
<button class="btn">我是一个按钮</button>
</div>
<div>
<h2>W3C Luma 方法</h2>
<button class="btn btn--w3c">我是一个按钮</button>
</div>
</main>
/*
The challenge:
1) Set text to either black or white depending on the element background perceived lightness (luma)
2) Set a border as a darker variation of the base color to improve button visibility, ONLY if background luma is really high
3) Automatically generate a secondary, 60º rotated hue color
*/
:root {
/* theme color variables to use in RGB declarations */
--red: 128;
--green: 40;
--blue: 220;
/*the threshold at which colors are considered "light".
Range: decimals from 0 to 1,
recommended 0.5 - 0.6*/
--threshold: 0.6;
/*the threshold at which a darker border will be applied.
Range: decimals from 0 to 1,
recommended 0.8+*/
--border-threshold: 0.8;
}
.btn {
/*
Calcs perceived brightness using the
sRGB Luma method
lightness = (red * 0.2126 + green * 0.7152 + blue * 0.0722) / 255
*/
--r: calc(var(--red) * 0.2126);
--g: calc(var(--green) * 0.7152);
--b: calc(var(--blue) * 0.0722);
--lightness: calc((var(--r) + var(--g) + var(--b)) / 255);
/*
1) Any lightness value above the threshold will be considered "light", therefore apply a black text color. Any bellow will be considered dark, and use white color.
This results from appying either a sub-zero (negative) or a higher-than-100 lightness value, which are capped to 0 and 100 respectively, to a HSL declaration
*/
color: hsl(0, 0%, calc((var(--lightness) - var(--threshold)) * -10000000%));
/*
2) sets the border as a 50% darker shade of the base color, ONLY if background color luma is higher than the border threshold.
To achieve this I use the same sub-zero or higher-than-max technique, only this time using the Alpha value from an RGBA declaration.
This results in a border that's either fully transparent or fully opaque
*/
--border-alpha: calc((var(--lightness) - var(--border-threshold)) * 100);
border-color: rgba(calc(var(--red) - 50), calc(var(--green) - 50), calc(var(--blue) - 50), var(--border-alpha));
/* 3)sets the background for the base class*/
background: rgb(var(--red), var(--green), var(--blue));
}
.btn--w3c{
/* Alternative calc using the
W3C luma method
lightness = (red * 0.299 + green * 0.587 + blue * 0.114) / 255
*/
--r: calc(var(--red) * 0.299);
--g: calc(var(--green) * 0.587);
--b: calc(var(--blue) * 0.114);
}
/* just basic styles*/
body{
background: honeydew;
padding:1rem;
max-width: 600px;
margin: auto;
}
.sliders{
display:grid;
grid-template-columns: repeat(auto-fit, minmax(150px,1fr));
grid-gap:1em;
}
.buttons{
display:grid;
grid-template-columns: repeat(auto-fit, minmax(200px,1fr));
grid-gap: 1em;
}
.btn{
padding: 1em;
font-size: 1.5rem;
border-radius: 0.2em;
box-sizing: border-box;
width:100%;
border-width: .2em;
border-style: solid;
}
input[type=range]{
display:flex;
flex-direction:column;
color: black;
text-align:center;
margin: 10px;
font-weight:600;
}
input::before{
content: attr(id);
text-transform: capitalize;
}
input[id=red]::after{
counter-reset: red var(--red);
content: counter(red);
}
input[id=green]::after{
counter-reset: green var(--green);
content: counter(green);
}
input[id=blue]::after{
counter-reset: blue var(--blue);
content: counter(blue);
}
/*
JS is used only to set the CSS custom properties used in the base color
Color changes are calculated in CSS
*/
const root = document.documentElement;
const inputs = [].slice.call(document.querySelectorAll('input'));
inputs.forEach(input => input.addEventListener('change', handleUpdate));
inputs.forEach(input => input.addEventListener('mousemove', handleUpdate));
function handleUpdate(e) {
if (this.id === 'red') root.style.setProperty('--red', this.value);
if (this.id === 'green') root.style.setProperty('--green', this.value);
if (this.id === 'blue') root.style.setProperty('--blue', this.value);
}
This Pen doesn't use any external CSS resources.
This Pen doesn't use any external JavaScript resources.