Created
March 28, 2024 18:20
-
-
Save nachoaguirre/0122fc26c5443dbcb256628f063220b1 to your computer and use it in GitHub Desktop.
typescript helper to darken or lighten a color
This file contains hidden or 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
export const useThemeVariables3 = () => { | |
interface ConfigTheme { | |
primaryColor: string; | |
textColor: string; | |
contrastColor: string; | |
bodyBackgroundColor: string; | |
linksColor: string; | |
headerBackgroundColor: string; | |
headerIconColor: string; | |
headerTextColor: string; | |
titlesColor: string; | |
sectionBackgroundColor: string; | |
} | |
type ColorObject = Record<"r" | "g" | "b" | "a", number>; | |
const toColorObject = (rgbOrHex: string): ColorObject => { | |
const { length } = rgbOrHex; | |
const outputColor = {} as ColorObject; | |
if (length > 9) { | |
const rgbaColor = rgbOrHex.split(","); | |
const [rgbaAndRed, green, blue, alpha] = rgbaColor; | |
if (rgbaAndRed.slice(0, 3) !== "rgb") { | |
throw new Error("Invalid color format"); | |
} | |
const red = rgbaAndRed[3] === "a" ? rgbaAndRed.slice(5) : rgbaAndRed.slice(4); | |
outputColor.r = parseInt(red, 10); | |
outputColor.g = parseInt(green, 10); | |
outputColor.b = parseInt(blue, 10); | |
outputColor.a = alpha ? parseFloat(alpha) : -1; | |
} else { | |
if (length === 8 || length === 6 || length < 4) { | |
throw new Error("Invalid hex color format"); | |
} | |
let HexColor = rgbOrHex; | |
if (length < 6) { | |
HexColor = `#${rgbOrHex[1]}${rgbOrHex[1]}${rgbOrHex[2]}${rgbOrHex[2]}${rgbOrHex[3]}${rgbOrHex[3]}${ | |
length > 4 ? rgbOrHex[4] + rgbOrHex[4] : "" | |
}`; | |
} | |
if (length === 9 || length === 5) { | |
const hexRed = parseInt(HexColor.slice(1, 3), 16); | |
outputColor.r = hexRed; | |
const hexGreen = parseInt(HexColor.slice(3, 5), 16); | |
outputColor.g = hexGreen; | |
const hexBlue = parseInt(HexColor.slice(5, 7), 16); | |
outputColor.b = hexBlue; | |
const hexAlpha = parseInt(HexColor.slice(7, 9), 16); | |
outputColor.a = Math.round((hexAlpha / 255) * 100) / 100; | |
} else { | |
const hexRed = parseInt(HexColor.slice(1, 3), 16); | |
outputColor.r = hexRed; | |
const hexGreen = parseInt(HexColor.slice(3, 5), 16); | |
outputColor.g = hexGreen; | |
const hexBlue = parseInt(HexColor.slice(5, 7), 16); | |
outputColor.b = hexBlue; | |
outputColor.a = -1; | |
} | |
} | |
return outputColor; | |
}; | |
const black: ColorObject = { r: 0, g: 0, b: 0, a: -1 }; | |
const white: ColorObject = { r: 255, g: 255, b: 255, a: -1 }; | |
const toHex = (value: number): string => { | |
const hex = Math.max(0, Math.min(255, Math.round(value))).toString(16); | |
return hex.length < 2 ? '0' + hex : hex; | |
} | |
const tint = ( | |
ratio: number, | |
inputColor: string, | |
{ toColor, useLinear, reformat }: { toColor?: string; useLinear?: boolean; reformat?: boolean } = {} | |
) => { | |
const { round } = Math; | |
const clampedRatio = Math.min(Math.max(ratio, -1), 1); | |
let baseColor = inputColor; | |
if (inputColor[0] !== "r" && inputColor[0] !== "#") { | |
baseColor = "#000"; | |
} | |
let isRGBformat = baseColor.length > 9 || baseColor.includes("rgb("); | |
isRGBformat = reformat ? !isRGBformat : isRGBformat; | |
if (toColor) { | |
const isToColorRgbFormat = (toColor && toColor?.length > 9) || toColor?.includes("rgb("); | |
isRGBformat = reformat ? !isToColorRgbFormat : isToColorRgbFormat; | |
} | |
const formattedBaseColor = toColorObject(baseColor); | |
const isNegativeRatio = clampedRatio < 0; | |
const toColorDefault = isNegativeRatio ? black : white; | |
const formattedToColor = toColor && !reformat ? toColorObject(toColor) : toColorDefault; | |
const toColorRatio = Math.abs(clampedRatio); | |
const baseRatio = 1 - toColorRatio; | |
const outputColor = {} as ColorObject; | |
if (useLinear) { | |
outputColor.r = round(baseRatio * formattedBaseColor.r + toColorRatio * formattedToColor.r); | |
outputColor.g = round(baseRatio * formattedBaseColor.g + toColorRatio * formattedToColor.g); | |
outputColor.b = round(baseRatio * formattedBaseColor.b + toColorRatio * formattedToColor.b); | |
} else { | |
outputColor.r = round((baseRatio * formattedBaseColor.r ** 2 + toColorRatio * formattedToColor.r ** 2) ** 0.5); | |
outputColor.g = round((baseRatio * formattedBaseColor.g ** 2 + toColorRatio * formattedToColor.g ** 2) ** 0.5); | |
outputColor.b = round((baseRatio * formattedBaseColor.b ** 2 + toColorRatio * formattedToColor.b ** 2) ** 0.5); | |
} | |
const blendedAlpha = formattedBaseColor.a * baseRatio + formattedToColor.a * toColorRatio; | |
outputColor.a = formattedToColor.a < 0 ? formattedBaseColor.a : blendedAlpha; | |
const hasAlpha = formattedBaseColor.a >= 0 || formattedToColor.a >= 0; | |
if (isRGBformat) { | |
return `rgb${hasAlpha ? "a" : ""}(${outputColor.r},${outputColor.g},${outputColor.b}${ | |
hasAlpha ? `,${round(outputColor.a * 1000) / 1000}` : "" | |
})`; | |
} | |
return `#${toHex(outputColor.r)}${toHex(outputColor.g)}${toHex(outputColor.b)}`; | |
}; | |
const configThemeExample = { | |
textColor: '#d5d5d5', | |
contrastColor: '#C86A6E', | |
bodyBackgroundColor: '#292B3F', | |
linksColor: '#4D9A4A', | |
headerBackgroundColor: '#292B3F', | |
headerIconColor: '#fff', | |
titlesColor: '#eee', | |
sectionBackgroundColor: '#1A1F2B', | |
}; | |
const useStyles = (configTheme: ConfigTheme): void => { | |
document.documentElement.style.setProperty('--text-color', configTheme.textColor); | |
const contrastColor = configTheme.contrastColor; | |
document.documentElement.style.setProperty('--contrast-color', contrastColor); | |
document.documentElement.style.setProperty('--contrast-color-light-10', tint(0.10, contrastColor)); | |
document.documentElement.style.setProperty('--contrast-color-dark-30', tint(-0.3, contrastColor)); | |
const bodyBackgroundColor = configTheme.bodyBackgroundColor; | |
document.documentElement.style.setProperty('--body-bg-color', bodyBackgroundColor); | |
document.documentElement.style.setProperty('--body-bg-color-light-10', tint(0.10, bodyBackgroundColor)); | |
document.documentElement.style.setProperty('--body-bg-color-dark-10', tint(-0.10, bodyBackgroundColor)); | |
document.documentElement.style.setProperty('--body-bg-color-dark-50', tint(-0.5, bodyBackgroundColor)); | |
document.documentElement.style.setProperty('--body-bg-color-dark-90', tint(-0.9, bodyBackgroundColor)); | |
document.documentElement.style.setProperty('--links-color', configTheme.linksColor); | |
document.documentElement.style.setProperty('--header-bg-color', configTheme.headerBackgroundColor); | |
document.documentElement.style.setProperty('--header-icon-color', configTheme.headerIconColor); | |
document.documentElement.style.setProperty('--titles-color', configTheme.titlesColor); | |
document.documentElement.style.setProperty('--section-color', configTheme.sectionBackgroundColor); | |
} | |
return { | |
useStyles | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment