Files
streamyfin/utils/atoms/primaryColor.ts
Fredrik Burmester 4641ff726c wip: design refactor
2024-08-28 22:00:50 +02:00

74 lines
2.2 KiB
TypeScript

import { atom, useAtom } from "jotai";
interface ThemeColors {
primary: string;
secondary: string;
average: string;
text: string;
}
const calculateTextColor = (backgroundColor: string): string => {
// Convert hex to RGB
const r = parseInt(backgroundColor.slice(1, 3), 16);
const g = parseInt(backgroundColor.slice(3, 5), 16);
const b = parseInt(backgroundColor.slice(5, 7), 16);
// Calculate perceived brightness
// Using the formula: (R * 299 + G * 587 + B * 114) / 1000
const brightness = (r * 299 + g * 587 + b * 114) / 1000;
// Calculate contrast ratio with white and black
const contrastWithWhite = calculateContrastRatio([255, 255, 255], [r, g, b]);
const contrastWithBlack = calculateContrastRatio([0, 0, 0], [r, g, b]);
// Use black text if the background is bright and has good contrast with black
if (brightness > 180 && contrastWithBlack >= 4.5) {
return "#000000";
}
// Otherwise, use white text
return "#FFFFFF";
};
// Helper function to calculate contrast ratio
const calculateContrastRatio = (rgb1: number[], rgb2: number[]): number => {
const l1 = calculateRelativeLuminance(rgb1);
const l2 = calculateRelativeLuminance(rgb2);
const lighter = Math.max(l1, l2);
const darker = Math.min(l1, l2);
return (lighter + 0.05) / (darker + 0.05);
};
// Helper function to calculate relative luminance
const calculateRelativeLuminance = (rgb: number[]): number => {
const [r, g, b] = rgb.map((c) => {
c /= 255;
return c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4);
});
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
};
const baseThemeColorAtom = atom<ThemeColors>({
primary: "#FFFFFF",
secondary: "#000000",
average: "#888888",
text: "#000000",
});
export const itemThemeColorAtom = atom(
(get) => get(baseThemeColorAtom),
(get, set, update: Partial<ThemeColors>) => {
const currentColors = get(baseThemeColorAtom);
const newColors = { ...currentColors, ...update };
// Recalculate text color if primary color changes
if (update.average) {
newColors.text = calculateTextColor(update.average);
}
set(baseThemeColorAtom, newColors);
}
);
export const useItemThemeColor = () => useAtom(itemThemeColorAtom);