<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);
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

This Pen doesn't use any external JavaScript resources.