Skip to content

Instantly share code, notes, and snippets.

@nachoaguirre
Created March 28, 2024 18:20
Show Gist options
  • Save nachoaguirre/0122fc26c5443dbcb256628f063220b1 to your computer and use it in GitHub Desktop.
Save nachoaguirre/0122fc26c5443dbcb256628f063220b1 to your computer and use it in GitHub Desktop.
typescript helper to darken or lighten a color
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