zenn.dev でコメントいただいたので検証してみました。
テストしてアスペクト比4.5未満の色がないかをチェックします。 (ただし約1677万色をチェックするので少し時間がかかります)
$ node test.js
npm でこういったパッケージは沢山あるようなので、こういうのを使うのもありかな。 ただ自作しても大した手間ではないし、カスタマイズもできるから自作でも良いかも。
zenn.dev でコメントいただいたので検証してみました。
テストしてアスペクト比4.5未満の色がないかをチェックします。 (ただし約1677万色をチェックするので少し時間がかかります)
$ node test.js
npm でこういったパッケージは沢山あるようなので、こういうのを使うのもありかな。 ただ自作しても大した手間ではないし、カスタマイズもできるから自作でも良いかも。
<!doctype html> | |
<html lang="ja"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, user-scalable=no"> | |
<meta http-equiv="X-UA-Compatible" content="ie=edge"> | |
<title>背景色をベースに文字色を判定してみる</title> | |
<style type="text/css"> | |
span { | |
display: inline-block; | |
font-family: Menlo, Monaco, Consolas, "Courier New", monospace; | |
margin: 0.5rem; | |
padding: 1rem 2rem; | |
} | |
</style> | |
</head> | |
<body> | |
<p>※CR: Contrast Ratio</p> | |
<div id="colors"></div> | |
<script type="text/javascript"> | |
const BLACK = {red: 0, green: 0, blue: 0} | |
const WHITE = {red: 255, green: 255, blue: 255} | |
const getRGBForCalculateLuminance = (_color) => { | |
const color = _color / 255 | |
if (color <= 0.03928) { | |
return color / 12.92; | |
} else { | |
return Math.pow(((color + 0.055) / 1.055), 2.4); | |
} | |
} | |
const getRelativeLuminance = (color) => { | |
const {red, green, blue} = color | |
let R = getRGBForCalculateLuminance(red); | |
let G = getRGBForCalculateLuminance(green); | |
let B = getRGBForCalculateLuminance(blue); | |
return 0.2126 * R + 0.7152 * G + 0.0722 * B; | |
} | |
const getContrastRatio = (color1, color2) => { | |
const luminance1 = getRelativeLuminance(color1); | |
const luminance2 = getRelativeLuminance(color2); | |
const bright = Math.max(luminance1, luminance2); | |
const dark = Math.min(luminance1, luminance2); | |
return (bright + 0.05) / (dark + 0.05); | |
} | |
const getFontColor = (color) => { | |
// 元の計算方法 | |
// const brightness = (color.red * 0.299) + (color.green * 0.587) + (color.blue * 0.114) | |
// return brightness >= 140 ? BLACK : WHITE | |
const whiteRatio = getContrastRatio(color, WHITE) | |
const blackRatio = getContrastRatio(color, BLACK) | |
return whiteRatio > blackRatio ? WHITE : BLACK | |
} | |
const toHexColor = ({red, green, blue}) => ([ | |
`0${Number(red).toString(16)}`.slice(-2), | |
`0${Number(green).toString(16)}`.slice(-2), | |
`0${Number(blue).toString(16)}`.slice(-2), | |
]).join('') | |
const bases = ['00', '11', '22', '33', '44', '55', '66', '77', '88', '99', 'AA', 'BB', 'CC', 'DD', 'EE', 'FF']; | |
const colors = []; | |
bases.forEach((red) => { | |
bases.forEach((green) => { | |
bases.forEach((blue) => { | |
const backgroundColor = {red: parseInt(red, 16), green: parseInt(green, 16), blue: parseInt(blue, 16)} | |
const color = getFontColor(backgroundColor) | |
const contrast = getContrastRatio(backgroundColor, color) | |
colors.push({background: `${red}${green}${blue}`, color: toHexColor(color), contrast}) | |
}) | |
}) | |
}) | |
document.getElementById("colors").innerHTML = colors.map(({background, color, contrast}) => { | |
return `<span style="background-color: #${background}; color: #${color}">#${background} (CR:${Math.round(contrast * 100) / 100})</span>` | |
}).join('') | |
</script> | |
</body> | |
</html> |
const BLACK = {red: 0, green: 0, blue: 0} | |
const WHITE = {red: 255, green: 255, blue: 255} | |
const getRGBForCalculateLuminance = (_color) => { | |
const color = _color / 255 | |
if (color <= 0.03928) { | |
return color / 12.92; | |
} else { | |
return Math.pow(((color + 0.055) / 1.055), 2.4); | |
} | |
} | |
const getRelativeLuminance = (color) => { | |
const {red, green, blue} = color | |
let R = getRGBForCalculateLuminance(red); | |
let G = getRGBForCalculateLuminance(green); | |
let B = getRGBForCalculateLuminance(blue); | |
return 0.2126 * R + 0.7152 * G + 0.0722 * B; | |
} | |
const getContrastRatio = (color1, color2) => { | |
const luminance1 = getRelativeLuminance(color1); | |
const luminance2 = getRelativeLuminance(color2); | |
const bright = Math.max(luminance1, luminance2); | |
const dark = Math.min(luminance1, luminance2); | |
return (bright + 0.05) / (dark + 0.05); | |
} | |
const getFontColor = (color) => { | |
// 元の計算方法 | |
// const brightness = (color.red * 0.299) + (color.green * 0.587) + (color.blue * 0.114) | |
// return brightness >= 140 ? BLACK : WHITE | |
const whiteRatio = getContrastRatio(color, WHITE) | |
const blackRatio = getContrastRatio(color, BLACK) | |
return whiteRatio > blackRatio ? WHITE : BLACK | |
} | |
const toHexColor = ({red, green, blue}) => ([ | |
`0${Number(red).toString(16)}`.slice(-2), | |
`0${Number(green).toString(16)}`.slice(-2), | |
`0${Number(blue).toString(16)}`.slice(-2), | |
]).join('') | |
module.exports = { | |
getFontColor, | |
getContrastRatio, | |
toHexColor, | |
} |
const {getFontColor, getContrastRatio, toHexColor} = require('./index'); | |
const STEP = 1 | |
const THRESHOLD = 4.5 | |
const SHOW_RESULT_DETAILS = false | |
const test = () => { | |
const backgroundColors = []; | |
for (let red = 0; red <= 255; red += STEP) { | |
for (let green = 0; green <= 255; green += STEP) { | |
for (let blue = 0; blue <= 255; blue += STEP) { | |
backgroundColors.push({red, green, blue}) | |
} | |
} | |
} | |
const invalidColors = []; | |
let minContrastRatio = Number.MAX_SAFE_INTEGER; | |
backgroundColors.forEach((bgColor) => { | |
const fontColor = getFontColor(bgColor) | |
const ratio = getContrastRatio(bgColor, fontColor) | |
if (ratio < minContrastRatio) { | |
minContrastRatio = ratio | |
} | |
if (ratio < THRESHOLD) { | |
invalidColors.push(bgColor) | |
} | |
}) | |
console.log('') | |
console.log(`\u001b[31m[コントラスト比が${THRESHOLD}未満の色]\u001b[0m`) | |
console.log(`${invalidColors.length}/${backgroundColors.length}色 (${Math.round((invalidColors.length / backgroundColors.length) * 100)}%)`) | |
if (SHOW_RESULT_DETAILS) { | |
invalidColors.forEach((invalidColor) => { | |
console.log(`* ${toHexColor(invalidColor)} (コントラスト比: ${getContrastRatio(invalidColor, getFontColor(invalidColor))})`) | |
}) | |
} | |
console.log('') | |
console.log("[最小コントラスト比]") | |
console.log(minContrastRatio) | |
console.log('') | |
} | |
test() |