<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Color Palette Generator</title>
<!-- Use Tailwind CSS for easy styling -->
<script src="https://cdn.tailwindcss.com"></script>
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #f3f4f6;
color: #1f2937;
}
</style>
</head>
<body class="bg-gray-100 flex items-center justify-center min-h-screen p-4">
<!-- Main Container -->
<div class="bg-white p-8 rounded-2xl shadow-xl max-w-4xl w-full">
<h1 class="text-3xl font-bold text-center mb-6 text-gray-800">Color Palette Generator</h1>
<p class="text-center text-gray-600 mb-8">
Click on a color to add it to your custom palette below.
</p>
<!-- Generated Color Swatches Display -->
<div id="spectrum-container" class="mb-8">
<h2 class="text-xl font-bold text-gray-700 mb-4">Click to select colors:</h2>
<div id="swatches-container" class="flex flex-col gap-4 border border-gray-200 rounded-lg p-2 min-h-[4rem]">
<!-- Spectrum swatches will be added here dynamically -->
</div>
</div>
<!-- Selected Colors Display -->
<div id="selected-palette-container" class="mb-8 hidden">
<h2 class="text-xl font-bold text-gray-700 mb-4">
Your Selected Colors:
<span class="text-gray-500 font-normal">
(<span id="color-count">0</span> colors)
</span>
</h2>
<div id="custom-palette-container" class="flex flex-wrap gap-2 justify-center border border-gray-200 rounded-lg p-2 min-h-[4rem]">
<!-- Selected color swatches will be added here -->
</div>
</div>
<!-- Generate and Copy Buttons -->
<div class="flex items-center justify-center gap-4 mb-6">
<button id="copy-code-btn" class="bg-yellow-500 text-gray-900 font-semibold py-3 px-6 rounded-lg shadow-md hover:bg-yellow-600 transition-colors duration-200 focus:outline-none focus:ring-2 focus:ring-yellow-400 focus:ring-opacity-50 hidden">
Copy Code
</button>
</div>
<!-- Generated Code Output -->
<div id="code-output-container" class="relative bg-gray-900 text-gray-50 text-sm rounded-lg p-4 shadow-inner hidden">
<pre class="whitespace-pre-wrap break-all"><code id="code-output" class="font-mono"></code></pre>
<span id="copy-message" class="absolute top-2 right-4 text-xs text-green-400 opacity-0 transition-opacity duration-300">Copied!</span>
</div>
<!-- Custom Message Box for alerts -->
<div id="message-box" class="fixed top-4 left-1/2 -translate-x-1/2 p-4 bg-red-500 text-white rounded-lg shadow-lg hidden">
Please add at least one color before generating the code.
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', () => {
const swatchesContainer = document.getElementById('swatches-container');
const selectedPaletteContainer = document.getElementById('selected-palette-container');
const customPaletteContainer = document.getElementById('custom-palette-container');
const colorCountSpan = document.getElementById('color-count');
const copyCodeBtn = document.getElementById('copy-code-btn');
const codeOutputContainer = document.getElementById('code-output-container');
const codeOutput = document.getElementById('code-output');
const copyMessage = document.getElementById('copy-message');
const messageBox = document.getElementById('message-box');
let selectedColors = [];
// Function to show a custom message
const showMessage = (text) => {
messageBox.textContent = text;
messageBox.classList.remove('hidden');
setTimeout(() => {
messageBox.classList.add('hidden');
}, 3000);
};
// Function to convert HSL to Hexadecimal color string
const hslToHex = (h, s, l) => {
l /= 100;
const a = s * Math.min(l, 1 - l) / 100;
const f = n => {
const k = (n + h / 30) % 12;
const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1);
return Math.round(255 * color).toString(16).padStart(2, '0');
};
return `#${f(0)}${f(8)}${f(4)}`.toUpperCase();
};
// Function to create and append a color swatch
const createSwatch = (container, color, isRemovable = false) => {
const swatch = document.createElement('div');
swatch.className = `w-6 h-6 rounded-md shadow-sm cursor-pointer transition-transform duration-100 hover:scale-110`;
swatch.style.backgroundColor = color;
if (isRemovable) {
// Add a click listener to remove the color from the custom palette
swatch.addEventListener('click', () => {
const index = selectedColors.indexOf(color);
if (index > -1) {
selectedColors.splice(index, 1);
}
refreshCustomPalette();
});
} else {
// Add a click listener to add the color to the custom palette
swatch.addEventListener('click', () => {
if (!selectedColors.includes(color)) {
selectedColors.push(color);
refreshCustomPalette();
}
});
}
container.appendChild(swatch);
};
// Function to generate the code and update the display
const generateAndDisplayCode = () => {
if (selectedColors.length === 0) {
codeOutputContainer.classList.add('hidden');
copyCodeBtn.classList.add('hidden');
return;
}
const formattedColors = selectedColors.map(color => `'${color}'`).join(', ');
const codeString = `const palette = [${formattedColors}];`;
codeOutput.textContent = codeString;
codeOutputContainer.classList.remove('hidden');
copyCodeBtn.classList.remove('hidden');
};
// Refresh the display of the custom palette and update the counter
const refreshCustomPalette = () => {
customPaletteContainer.innerHTML = '';
if (selectedColors.length > 0) {
selectedPaletteContainer.classList.remove('hidden');
selectedColors.forEach(color => createSwatch(customPaletteContainer, color, true));
} else {
selectedPaletteContainer.classList.add('hidden');
}
colorCountSpan.textContent = selectedColors.length;
generateAndDisplayCode();
};
// Function to generate and display the main color spectrum
const generateAndDisplaySpectrum = () => {
// Define 15 base hues for a diverse palette, with pinks and purples grouped at the top
const baseHues = [320, 300, 280, 260, 220, 200, 190, 180, 150, 100, 60, 30, 0, 20, 0];
baseHues.forEach((hue, index) => {
const rowContainer = document.createElement('div');
rowContainer.className = 'flex flex-wrap gap-1 justify-start w-full';
swatchesContainer.appendChild(rowContainer);
for (let i = 0; i < 20; i++) {
// Special handling for red, brown and grey rows
let saturation;
if (index === 12) { // Red row (new position)
saturation = 70 + (i % 3) * 10;
} else if (index === 13) { // Brown row (new position)
saturation = 50 - (i / 19) * 30;
} else if (index === 14) { // Grey row (new position)
saturation = 5;
} else {
saturation = 50 + (i % 3) * 15;
}
const lightness = 95 - (i / 19) * 60; // Light to darker, but not too dark
const hexColor = hslToHex(hue, saturation, lightness);
createSwatch(rowContainer, hexColor);
}
});
};
// Event listener for the "Copy Code" button
copyCodeBtn.addEventListener('click', () => {
const textToCopy = codeOutput.textContent;
// Use a more reliable method for copying text to the clipboard
const tempInput = document.createElement('textarea');
tempInput.value = textToCopy;
document.body.appendChild(tempInput);
tempInput.select();
try {
document.execCommand('copy');
copyMessage.classList.add('opacity-100');
setTimeout(() => {
copyMessage.classList.remove('opacity-100');
}, 1500);
} catch (err) {
console.error('Failed to copy text: ', err);
}
document.body.removeChild(tempInput);
});
// Generate the spectrum when the page first loads
generateAndDisplaySpectrum();
});
</script>
</body>
</html>