Spaces:
Running
Running
Download index.html from Decide2Design/gradiant-picker: direct link, hf CLI and curl.
- Browser
- Download file 45 kB
-
https://huggingface.co/spaces/Decide2Design/gradiant-picker/resolve/main/index.html
- Command line
-
hf download hf://spaces/Decide2Design/gradiant-picker/index.html
-
curl -L -o index.html https://huggingface.co/spaces/Decide2Design/gradiant-picker/resolve/main/index.html
45 kB
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Gradient Picker | Interactive Color Tool</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/chroma-js@2.1.2/chroma.min.js"></script> | |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
| <style> | |
| .gradient-preview { | |
| height: 120px; | |
| border-radius: 0.5rem; | |
| box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -1px rgba(0, 0, 0, 0.06); | |
| } | |
| .color-slider { | |
| -webkit-appearance: none; | |
| width: 100%; | |
| height: 8px; | |
| border-radius: 4px; | |
| outline: none; | |
| } | |
| .color-slider::-webkit-slider-thumb { | |
| -webkit-appearance: none; | |
| appearance: none; | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| cursor: pointer; | |
| box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); | |
| border: 2px solid white; | |
| } | |
| .color-picker-handle { | |
| width: 24px; | |
| height: 24px; | |
| border-radius: 50%; | |
| background: white; | |
| border: 2px solid #333; | |
| position: absolute; | |
| top: -10px; | |
| transform: translateX(-12px); | |
| cursor: pointer; | |
| box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); | |
| } | |
| .color-picker-handle::after { | |
| content: ''; | |
| position: absolute; | |
| width: 8px; | |
| height: 8px; | |
| background: #333; | |
| border-radius: 50%; | |
| top: 50%; | |
| left: 50%; | |
| transform: translate(-50%, -50%); | |
| } | |
| .color-block { | |
| transition: all 0.2s ease; | |
| cursor: pointer; | |
| } | |
| .color-block:hover { | |
| transform: scale(1.05); | |
| box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15); | |
| } | |
| .copy-btn { | |
| transition: all 0.2s ease; | |
| } | |
| .copy-btn:hover { | |
| transform: translateY(-2px); | |
| } | |
| .copy-btn:active { | |
| transform: translateY(0); | |
| } | |
| .tab-content { | |
| display: none; | |
| } | |
| .tab-content.active { | |
| display: block; | |
| } | |
| .contrast-ratio { | |
| font-weight: bold; | |
| } | |
| .contrast-ratio.aa-pass { | |
| color: #10b981; | |
| } | |
| .contrast-ratio.aa-fail { | |
| color: #ef4444; | |
| } | |
| .contrast-ratio.aaa-pass { | |
| color: #10b981; | |
| } | |
| .contrast-ratio.aaa-fail { | |
| color: #ef4444; | |
| } | |
| </style> | |
| </head> | |
| <body class="bg-gray-50 text-gray-800 dark:bg-gray-900 dark:text-gray-200 min-h-screen"> | |
| <div class="container mx-auto px-4 py-8"> | |
| <header class="mb-8 text-center"> | |
| <h1 class="text-3xl md:text-4xl font-bold bg-gradient-to-r from-purple-600 to-blue-500 bg-clip-text text-transparent">Gradient Picker</h1> | |
| <p class="mt-2 text-gray-600 dark:text-gray-400">Create, explore and export beautiful color gradients</p> | |
| </header> | |
| <div class="max-w-4xl mx-auto bg-white dark:bg-gray-800 rounded-xl shadow-lg overflow-hidden p-6 mb-8"> | |
| <!-- Color Inputs --> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-6"> | |
| <div class="color-input"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <label class="font-medium">Start Color</label> | |
| <div class="flex items-center"> | |
| <input type="text" id="color1-hex" value="#3b82f6" class="w-24 px-2 py-1 border rounded text-sm font-mono"> | |
| <button class="copy-btn ml-2 p-1 rounded-full text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100" onclick="copyToClipboard('color1-hex')"> | |
| <i class="far fa-copy"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <input type="color" id="color1" value="#3b82f6" class="w-full h-10 rounded cursor-pointer"> | |
| <div class="mt-2 grid grid-cols-3 gap-2"> | |
| <div> | |
| <label class="text-xs text-gray-500 dark:text-gray-400">RGB</label> | |
| <input type="text" id="color1-rgb" readonly class="w-full px-2 py-1 border rounded text-sm font-mono bg-gray-50 dark:bg-gray-700"> | |
| </div> | |
| <div> | |
| <label class="text-xs text-gray-500 dark:text-gray-400">HSL</label> | |
| <input type="text" id="color1-hsl" readonly class="w-full px-2 py-1 border rounded text-sm font-mono bg-gray-50 dark:bg-gray-700"> | |
| </div> | |
| <div> | |
| <label class="text-xs text-gray-500 dark:text-gray-400">Name</label> | |
| <input type="text" id="color1-name" readonly class="w-full px-2 py-1 border rounded text-sm font-mono bg-gray-50 dark:bg-gray-700"> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="color-input"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <label class="font-medium">End Color</label> | |
| <div class="flex items-center"> | |
| <input type="text" id="color2-hex" value="#ec4899" class="w-24 px-2 py-1 border rounded text-sm font-mono"> | |
| <button class="copy-btn ml-2 p-1 rounded-full text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100" onclick="copyToClipboard('color2-hex')"> | |
| <i class="far fa-copy"></i> | |
| </button> | |
| </div> | |
| </div> | |
| <input type="color" id="color2" value="#ec4899" class="w-full h-10 rounded cursor-pointer"> | |
| <div class="mt-2 grid grid-cols-3 gap-2"> | |
| <div> | |
| <label class="text-xs text-gray-500 dark:text-gray-400">RGB</label> | |
| <input type="text" id="color2-rgb" readonly class="w-full px-2 py-1 border rounded text-sm font-mono bg-gray-50 dark:bg-gray-700"> | |
| </div> | |
| <div> | |
| <label class="text-xs text-gray-500 dark:text-gray-400">HSL</label> | |
| <input type="text" id="color2-hsl" readonly class="w-full px-2 py-1 border rounded text-sm font-mono bg-gray-50 dark:bg-gray-700"> | |
| </div> | |
| <div> | |
| <label class="text-xs text-gray-500 dark:text-gray-400">Name</label> | |
| <input type="text" id="color2-name" readonly class="w-full px-2 py-1 border rounded text-sm font-mono bg-gray-50 dark:bg-gray-700"> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Gradient Controls --> | |
| <div class="mb-6"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <label class="font-medium">Gradient Steps</label> | |
| <span id="steps-value" class="font-mono">5</span> | |
| </div> | |
| <input type="range" id="steps" min="2" max="20" value="5" class="color-slider"> | |
| </div> | |
| <!-- Gradient Preview --> | |
| <div class="mb-6 relative"> | |
| <div id="gradient-preview" class="gradient-preview" style="background: linear-gradient(to right, #3b82f6, #ec4899);"></div> | |
| <div id="color-picker-handle" class="color-picker-handle" style="left: 50%;"></div> | |
| </div> | |
| <!-- Picked Color Info --> | |
| <div id="picked-color-info" class="bg-gray-100 dark:bg-gray-700 rounded-lg p-4 mb-6 hidden"> | |
| <div class="flex items-center justify-between"> | |
| <div class="flex items-center"> | |
| <div id="picked-color-swatch" class="w-10 h-10 rounded-full border-2 border-white shadow mr-3"></div> | |
| <div> | |
| <div class="font-medium">Selected Color</div> | |
| <div id="picked-color-hex" class="text-sm font-mono">#9d7dce</div> | |
| </div> | |
| </div> | |
| <button id="copy-picked-color" class="copy-btn px-3 py-1 bg-gray-200 dark:bg-gray-600 rounded-full text-sm flex items-center"> | |
| <i class="far fa-copy mr-1"></i> Copy | |
| </button> | |
| </div> | |
| <div class="mt-3 grid grid-cols-3 gap-2"> | |
| <div> | |
| <label class="text-xs text-gray-500 dark:text-gray-400">RGB</label> | |
| <input type="text" id="picked-color-rgb" readonly class="w-full px-2 py-1 border rounded text-sm font-mono bg-gray-50 dark:bg-gray-700"> | |
| </div> | |
| <div> | |
| <label class="text-xs text-gray-500 dark:text-gray-400">HSL</label> | |
| <input type="text" id="picked-color-hsl" readonly class="w-full px-2 py-1 border rounded text-sm font-mono bg-gray-50 dark:bg-gray-700"> | |
| </div> | |
| <div> | |
| <label class="text-xs text-gray-500 dark:text-gray-400">Name</label> | |
| <input type="text" id="picked-color-name" readonly class="w-full px-2 py-1 border rounded text-sm font-mono bg-gray-50 dark:bg-gray-700"> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Color Steps --> | |
| <div class="mb-6"> | |
| <h3 class="font-medium mb-3">Color Steps</h3> | |
| <div id="color-steps" class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-5 gap-3"> | |
| <!-- Color blocks will be generated here --> | |
| </div> | |
| </div> | |
| <!-- Tabs --> | |
| <div class="mb-6"> | |
| <div class="flex border-b border-gray-200 dark:border-gray-700"> | |
| <button class="tab-btn px-4 py-2 font-medium text-blue-600 dark:text-blue-400 border-b-2 border-blue-600 dark:border-blue-400" data-tab="export">Export</button> | |
| <button class="tab-btn px-4 py-2 font-medium text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100" data-tab="harmonies">Color Harmonies</button> | |
| <button class="tab-btn px-4 py-2 font-medium text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100" data-tab="contrast">Contrast Checker</button> | |
| <button class="tab-btn px-4 py-2 font-medium text-gray-600 dark:text-gray-400 hover:text-gray-900 dark:hover:text-gray-100" data-tab="preview">UI Preview</button> | |
| </div> | |
| <div id="export" class="tab-content active mt-4"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">CSS Gradient</label> | |
| <div class="relative"> | |
| <textarea id="css-gradient" readonly class="w-full h-20 px-3 py-2 border rounded font-mono text-sm bg-gray-50 dark:bg-gray-700"></textarea> | |
| <button onclick="copyToClipboard('css-gradient')" class="absolute top-2 right-2 copy-btn px-2 py-1 bg-gray-200 dark:bg-gray-600 rounded text-xs"> | |
| <i class="far fa-copy mr-1"></i> Copy | |
| </button> | |
| </div> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium mb-2">SVG Gradient</label> | |
| <div class="relative"> | |
| <textarea id="svg-gradient" readonly class="w-full h-20 px-3 py-2 border rounded font-mono text-sm bg-gray-50 dark:bg-gray-700"></textarea> | |
| <button onclick="copyToClipboard('svg-gradient')" class="absolute top-2 right-2 copy-btn px-2 py-1 bg-gray-200 dark:bg-gray-600 rounded text-xs"> | |
| <i class="far fa-copy mr-1"></i> Copy | |
| </button> | |
| </div> | |
| </div> | |
| </div> | |
| <div class="mt-4"> | |
| <button id="download-png" class="px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white rounded flex items-center"> | |
| <i class="fas fa-download mr-2"></i> Download PNG | |
| </button> | |
| </div> | |
| </div> | |
| <div id="harmonies" class="tab-content mt-4"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div> | |
| <h4 class="font-medium mb-3">Complementary</h4> | |
| <div id="complementary-colors" class="grid grid-cols-5 gap-2"> | |
| <!-- Complementary colors will be generated here --> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 class="font-medium mb-3">Analogous</h4> | |
| <div id="analogous-colors" class="grid grid-cols-3 gap-2"> | |
| <!-- Analogous colors will be generated here --> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 class="font-medium mb-3">Triadic</h4> | |
| <div id="triadic-colors" class="grid grid-cols-3 gap-2"> | |
| <!-- Triadic colors will be generated here --> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 class="font-medium mb-3">Tetradic</h4> | |
| <div id="tetradic-colors" class="grid grid-cols-4 gap-2"> | |
| <!-- Tetradic colors will be generated here --> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="contrast" class="tab-content mt-4"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-6"> | |
| <div> | |
| <h4 class="font-medium mb-3">Contrast Ratio</h4> | |
| <div class="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg"> | |
| <div class="flex items-center justify-between mb-2"> | |
| <span>WCAG AA (4.5:1)</span> | |
| <span id="contrast-aa" class="contrast-ratio aa-pass">4.8:1</span> | |
| </div> | |
| <div class="flex items-center justify-between mb-2"> | |
| <span>WCAG AAA (7:1)</span> | |
| <span id="contrast-aaa" class="contrast-ratio aaa-fail">4.8:1</span> | |
| </div> | |
| <div class="mt-4"> | |
| <label class="block text-sm font-medium mb-2">Test Background</label> | |
| <input type="color" id="contrast-bg" value="#ffffff" class="w-full h-10 rounded cursor-pointer"> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <h4 class="font-medium mb-3">Preview</h4> | |
| <div id="contrast-preview" class="p-4 rounded-lg" style="background-color: #ffffff;"> | |
| <p class="text-lg" style="color: #3b82f6;">This is sample text to demonstrate contrast</p> | |
| <p class="text-sm mt-2" style="color: #3b82f6;">Smaller text requires higher contrast</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <div id="preview" class="tab-content mt-4"> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <div class="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg"> | |
| <div class="flex items-center justify-between mb-3"> | |
| <h4 class="font-medium">Card</h4> | |
| <div class="flex"> | |
| <button class="preview-mode-btn px-2 py-1 text-xs bg-gray-200 dark:bg-gray-600 rounded-l" data-mode="light">Light</button> | |
| <button class="preview-mode-btn px-2 py-1 text-xs bg-gray-300 dark:bg-gray-800 rounded-r" data-mode="dark">Dark</button> | |
| </div> | |
| </div> | |
| <div class="preview-card p-4 rounded-lg shadow" style="background: linear-gradient(to right, #3b82f6, #ec4899);"> | |
| <h5 class="font-bold text-white">Card Title</h5> | |
| <p class="text-white text-opacity-90 text-sm mt-1">This is a preview card with your gradient</p> | |
| </div> | |
| </div> | |
| </div> | |
| <div> | |
| <div class="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg"> | |
| <h4 class="font-medium mb-3">Buttons</h4> | |
| <div class="grid grid-cols-2 gap-3"> | |
| <button class="px-4 py-2 rounded text-white font-medium" style="background-color: #3b82f6;">Primary</button> | |
| <button class="px-4 py-2 rounded text-white font-medium" style="background-color: #ec4899;">Secondary</button> | |
| <button class="px-4 py-2 rounded font-medium border" style="border-color: #3b82f6; color: #3b82f6;">Outline</button> | |
| <button class="px-4 py-2 rounded font-medium border" style="border-color: #ec4899; color: #ec4899;">Outline</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <footer class="text-center text-gray-600 dark:text-gray-400 text-sm mt-8"> | |
| <p>Gradient Picker © 2023 | Made with <i class="fas fa-heart text-red-500"></i> for designers & developers</p> | |
| </footer> | |
| </div> | |
| <script> | |
| // DOM Elements | |
| const color1Input = document.getElementById('color1'); | |
| const color1HexInput = document.getElementById('color1-hex'); | |
| const color1RgbInput = document.getElementById('color1-rgb'); | |
| const color1HslInput = document.getElementById('color1-hsl'); | |
| const color1NameInput = document.getElementById('color1-name'); | |
| const color2Input = document.getElementById('color2'); | |
| const color2HexInput = document.getElementById('color2-hex'); | |
| const color2RgbInput = document.getElementById('color2-rgb'); | |
| const color2HslInput = document.getElementById('color2-hsl'); | |
| const color2NameInput = document.getElementById('color2-name'); | |
| const stepsInput = document.getElementById('steps'); | |
| const stepsValue = document.getElementById('steps-value'); | |
| const gradientPreview = document.getElementById('gradient-preview'); | |
| const colorPickerHandle = document.getElementById('color-picker-handle'); | |
| const pickedColorInfo = document.getElementById('picked-color-info'); | |
| const pickedColorSwatch = document.getElementById('picked-color-swatch'); | |
| const pickedColorHex = document.getElementById('picked-color-hex'); | |
| const pickedColorRgb = document.getElementById('picked-color-rgb'); | |
| const pickedColorHsl = document.getElementById('picked-color-hsl'); | |
| const pickedColorName = document.getElementById('picked-color-name'); | |
| const copyPickedColorBtn = document.getElementById('copy-picked-color'); | |
| const colorStepsContainer = document.getElementById('color-steps'); | |
| const cssGradientTextarea = document.getElementById('css-gradient'); | |
| const svgGradientTextarea = document.getElementById('svg-gradient'); | |
| const downloadPngBtn = document.getElementById('download-png'); | |
| const complementaryColorsContainer = document.getElementById('complementary-colors'); | |
| const analogousColorsContainer = document.getElementById('analogous-colors'); | |
| const triadicColorsContainer = document.getElementById('triadic-colors'); | |
| const tetradicColorsContainer = document.getElementById('tetradic-colors'); | |
| const contrastRatioAA = document.getElementById('contrast-aa'); | |
| const contrastRatioAAA = document.getElementById('contrast-aaa'); | |
| const contrastBgInput = document.getElementById('contrast-bg'); | |
| const contrastPreview = document.getElementById('contrast-preview'); | |
| // Tab functionality | |
| const tabButtons = document.querySelectorAll('.tab-btn'); | |
| tabButtons.forEach(button => { | |
| button.addEventListener('click', () => { | |
| // Remove active class from all buttons and content | |
| tabButtons.forEach(btn => { | |
| btn.classList.remove('text-blue-600', 'dark:text-blue-400', 'border-b-2', 'border-blue-600', 'dark:border-blue-400'); | |
| btn.classList.add('text-gray-600', 'dark:text-gray-400'); | |
| }); | |
| // Add active class to clicked button | |
| button.classList.add('text-blue-600', 'dark:text-blue-400', 'border-b-2', 'border-blue-600', 'dark:border-blue-400'); | |
| button.classList.remove('text-gray-600', 'dark:text-gray-400'); | |
| // Hide all tab content | |
| document.querySelectorAll('.tab-content').forEach(content => { | |
| content.classList.remove('active'); | |
| }); | |
| // Show selected tab content | |
| const tabId = button.getAttribute('data-tab'); | |
| document.getElementById(tabId).classList.add('active'); | |
| }); | |
| }); | |
| // Preview mode (light/dark) | |
| const previewModeButtons = document.querySelectorAll('.preview-mode-btn'); | |
| previewModeButtons.forEach(button => { | |
| button.addEventListener('click', () => { | |
| previewModeButtons.forEach(btn => { | |
| btn.classList.remove('bg-gray-300', 'dark:bg-gray-800'); | |
| btn.classList.add('bg-gray-200', 'dark:bg-gray-600'); | |
| }); | |
| button.classList.add('bg-gray-300', 'dark:bg-gray-800'); | |
| button.classList.remove('bg-gray-200', 'dark:bg-gray-600'); | |
| const mode = button.getAttribute('data-mode'); | |
| const previewCard = document.querySelector('.preview-card'); | |
| if (mode === 'dark') { | |
| previewCard.classList.add('dark-mode-preview'); | |
| previewCard.querySelector('h5').classList.add('dark:text-white'); | |
| previewCard.querySelector('p').classList.add('dark:text-white'); | |
| } else { | |
| previewCard.classList.remove('dark-mode-preview'); | |
| previewCard.querySelector('h5').classList.remove('dark:text-white'); | |
| previewCard.querySelector('p').classList.remove('dark:text-white'); | |
| } | |
| }); | |
| }); | |
| // Initialize | |
| function init() { | |
| updateColorInfo(color1Input, color1HexInput, color1RgbInput, color1HslInput, color1NameInput); | |
| updateColorInfo(color2Input, color2HexInput, color2RgbInput, color2HslInput, color2NameInput); | |
| updateGradient(); | |
| updateColorSteps(); | |
| updateColorHarmonies(); | |
| updateContrastRatio(); | |
| // Set initial tab | |
| document.querySelector('.tab-btn[data-tab="export"]').click(); | |
| } | |
| // Update color information | |
| function updateColorInfo(colorInput, hexInput, rgbInput, hslInput, nameInput) { | |
| const color = chroma(colorInput.value); | |
| // Update hex value | |
| hexInput.value = color.hex(); | |
| // Update RGB value | |
| const rgb = color.rgb(); | |
| rgbInput.value = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; | |
| // Update HSL value | |
| const hsl = color.hsl(); | |
| hslInput.value = `hsl(${Math.round(hsl[0])}, ${Math.round(hsl[1] * 100)}%, ${Math.round(hsl[2] * 100)}%)`; | |
| // Update color name (simplified) | |
| nameInput.value = getColorName(color.hex()); | |
| } | |
| // Simple color name lookup | |
| function getColorName(hex) { | |
| const colorMap = { | |
| '#3b82f6': 'Blue 500', | |
| '#ec4899': 'Pink 500', | |
| '#ffffff': 'White', | |
| '#000000': 'Black', | |
| '#f59e0b': 'Amber 500', | |
| '#10b981': 'Emerald 500', | |
| '#ef4444': 'Red 500', | |
| '#8b5cf6': 'Violet 500', | |
| '#f97316': 'Orange 500', | |
| '#14b8a6': 'Teal 500' | |
| }; | |
| return colorMap[hex.toLowerCase()] || 'Custom Color'; | |
| } | |
| // Update gradient preview | |
| function updateGradient() { | |
| const color1 = color1HexInput.value; | |
| const color2 = color2HexInput.value; | |
| gradientPreview.style.background = `linear-gradient(to right, ${color1}, ${color2})`; | |
| // Update CSS gradient code | |
| cssGradientTextarea.value = `background: linear-gradient(to right, ${color1}, ${color2});`; | |
| // Update SVG gradient code | |
| svgGradientTextarea.value = `<svg width="100%" height="100%"> | |
| <defs> | |
| <linearGradient id="gradient" x1="0%" y1="0%" x2="100%" y2="0%"> | |
| <stop offset="0%" stop-color="${color1}" /> | |
| <stop offset="100%" stop-color="${color2}" /> | |
| </linearGradient> | |
| </defs> | |
| <rect width="100%" height="100%" fill="url(#gradient)" /> | |
| </svg>`; | |
| // Update UI preview elements | |
| document.querySelectorAll('.preview-card').forEach(card => { | |
| card.style.background = `linear-gradient(to right, ${color1}, ${color2})`; | |
| }); | |
| document.querySelectorAll('.preview-card h5, .preview-card p').forEach(text => { | |
| // Choose text color with better contrast | |
| const contrast1 = chroma.contrast(color1, '#ffffff'); | |
| const contrast2 = chroma.contrast(color2, '#ffffff'); | |
| text.style.color = contrast1 > contrast2 ? '#ffffff' : '#000000'; | |
| }); | |
| // Update buttons in preview | |
| const buttons = document.querySelectorAll('#preview button'); | |
| if (buttons.length >= 4) { | |
| buttons[0].style.backgroundColor = color1; | |
| buttons[1].style.backgroundColor = color2; | |
| buttons[2].style.borderColor = color1; | |
| buttons[2].style.color = color1; | |
| buttons[3].style.borderColor = color2; | |
| buttons[3].style.color = color2; | |
| } | |
| } | |
| // Update color steps | |
| function updateColorSteps() { | |
| const steps = parseInt(stepsInput.value); | |
| stepsValue.textContent = steps; | |
| const color1 = color1HexInput.value; | |
| const color2 = color2HexInput.value; | |
| colorStepsContainer.innerHTML = ''; | |
| // Generate color steps | |
| for (let i = 0; i < steps; i++) { | |
| const t = i / (steps - 1); | |
| const color = chroma.mix(color1, color2, t); | |
| const hex = color.hex(); | |
| const rgb = color.rgb(); | |
| const rgbStr = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; | |
| const colorBlock = document.createElement('div'); | |
| colorBlock.className = 'color-block h-16 rounded-lg flex flex-col items-center justify-center text-white text-xs font-medium'; | |
| colorBlock.style.backgroundColor = hex; | |
| colorBlock.innerHTML = ` | |
| <span class="font-mono">${hex}</span> | |
| <button class="copy-btn mt-1 px-2 py-1 bg-black bg-opacity-20 rounded-full text-xs" data-color="${hex}"> | |
| <i class="far fa-copy mr-1"></i> Copy | |
| </button> | |
| `; | |
| // Add click event to show color info | |
| colorBlock.addEventListener('click', () => { | |
| showPickedColorInfo(hex); | |
| }); | |
| // Add click event to copy button | |
| const copyBtn = colorBlock.querySelector('button'); | |
| copyBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| copyToClipboardFromAttribute(copyBtn, 'data-color'); | |
| // Show feedback | |
| const originalText = copyBtn.innerHTML; | |
| copyBtn.innerHTML = '<i class="fas fa-check mr-1"></i> Copied!'; | |
| setTimeout(() => { | |
| copyBtn.innerHTML = originalText; | |
| }, 1500); | |
| }); | |
| colorStepsContainer.appendChild(colorBlock); | |
| } | |
| } | |
| // Show picked color info | |
| function showPickedColorInfo(hex) { | |
| const color = chroma(hex); | |
| pickedColorSwatch.style.backgroundColor = hex; | |
| pickedColorHex.textContent = hex; | |
| const rgb = color.rgb(); | |
| pickedColorRgb.value = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; | |
| const hsl = color.hsl(); | |
| pickedColorHsl.value = `hsl(${Math.round(hsl[0])}, ${Math.round(hsl[1] * 100)}%, ${Math.round(hsl[2] * 100)}%)`; | |
| pickedColorName.value = getColorName(hex); | |
| pickedColorInfo.classList.remove('hidden'); | |
| // Position the handle on the gradient | |
| const color1 = color1HexInput.value; | |
| const color2 = color2HexInput.value; | |
| if (hex.toLowerCase() === color1.toLowerCase()) { | |
| colorPickerHandle.style.left = '0%'; | |
| } else if (hex.toLowerCase() === color2.toLowerCase()) { | |
| colorPickerHandle.style.left = '100%'; | |
| } else { | |
| // Find position in gradient | |
| const steps = parseInt(stepsInput.value); | |
| let closestPos = 50; | |
| for (let i = 0; i < steps; i++) { | |
| const t = i / (steps - 1); | |
| const stepColor = chroma.mix(color1, color2, t); | |
| if (stepColor.hex().toLowerCase() === hex.toLowerCase()) { | |
| closestPos = t * 100; | |
| break; | |
| } | |
| } | |
| colorPickerHandle.style.left = `${closestPos}%`; | |
| } | |
| } | |
| // Update color harmonies | |
| function updateColorHarmonies() { | |
| const color = chroma(color1HexInput.value); | |
| // Complementary colors | |
| complementaryColorsContainer.innerHTML = ''; | |
| const complementary = color.set('hsl.h', (color.get('hsl.h') + 180) % 360); | |
| addColorToHarmony(complementaryColorsContainer, color.hex()); | |
| addColorToHarmony(complementaryColorsContainer, complementary.hex()); | |
| // Analogous colors | |
| analogousColorsContainer.innerHTML = ''; | |
| const analogous1 = color.set('hsl.h', (color.get('hsl.h') + 30) % 360); | |
| const analogous2 = color.set('hsl.h', (color.get('hsl.h') - 30 + 360) % 360); | |
| addColorToHarmony(analogousColorsContainer, analogous2.hex()); | |
| addColorToHarmony(analogousColorsContainer, color.hex()); | |
| addColorToHarmony(analogousColorsContainer, analogous1.hex()); | |
| // Triadic colors | |
| triadicColorsContainer.innerHTML = ''; | |
| const triadic1 = color.set('hsl.h', (color.get('hsl.h') + 120) % 360); | |
| const triadic2 = color.set('hsl.h', (color.get('hsl.h') + 240) % 360); | |
| addColorToHarmony(triadicColorsContainer, color.hex()); | |
| addColorToHarmony(triadicColorsContainer, triadic1.hex()); | |
| addColorToHarmony(triadicColorsContainer, triadic2.hex()); | |
| // Tetradic colors | |
| tetradicColorsContainer.innerHTML = ''; | |
| const tetradic1 = color.set('hsl.h', (color.get('hsl.h') + 90) % 360); | |
| const tetradic2 = color.set('hsl.h', (color.get('hsl.h') + 180) % 360); | |
| const tetradic3 = color.set('hsl.h', (color.get('hsl.h') + 270) % 360); | |
| addColorToHarmony(tetradicColorsContainer, color.hex()); | |
| addColorToHarmony(tetradicColorsContainer, tetradic1.hex()); | |
| addColorToHarmony(tetradicColorsContainer, tetradic2.hex()); | |
| addColorToHarmony(tetradicColorsContainer, tetradic3.hex()); | |
| } | |
| // Add color to harmony container | |
| function addColorToHarmony(container, hex) { | |
| const colorBlock = document.createElement('div'); | |
| colorBlock.className = 'color-block h-16 rounded-lg flex flex-col items-center justify-center text-white text-xs font-medium'; | |
| colorBlock.style.backgroundColor = hex; | |
| colorBlock.innerHTML = ` | |
| <span class="font-mono">${hex}</span> | |
| <button class="copy-btn mt-1 px-2 py-1 bg-black bg-opacity-20 rounded-full text-xs" data-color="${hex}"> | |
| <i class="far fa-copy mr-1"></i> Copy | |
| </button> | |
| `; | |
| // Add click event to show color info | |
| colorBlock.addEventListener('click', () => { | |
| showPickedColorInfo(hex); | |
| }); | |
| // Add click event to copy button | |
| const copyBtn = colorBlock.querySelector('button'); | |
| copyBtn.addEventListener('click', (e) => { | |
| e.stopPropagation(); | |
| copyToClipboardFromAttribute(copyBtn, 'data-color'); | |
| // Show feedback | |
| const originalText = copyBtn.innerHTML; | |
| copyBtn.innerHTML = '<i class="fas fa-check mr-1"></i> Copied!'; | |
| setTimeout(() => { | |
| copyBtn.innerHTML = originalText; | |
| }, 1500); | |
| }); | |
| container.appendChild(colorBlock); | |
| } | |
| // Update contrast ratio | |
| function updateContrastRatio() { | |
| const color = color1HexInput.value; | |
| const bgColor = contrastBgInput.value; | |
| const ratio = chroma.contrast(color, bgColor); | |
| contrastRatioAA.textContent = ratio.toFixed(1) + ':1'; | |
| contrastRatioAAA.textContent = ratio.toFixed(1) + ':1'; | |
| // Update classes based on WCAG guidelines | |
| contrastRatioAA.classList.remove('aa-pass', 'aa-fail'); | |
| contrastRatioAAA.classList.remove('aaa-pass', 'aaa-fail'); | |
| if (ratio >= 4.5) { | |
| contrastRatioAA.classList.add('aa-pass'); | |
| } else { | |
| contrastRatioAA.classList.add('aa-fail'); | |
| } | |
| if (ratio >= 7) { | |
| contrastRatioAAA.classList.add('aaa-pass'); | |
| } else { | |
| contrastRatioAAA.classList.add('aaa-fail'); | |
| } | |
| // Update preview | |
| contrastPreview.style.backgroundColor = bgColor; | |
| const previewText = contrastPreview.querySelectorAll('p'); | |
| previewText.forEach(text => { | |
| text.style.color = color; | |
| }); | |
| } | |
| // Copy to clipboard | |
| function copyToClipboard(elementId) { | |
| const element = document.getElementById(elementId); | |
| element.select(); | |
| document.execCommand('copy'); | |
| // Show feedback | |
| const originalText = element.value; | |
| if (elementId === 'css-gradient' || elementId === 'svg-gradient') { | |
| const button = element.nextElementSibling; | |
| const originalButtonText = button.innerHTML; | |
| button.innerHTML = '<i class="fas fa-check mr-1"></i> Copied!'; | |
| setTimeout(() => { | |
| button.innerHTML = originalButtonText; | |
| }, 1500); | |
| } | |
| } | |
| function copyToClipboardFromAttribute(element, attribute) { | |
| const text = element.getAttribute(attribute); | |
| navigator.clipboard.writeText(text); | |
| } | |
| // Download PNG | |
| function downloadPng() { | |
| alert('PNG download functionality would be implemented in a real application using a canvas library like html2canvas.'); | |
| // In a real implementation, you would use html2canvas or similar to capture the gradient | |
| // and then create a download link for the PNG image. | |
| } | |
| // Event listeners | |
| color1Input.addEventListener('input', () => { | |
| color1HexInput.value = color1Input.value; | |
| updateColorInfo(color1Input, color1HexInput, color1RgbInput, color1HslInput, color1NameInput); | |
| updateGradient(); | |
| updateColorSteps(); | |
| updateColorHarmonies(); | |
| updateContrastRatio(); | |
| }); | |
| color1HexInput.addEventListener('change', () => { | |
| if (chroma.valid(color1HexInput.value)) { | |
| color1Input.value = chroma(color1HexInput.value).hex(); | |
| updateColorInfo(color1Input, color1HexInput, color1RgbInput, color1HslInput, color1NameInput); | |
| updateGradient(); | |
| updateColorSteps(); | |
| updateColorHarmonies(); | |
| updateContrastRatio(); | |
| } else { | |
| alert('Invalid HEX color code'); | |
| color1HexInput.value = color1Input.value; | |
| } | |
| }); | |
| color2Input.addEventListener('input', () => { | |
| color2HexInput.value = color2Input.value; | |
| updateColorInfo(color2Input, color2HexInput, color2RgbInput, color2HslInput, color2NameInput); | |
| updateGradient(); | |
| updateColorSteps(); | |
| updateContrastRatio(); | |
| }); | |
| color2HexInput.addEventListener('change', () => { | |
| if (chroma.valid(color2HexInput.value)) { | |
| color2Input.value = chroma(color2HexInput.value).hex(); | |
| updateColorInfo(color2Input, color2HexInput, color2RgbInput, color2HslInput, color2NameInput); | |
| updateGradient(); | |
| updateColorSteps(); | |
| updateContrastRatio(); | |
| } else { | |
| alert('Invalid HEX color code'); | |
| color2HexInput.value = color2Input.value; | |
| } | |
| }); | |
| stepsInput.addEventListener('input', () => { | |
| updateColorSteps(); | |
| }); | |
| // Gradient picker handle interaction | |
| let isDragging = false; | |
| colorPickerHandle.addEventListener('mousedown', () => { | |
| isDragging = true; | |
| }); | |
| document.addEventListener('mousemove', (e) => { | |
| if (isDragging) { | |
| const gradientRect = gradientPreview.getBoundingClientRect(); | |
| let x = e.clientX - gradientRect.left; | |
| x = Math.max(0, Math.min(x, gradientRect.width)); | |
| const percentage = (x / gradientRect.width) * 100; | |
| colorPickerHandle.style.left = `${percentage}%`; | |
| // Calculate color at this position | |
| const color1 = color1HexInput.value; | |
| const color2 = color2HexInput.value; | |
| const color = chroma.mix(color1, color2, percentage / 100); | |
| // Update picked color info | |
| pickedColorSwatch.style.backgroundColor = color.hex(); | |
| pickedColorHex.textContent = color.hex(); | |
| const rgb = color.rgb(); | |
| pickedColorRgb.value = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; | |
| const hsl = color.hsl(); | |
| pickedColorHsl.value = `hsl(${Math.round(hsl[0])}, ${Math.round(hsl[1] * 100)}%, ${Math.round(hsl[2] * 100)}%)`; | |
| pickedColorName.value = getColorName(color.hex()); | |
| pickedColorInfo.classList.remove('hidden'); | |
| } | |
| }); | |
| document.addEventListener('mouseup', () => { | |
| isDragging = false; | |
| }); | |
| gradientPreview.addEventListener('click', (e) => { | |
| const gradientRect = gradientPreview.getBoundingClientRect(); | |
| let x = e.clientX - gradientRect.left; | |
| x = Math.max(0, Math.min(x, gradientRect.width)); | |
| const percentage = (x / gradientRect.width) * 100; | |
| colorPickerHandle.style.left = `${percentage}%`; | |
| // Calculate color at this position | |
| const color1 = color1HexInput.value; | |
| const color2 = color2HexInput.value; | |
| const color = chroma.mix(color1, color2, percentage / 100); | |
| // Update picked color info | |
| pickedColorSwatch.style.backgroundColor = color.hex(); | |
| pickedColorHex.textContent = color.hex(); | |
| const rgb = color.rgb(); | |
| pickedColorRgb.value = `rgb(${rgb[0]}, ${rgb[1]}, ${rgb[2]})`; | |
| const hsl = color.hsl(); | |
| pickedColorHsl.value = `hsl(${Math.round(hsl[0])}, ${Math.round(hsl[1] * 100)}%, ${Math.round(hsl[2] * 100)}%)`; | |
| pickedColorName.value = getColorName(color.hex()); | |
| pickedColorInfo.classList.remove('hidden'); | |
| }); | |
| copyPickedColorBtn.addEventListener('click', () => { | |
| navigator.clipboard.writeText(pickedColorHex.textContent); | |
| // Show feedback | |
| const originalText = copyPickedColorBtn.innerHTML; | |
| copyPickedColorBtn.innerHTML = '<i class="fas fa-check mr-1"></i> Copied!'; | |
| setTimeout(() => { | |
| copyPickedColorBtn.innerHTML = originalText; | |
| }, 1500); | |
| }); | |
| contrastBgInput.addEventListener('input', () => { | |
| updateContrastRatio(); | |
| }); | |
| downloadPngBtn.addEventListener('click', downloadPng); | |
| // Initialize the app | |
| init(); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Decide2Design/gradiant-picker" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |