Skip to content

Instantly share code, notes, and snippets.

@hellais
Last active October 20, 2023 10:33
Show Gist options
  • Save hellais/0e57c0fb7f445b3aea9e0443ce5396e8 to your computer and use it in GitHub Desktop.
Save hellais/0e57c0fb7f445b3aea9e0443ce5396e8 to your computer and use it in GitHub Desktop.
Dark or Light color picker based on luminace
gray0 (#f8f9fa): #000000
gray1 (#f1f3f5): #000000
gray2 (#e9ecef): #000000
gray3 (#dee2e6): #000000
gray4 (#ced4da): #000000
gray5 (#adb5bd): #000000
gray6 (#868e96): #000000
gray7 (#495057): #ffffff
gray8 (#343a40): #ffffff
gray9 (#212529): #ffffff
blue0 (#e7f5ff): #000000
blue1 (#c9e8ff): #000000
blue2 (#8dd5f8): #000000
blue3 (#5db8fe): #000000
blue4 (#37a6ed): #000000
blue5 (#0588cb): #000000
blue6 (#0f77b8): #ffffff
blue7 (#056aa6): #ffffff
blue8 (#005f9c): #ffffff
blue9 (#005a99): #ffffff
indigo0 (#edf2ff): #000000
indigo1 (#dbe4ff): #000000
indigo2 (#bac8ff): #000000
indigo3 (#91a7ff): #000000
indigo4 (#748ffc): #000000
indigo5 (#5c7cfa): #000000
indigo6 (#4c6ef5): #000000
indigo7 (#4263eb): #ffffff
indigo8 (#3b5bdb): #ffffff
indigo9 (#364fc7): #ffffff
violet0 (#f3f0ff): #000000
violet1 (#e5dbff): #000000
violet2 (#d0bfff): #000000
violet3 (#b197fc): #000000
violet4 (#9775fa): #000000
violet5 (#845ef7): #000000
violet6 (#7950f2): #ffffff
violet7 (#7048e8): #ffffff
violet8 (#6741d9): #ffffff
violet9 (#5f3dc4): #ffffff
fuchsia0 (#f8f0fc): #000000
fuchsia1 (#f3d9fa): #000000
fuchsia2 (#eebefa): #000000
fuchsia3 (#e599f7): #000000
fuchsia4 (#da77f2): #000000
fuchsia5 (#cc5de8): #000000
fuchsia6 (#be4bdb): #000000
fuchsia7 (#ae3ec9): #ffffff
fuchsia8 (#9c36b5): #ffffff
fuchsia9 (#862e9c): #ffffff
pink0 (#fff0f6): #000000
pink1 (#ffdeeb): #000000
pink2 (#fcc2d7): #000000
pink3 (#faa2c1): #000000
pink4 (#f783ac): #000000
pink5 (#f06595): #000000
pink6 (#e64980): #000000
pink7 (#d6336c): #000000
pink8 (#c2255c): #ffffff
pink9 (#a61e4d): #ffffff
red0 (#fff5f5): #000000
red1 (#ffe3e3): #000000
red2 (#ffc9c9): #000000
red3 (#ffa8a8): #000000
red4 (#ff8787): #000000
red5 (#ff6b6b): #000000
red6 (#fa5252): #000000
red7 (#f03e3e): #000000
red8 (#e03131): #000000
red9 (#c92a2a): #ffffff
orange0 (#fff4e6): #000000
orange1 (#ffe8cc): #000000
orange2 (#ffd8a8): #000000
orange3 (#ffc078): #000000
orange4 (#ffa94d): #000000
orange5 (#ff922b): #000000
orange6 (#fd7e14): #000000
orange7 (#f76707): #000000
orange8 (#e8590c): #000000
orange9 (#d9480f): #000000
yellow0 (#fff9db): #000000
yellow1 (#fff3bf): #000000
yellow2 (#ffec99): #000000
yellow3 (#ffe066): #000000
yellow4 (#ffd43b): #000000
yellow5 (#fcc419): #000000
yellow6 (#fab005): #000000
yellow7 (#f59f00): #000000
yellow8 (#f08c00): #000000
yellow9 (#e67700): #000000
lime0 (#f4fce3): #000000
lime1 (#e9fac8): #000000
lime2 (#d8f5a2): #000000
lime3 (#c0eb75): #000000
lime4 (#a9e34b): #000000
lime5 (#94d82d): #000000
lime6 (#82c91e): #000000
lime7 (#74b816): #000000
lime8 (#66a80f): #000000
lime9 (#5c940d): #000000
green0 (#ebfbee): #000000
green1 (#d3f9d8): #000000
green2 (#b2f2bb): #000000
green3 (#8ce99a): #000000
green4 (#69db7c): #000000
green5 (#51cf66): #000000
green6 (#40c057): #000000
green7 (#37b24d): #000000
green8 (#2f9e44): #000000
green9 (#2b8a3e): #000000
teal0 (#e6fcf5): #000000
teal1 (#c3fae8): #000000
teal2 (#96f2d7): #000000
teal3 (#63e6be): #000000
teal4 (#38d9a9): #000000
teal5 (#20c997): #000000
teal6 (#12b886): #000000
teal7 (#0ca678): #000000
teal8 (#099268): #000000
teal9 (#087f5b): #ffffff
cyan0 (#e3fafc): #000000
cyan1 (#c5f6fa): #000000
cyan2 (#99e9f2): #000000
cyan3 (#66d9e8): #000000
cyan4 (#3bc9db): #000000
cyan5 (#22b8cf): #000000
cyan6 (#15aabf): #000000
cyan7 (#1098ad): #000000
cyan8 (#0c8599): #000000
cyan9 (#0b7285): #ffffff
const hexToRGB = (hex) => {
/* Convert an RGB value (eg. #FFFFFF) into it's rgb triplet expressed as
* floating points.
* */
// Strip the leading # from the hex code
hex = hex.replace(/^#?([0-9a-f]{6})$/i, '$1')
// Convert the hex into a number
hex = Number(`0x${hex}`)
// Simple bit shifting to slice off each component from the hex map
return {
r: parseFloat(hex >> 16 & 0xff) / 255.0,
g: parseFloat(hex >> 8 & 0xff) / 255.0,
b: parseFloat(hex & 0xff) / 255.0
}
}
const sRGBTransform = (c) => {
/* Convert an sRGB to the correct color space in order to compute luminance formula.
* c is the color as float.
* */
if (c <= 0.03928) {
return c/12.92
}
return Math.pow(c+0.055/1.055, 2.4)
}
const getLuminance = ({r, g, b}) => {
/*
* Returns the luminance of an RGB triplet. Expects the r,g,b values to be
* already floating point numbers in the range of 0-1.
* */
return 0.2126 * sRGBTransform(r) + 0.7152 * sRGBTransform(g) + 0.0722 * sRGBTransform(b)
}
const contrastRatio = (a, b) => {
/*
* Calculate contrast ratio as per WCAG 2.0
* https://www.w3.org/TR/WCAG20-TECHS/G17.html
* the lighter color is l1, the darker is l2
*
* a and b are luminance values for the two colors
* */
l1 = b
l2 = a
if (a > b) {
l1 = a
l2 = b
}
return (l1 + 0.05) / (l2 + 0.05)
}
const pickTextColor = (color, darkColor, lightColor) => {
/* Returns the value of the prefered color between darkColor and lightColor
* to maximise the contrast against the color.
* Colors are expressed in thier RGB values as a hex string (eg. #FFCCDD).
* Returns the hex string of the chosen color between dark and light.
* */
let darkLum = getLuminance(hexToRGB(darkColor))
let lightLum = getLuminance(hexToRGB(lightColor))
let colorLum = getLuminance(hexToRGB(color))
const darkContrast = contrastRatio(colorLum, darkLum)
const lightContrast = contrastRatio(colorLum, lightLum)
if (contrastRatio(colorLum, darkLum) > contrastRatio(colorLum, lightLum)) {
return darkColor
}
return lightColor
}
let colors = {
white:"#ffffff",
black: "#000000",
ooniblue: "#0588cb",
gray1: "#f1f3f5",
violet5: "#845ef7",
lime9: "#5c940d",
cyan9: "#0b7285"
}
let whiteLum = getLuminance(hexToRGB(colors.white))
let blackLum = getLuminance(hexToRGB(colors.black))
let ooniblueLum = getLuminance(hexToRGB(colors.ooniblue))
console.log(`Luminance of ${colors.white}: ${whiteLum}`)
console.log(`Luminance of ${colors.black}: ${blackLum}`)
console.log(`Luminance of ${colors.ooniblue}: ${ooniblueLum}`)
Object.keys(colors).forEach((name) => {
let chosen = pickTextColor(colors[name], colors.black, colors.white)
console.log(`${name} (${colors[name]}): ${chosen}`)
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment