Analogous Color Calculator

Analogous Color Palette Calculator

1. Dashboard (Color Palette)
2. Data Configuration (Key Color Input)

Calculated Analogous Color Scheme ($\pm 30^{\circ}$ Hue Shift)

Please enter a starting color (e.g., #007bff, rgb(0, 123, 255), or hsl(210, 100%, 50%)) in the **Data Configuration** tab and click 'Calculate'.

We recommend using **Hex** format (e.g., #FF5733) for the most reliable results.

Error: Tool failed to load. Please check element IDs.

'; return; } // --- COLOR CONVERSION UTILITIES --- // 1. HEX to HSL function hexToHsl(hex) { let r = 0, g = 0, b = 0; // 3 digits if (hex.length === 4) { r = parseInt(hex[1] + hex[1], 16); g = parseInt(hex[2] + hex[2], 16); b = parseInt(hex[3] + hex[3], 16); } // 6 digits else if (hex.length === 7) { r = parseInt(hex.substring(1, 3), 16); g = parseInt(hex.substring(3, 5), 16); b = parseInt(hex.substring(5, 7), 16); } else { return null; // Invalid HEX } r /= 255; g /= 255; b /= 255; let max = Math.max(r, g, b), min = Math.min(r, g, b); let h, s, l = (max + min) / 2; if (max === min) { h = s = 0; // achromatic } else { let d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); switch (max) { case r: h = (g - b) / d + (g < b ? 6 : 0); break; case g: h = (b - r) / d + 2; break; case b: h = (r - g) / d + 4; break; } h /= 6; } return [Math.round(h * 360), Math.round(s * 100), Math.round(l * 100)]; } // 2. HSL to RGB (needed for intermediate steps) function hslToRgb(h, s, l) { h /= 360; s /= 100; l /= 100; let r, g, b; if (s === 0) { r = g = b = l; // achromatic } else { const hue2rgb = (p, q, t) => { if (t < 0) t += 1; if (t > 1) t -= 1; if (t < 1 / 6) return p + (q - p) * 6 * t; if (t < 1 / 2) return q; if (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6; return p; }; const q = l < 0.5 ? l * (1 + s) : l + s - l * s; const p = 2 * l - q; r = hue2rgb(p, q, h + 1 / 3); g = hue2rgb(p, q, h); b = hue2rgb(p, q, h - 1 / 3); } return [Math.round(r * 255), Math.round(g * 255), Math.round(b * 255)]; } // 3. RGB to HEX function rgbToHex(r, g, b) { const componentToHex = c => { const hex = c.toString(16); return hex.length === 1 ? "0" + hex : hex; }; return "#" + componentToHex(r) + componentToHex(g) + componentToHex(b); } // 4. Main Parsing Function function parseColorInput(colorString) { colorString = colorString.trim().toLowerCase(); // Regex for Hex (#FFF or #FFFFFF) const hexMatch = colorString.match(/^#?([0-9a-f]{3}|[0-9a-f]{6})$/i); if (hexMatch) { const hex = '#' + hexMatch[1]; const hsl = hexToHsl(hex); if (hsl) return { hex, hsl, valid: true }; } // Regex for RGB (rgb(r, g, b)) const rgbMatch = colorString.match(/^rgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)$/i); if (rgbMatch) { const r = parseInt(rgbMatch[1]), g = parseInt(rgbMatch[2]), b = parseInt(rgbMatch[3]); if (r > 255 || g > 255 || b > 255) return { valid: false }; const hex = rgbToHex(r, g, b); const hsl = hexToHsl(hex); return { hex, hsl, valid: true }; } // Regex for HSL (hsl(h, s%, l%)) const hslMatch = colorString.match(/^hsl\((\d{1,3}),\s*(\d{1,3})%,\s*(\d{1,3})%\)$/i); if (hslMatch) { const h = parseInt(hslMatch[1]), s = parseInt(hslMatch[2]), l = parseInt(hslMatch[3]); if (h > 360 || s > 100 || l > 100) return { valid: false }; const rgb = hslToRgb(h, s, l); const hex = rgbToHex(...rgb); return { hex, hsl: [h, s, l], valid: true }; } return { valid: false }; } // --- TAB NAVIGATION LOGIC (Standard implementation) --- const tabs = ['dashboard', 'config']; let currentTabIndex = 0; function updateNavButtons() { prevBtn.disabled = currentTabIndex === 0; nextBtn.disabled = currentTabIndex === tabs.length - 1; prevBtn.style.opacity = prevBtn.disabled ? '0.5' : '1'; nextBtn.style.opacity = nextBtn.disabled ? '0.5' : '1'; } window.switchTab = function(tabName) { const activeTab = document.querySelector('.tab-button.active'); const activeContent = document.querySelector('.tab-content.active'); const nextTabBtn = document.getElementById('tab-btn-' + tabName); const nextTabContent = document.getElementById('tab-content-' + tabName); if (!nextTabBtn || !nextTabContent) return; if (activeTab) activeTab.classList.remove('active'); if (activeContent) activeContent.classList.remove('active'); nextTabBtn.classList.add('active'); nextTabContent.classList.add('active'); currentTabIndex = tabs.indexOf(tabName); updateNavButtons(); }; window.navigateTabs = function(direction) { const newIndex = currentTabIndex + direction; if (newIndex >= 0 && newIndex < tabs.length) { switchTab(tabs[newIndex]); } }; // --- MAIN CALCULATION AND RENDERING --- window.calculateAnalogousAndSwitchToDashboard = function() { const inputColorString = keyColorInput.value; const parsed = parseColorInput(inputColorString); if (!parsed.valid) { alert("Invalid color input. Please enter a valid Hex (#RRGGBB), RGB (rgb(r,g,b)), or HSL (hsl(h,s%,l%)) code."); return; } const [h, s, l] = parsed.hsl; // Calculate Analogous Colors: Shift Hue by +/- 30 degrees const h1 = (h + 30) % 360; const h2 = (h - 30 + 360) % 360; // Add 360 to handle negative results const colors = [ { role: "Key Color (Primary)", h, s, l, hex: parsed.hex }, { role: "Analogous Color 1 (+30°)", h: h1, s, l }, { role: "Analogous Color 2 (-30°)", h: h2, s, l } ]; let html = ''; colors.forEach(color => { const rgb = hslToRgb(color.h, color.s, color.l); const hex = color.hex || rgbToHex(...rgb); const hslString = `hsl(${color.h}, ${color.s}%, ${color.l}%)`; const rgbString = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; html += ` ${color.role} ${hex.toUpperCase()} ${rgbString} ${hslString} `; }); resultsTableBody.innerHTML = html; initialMessage.style.display = 'none'; resultsTable.style.display = 'table'; switchTab('dashboard'); }; // II. C. PDF Download Functionality window.downloadPDF = function() { // III. B. Website Integrity: Use the print media query CSS to hide surrounding elements. window.print(); }; // Initial setup: Calculate with default values window.calculateAnalogousAndSwitchToDashboard(); switchTab('dashboard'); // Start on the dashboard });
Scroll to Top