// Shared color utilities for consistent heatmap theming // Use exact same colors for both daily and weekly views const EMPTY_DOT_DARK = "#374151"; // gray-600 equivalent const EMPTY_DOT_LIGHT = "#d1d5db"; // gray-300 equivalent export const getHeatmapTheme = (primaryColor: string) => ({ dark: [EMPTY_DOT_DARK, primaryColor], light: [EMPTY_DOT_LIGHT, primaryColor], }); export const getHeatmapColorIntensity = (level: number, primaryColor: string, isDarkMode: boolean = false) => { if (level === 0) { return null; // Will use CSS classes for theme-aware empty state } // Create different intensities of the primary color const hexToRgb = (hex: string) => { const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); return result ? { r: parseInt(result[1], 16), g: parseInt(result[2], 16), b: parseInt(result[3], 16) } : null; }; const rgbToHex = (r: number, g: number, b: number) => { return "#" + ((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1); }; const rgb = hexToRgb(primaryColor); if (!rgb) { // Fallback to green scale if color parsing fails const greenIntensities = ['#0e4429', '#006d32', '#26a641', '#39d353']; return greenIntensities[Math.min(level - 1, 3)]; } // Create intensity levels based on theme let intensityMultipliers: number[]; if (isDarkMode) { // Dark mode: Start darker, get brighter (40% → 100%) intensityMultipliers = [0.4, 0.6, 0.8, 1.0]; } else { // Light mode: Start brighter, get darker (100% → 40%) intensityMultipliers = [1.0, 0.8, 0.6, 0.4]; } const multiplier = intensityMultipliers[Math.min(level - 1, 3)]; const adjustedR = Math.round(rgb.r * multiplier); const adjustedG = Math.round(rgb.g * multiplier); const adjustedB = Math.round(rgb.b * multiplier); return rgbToHex(adjustedR, adjustedG, adjustedB); }; export const getEmptyDotColors = () => ({ dark: EMPTY_DOT_DARK, light: EMPTY_DOT_LIGHT, }); export const getEmptyDotClasses = () => "bg-[#d1d5db] dark:bg-[#374151]";