Created
September 18, 2023 22:54
-
-
Save adamstddrd/436c40dfb1ea6eecd5940b6035e940d8 to your computer and use it in GitHub Desktop.
A theme picker custom element with a "random" option
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* ---------------------------------------------------------------------------- | |
switch between color themes | |
---------------------------------------------------------------------------- */ | |
export default class ThemePicker extends HTMLElement { | |
static randomInt(min, max) { | |
return Math.floor(Math.random() * (max - min + 1) + min); | |
} | |
static getLuminanace(values) { | |
const rgb = values.map((v) => { | |
const val = v / 255; | |
return val <= 0.03928 ? val / 12.92 : ((val + 0.055) / 1.055) ** 2.4; | |
}); | |
return Number((0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2]).toFixed(3)); | |
} | |
static RGBToHSL(r, g, b) { | |
const red = r / 255; | |
const green = g / 255; | |
const blue = b / 255; | |
const l = Math.max(red, green, blue); | |
const s = l - Math.min(red, green, blue); | |
const h = s | |
? l === red | |
? (green - blue) / s | |
: l === green | |
? 2 + (blue - red) / s | |
: 4 + (red - green) / s | |
: 0; | |
return [ | |
Math.round(60 * h < 0 ? 60 * h + 360 : 60 * h), | |
Math.round(100 * ( | |
s ? (l <= 0.5 ? s / (2 * l - s) : s / (2 - (2 * l - s))) : 0 | |
)), | |
Math.round((100 * (2 * l - s)) / 2), | |
]; | |
} | |
static generateRgb() { | |
const red = ThemePicker.randomInt(0, 255); | |
const green = ThemePicker.randomInt(0, 255); | |
const blue = ThemePicker.randomInt(0, 255); | |
return [red, green, blue]; | |
} | |
static getContrastRatio(colorA, colorB) { | |
const lumA = ThemePicker.getLuminanace(colorA); | |
const lumB = ThemePicker.getLuminanace(colorB); | |
return (Math.max(lumA, lumB) + 0.05) / (Math.min(lumA, lumB) + 0.05); | |
} | |
generateTheme() { | |
const c1 = ThemePicker.generateRgb(); | |
const c2 = ThemePicker.generateRgb(); | |
const contrast = ThemePicker.getContrastRatio(c1, c2); | |
if (contrast < 4.5) { | |
this.generateTheme(); | |
} else { | |
const c1HSL = ThemePicker.RGBToHSL(c1[0], c1[1], c1[2]); | |
const c2HSL = ThemePicker.RGBToHSL(c2[0], c2[1], c2[2]); | |
const c1HSLString = `${c1HSL[0]}deg, ${c1HSL[1]}%, ${c1HSL[2]}%`; | |
const c2HSLString = `${c2HSL[0]}deg, ${c2HSL[1]}%, ${c2HSL[2]}%`; | |
document.documentElement.style.setProperty('--color-random-text', c1HSLString); | |
document.documentElement.style.setProperty('--color-random-sheet', c2HSLString); | |
localStorage.setItem('color-text', c1HSLString); | |
localStorage.setItem('color-sheet', c2HSLString); | |
} | |
} | |
save(theme) { | |
document.documentElement.setAttribute('theme', theme); | |
localStorage.setItem('theme', theme); | |
this.update(theme); | |
} | |
update(theme) { | |
if (this.querySelector('.--active')) { | |
this.querySelector('.--active').classList.remove('--active'); | |
} | |
this.querySelector(`[data-theme="${theme}"]`).classList.add('--active'); | |
} | |
selectable() { | |
const themeButtons = this.querySelectorAll('[data-theme]'); | |
themeButtons.forEach((element) => { | |
element.addEventListener('click', (e) => { | |
const newTheme = element.getAttribute('data-theme'); | |
this.save(newTheme); | |
if (newTheme === 'random') { | |
this.generateTheme(); | |
} | |
}); | |
}); | |
} | |
closeable() { | |
document.addEventListener('click', (e) => { | |
const isClickInside = this.contains(e.target); | |
const isOpen = this.querySelector('details[open]'); | |
if (!isClickInside && isOpen) { | |
this.querySelector('details[open]').removeAttribute('open'); | |
} | |
}); | |
} | |
connectedCallback() { | |
this.selectable(); | |
this.closeable(); | |
this.update(document.documentElement.getAttribute('theme')); | |
} | |
} | |
customElements.define('theme-picker', ThemePicker); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment