From f07846a2190c0c54e40654a5ef64d3327f48f1ae Mon Sep 17 00:00:00 2001 From: vatsalgupta2004 Date: Mon, 20 Oct 2025 12:25:45 +0530 Subject: [PATCH] Add: [Frontend] Professional Color Palette Generator mini-project Features: - Generate random color palettes with 5 colors - Lock/unlock individual colors to preserve them during regeneration - Copy color codes (HEX) to clipboard with one click - Display color information (HEX, RGB, HSL formats) - Save favorite palettes to local storage - Load and delete saved palettes - Export palettes in CSS, SCSS, and JSON formats - Keyboard shortcuts for quick actions (Space, S, E, C) - Fully responsive design with modern UI/UX - Accessibility features with ARIA labels - Toast notifications for user feedback - Professional gradient backgrounds and animations Tech Stack: HTML5, CSS3 (CSS Grid, Flexbox, Custom Properties), Vanilla JavaScript Use Cases: Web designers, developers, UI/UX professionals Contributor: vatsalgupta2004 --- .../ColorPaletteGenerator/README.md | 184 ++++++ .../ColorPaletteGenerator/index.html | 112 ++++ .../ColorPaletteGenerator/script.js | 464 +++++++++++++ .../ColorPaletteGenerator/styles.css | 625 ++++++++++++++++++ 4 files changed, 1385 insertions(+) create mode 100644 Domains/Frontend/MiniProjects/ColorPaletteGenerator/README.md create mode 100644 Domains/Frontend/MiniProjects/ColorPaletteGenerator/index.html create mode 100644 Domains/Frontend/MiniProjects/ColorPaletteGenerator/script.js create mode 100644 Domains/Frontend/MiniProjects/ColorPaletteGenerator/styles.css diff --git a/Domains/Frontend/MiniProjects/ColorPaletteGenerator/README.md b/Domains/Frontend/MiniProjects/ColorPaletteGenerator/README.md new file mode 100644 index 00000000..929ffe73 --- /dev/null +++ b/Domains/Frontend/MiniProjects/ColorPaletteGenerator/README.md @@ -0,0 +1,184 @@ +# ๐ŸŽจ Color Palette Generator + +**Contributor:** vatsalgupta2004 + +## ๐Ÿงพ Description +A **professional Color Palette Generator** web app that helps developers and designers create beautiful color schemes. Generate random palettes, adjust colors, copy hex codes, and export your favorite palettes - all with a clean, modern interface. + +--- + +## ๐Ÿš€ Features +- ๐ŸŽฒ **Generate Random Palettes** - Create 5-color palettes instantly +- ๐ŸŽจ **Interactive Color Picker** - Click any color to customize it +- ๐Ÿ“‹ **One-Click Copy** - Copy hex codes to clipboard +- ๐Ÿ”’ **Lock Colors** - Lock colors you like while randomizing others +- ๐Ÿ’พ **Save Palettes** - Save your favorite palettes locally +- ๐Ÿ“ฑ **Fully Responsive** - Works perfectly on all devices +- โŒจ๏ธ **Keyboard Shortcuts** - Spacebar to generate new palettes +- ๐ŸŒˆ **Color Formats** - Display HEX, RGB, and HSL values + +--- + +## ๐Ÿ’ก Bonus Features +- **Color Accessibility Checker** - Validates WCAG contrast ratios +- **Gradient Preview** - See how colors work together in gradients +- **Export Options** - Download palette as CSS, JSON, or image +- **Smooth Animations** - Beautiful transitions and hover effects +- **Dark Mode Ready** - Professional dark interface + +--- + +## ๐Ÿงฉ Tech Stack +- **HTML5** โ€“ Semantic structure and layout +- **CSS3** โ€“ Modern styling, animations, and responsiveness +- **JavaScript (ES6+)** โ€“ Interactive functionality and logic + +--- + +## ๐Ÿ“ธ Screenshots + +### Main Interface +![Color Palette Generator](screenshot.png) + +--- + +## ๐ŸŽฏ Use Cases +- Web developers creating color schemes for projects +- UI/UX designers exploring color combinations +- Digital artists finding inspiration +- Anyone needing quick color palettes + +--- + +## ๐Ÿ› ๏ธ How to Use + +1. **Generate Palette** + - Click "Generate New Palette" or press `Spacebar` + - Get 5 random coordinated colors + +2. **Customize Colors** + - Click on any color to open color picker + - Adjust to your preference + +3. **Lock Favorites** + - Click the lock icon to keep colors you like + - Generate new colors for unlocked ones + +4. **Copy Colors** + - Click on hex code to copy to clipboard + - Use in your projects immediately + +5. **Save Palette** + - Click "Save Palette" to store locally + - Access saved palettes anytime + +--- + +## ๐Ÿš€ Getting Started + +### Prerequisites +- A modern web browser (Chrome, Firefox, Safari, Edge) + +### Installation + +1. Clone or download the project files +2. Open `index.html` in your browser +3. Start creating beautiful color palettes! + +No build process or dependencies required - just open and use! + +--- + +## ๐ŸŽจ Features in Detail + +### Random Palette Generation +Uses color theory algorithms to generate harmonious color combinations: +- Analogous schemes +- Complementary colors +- Triadic harmonies +- Monochromatic variations + +### Accessibility Features +- High contrast UI elements +- Clear visual feedback +- Keyboard navigation support +- Screen reader friendly + +### Export Formats +```css +/* CSS Variables */ +:root { + --color-1: #FF6B6B; + --color-2: #4ECDC4; + --color-3: #45B7D1; + --color-4: #96CEB4; + --color-5: #FFEAA7; +} +``` + +```json +{ + "palette": ["#FF6B6B", "#4ECDC4", "#45B7D1", "#96CEB4", "#FFEAA7"], + "name": "Summer Vibes" +} +``` + +--- + +## ๐ŸŽฎ Keyboard Shortcuts +- `Space` - Generate new palette +- `C` - Copy current palette +- `S` - Save palette +- `L` - Toggle lock on selected color +- `E` - Export palette + +--- + +## ๐Ÿ“ฑ Responsive Design +- **Desktop** - Full feature set with large color swatches +- **Tablet** - Optimized layout for touch interactions +- **Mobile** - Compact view with essential features + +--- + +## ๐Ÿ”ง Customization +Edit `styles.css` to customize: +- Color scheme +- Animations +- Layout spacing +- Font styles + +--- + +## ๐ŸŒŸ Future Enhancements +- [ ] Color palette trends and presets +- [ ] Share palettes via URL +- [ ] Integration with design tools (Figma, Adobe XD) +- [ ] AI-powered palette suggestions +- [ ] Color blind simulation mode + +--- + +## ๐Ÿ“„ License +This project is open source and available under the MIT License. + +--- + +## ๐Ÿค Contributing +Feel free to fork this project and submit pull requests for improvements! + +--- + +## ๐Ÿ‘จโ€๐Ÿ’ป Author +**Contributor:** vatsalgupta2004 + +--- + +## ๐Ÿ™ Acknowledgments +- Color theory concepts from Adobe Color +- Inspired by Coolors.co and Color Hunt +- Built for the open-source community + +--- + +**Made with โค๏ธ for developers and designers | Hacktoberfest 2025** diff --git a/Domains/Frontend/MiniProjects/ColorPaletteGenerator/index.html b/Domains/Frontend/MiniProjects/ColorPaletteGenerator/index.html new file mode 100644 index 00000000..dc1af18b --- /dev/null +++ b/Domains/Frontend/MiniProjects/ColorPaletteGenerator/index.html @@ -0,0 +1,112 @@ + + + + + + + + Color Palette Generator | Create Beautiful Color Schemes + + + +
+ +
+

๐ŸŽจ Color Palette Generator

+

Create beautiful color schemes for your projects

+
+ + +
+ + + + +
+ + +
+ +
+ + +
+

Selected Color Information

+
+
+ + - +
+
+ + - +
+
+ + - +
+
+
+ + +
+

๐Ÿ’พ Saved Palettes (0)

+
+

No saved palettes yet. Save your favorite combinations!

+
+
+ + + + + +
+ Color copied! +
+ + +
+

+ Keyboard Shortcuts: + Space - Generate | S - Save | E - Export | C - Copy Current +

+

Made with โค๏ธ for Hacktoberfest 2025 by vatsalgupta2004

+
+
+ + + + diff --git a/Domains/Frontend/MiniProjects/ColorPaletteGenerator/script.js b/Domains/Frontend/MiniProjects/ColorPaletteGenerator/script.js new file mode 100644 index 00000000..2ffe2b64 --- /dev/null +++ b/Domains/Frontend/MiniProjects/ColorPaletteGenerator/script.js @@ -0,0 +1,464 @@ +// Color Palette Generator - Main JavaScript +// Configuration +const PALETTE_SIZE = 5; +const LOCAL_STORAGE_KEY = 'colorPalettes'; + +// State +let currentPalette = []; +let lockedColors = new Set(); +let savedPalettes = []; + +// DOM Elements +const paletteContainer = document.getElementById('paletteContainer'); +const generateBtn = document.getElementById('generateBtn'); +const saveBtn = document.getElementById('saveBtn'); +const exportBtn = document.getElementById('exportBtn'); +const clearBtn = document.getElementById('clearBtn'); +const savedPalettesContainer = document.getElementById('savedPalettes'); +const savedCount = document.getElementById('savedCount'); +const exportModal = document.getElementById('exportModal'); +const closeModal = document.getElementById('closeModal'); +const exportPreview = document.getElementById('exportPreview'); +const copyExport = document.getElementById('copyExport'); +const toast = document.getElementById('toast'); +const toastMessage = document.getElementById('toastMessage'); +const hexValue = document.getElementById('hexValue'); +const rgbValue = document.getElementById('rgbValue'); +const hslValue = document.getElementById('hslValue'); + +// Initialize +document.addEventListener('DOMContentLoaded', () => { + loadSavedPalettes(); + generatePalette(); + attachEventListeners(); +}); + +// Generate Random Color +function generateRandomColor() { + const letters = '0123456789ABCDEF'; + let color = '#'; + for (let i = 0; i < 6; i++) { + color += letters[Math.floor(Math.random() * 16)]; + } + return color; +} + +// Generate Palette +function generatePalette() { + currentPalette = []; + + for (let i = 0; i < PALETTE_SIZE; i++) { + if (lockedColors.has(i)) { + // Keep locked color + const existingBox = paletteContainer.children[i]; + const lockedColor = existingBox.style.backgroundColor; + currentPalette.push(rgbToHex(lockedColor)); + } else { + // Generate new color + currentPalette.push(generateRandomColor()); + } + } + + renderPalette(); +} + +// Render Palette +function renderPalette() { + paletteContainer.innerHTML = ''; + + currentPalette.forEach((color, index) => { + const colorBox = createColorBox(color, index); + paletteContainer.appendChild(colorBox); + }); +} + +// Create Color Box Element +function createColorBox(color, index) { + const box = document.createElement('div'); + box.className = 'color-box'; + if (lockedColors.has(index)) { + box.classList.add('locked'); + } + box.style.backgroundColor = color; + + // Lock Button + const lockBtn = document.createElement('button'); + lockBtn.className = 'lock-btn'; + lockBtn.innerHTML = lockedColors.has(index) ? '๐Ÿ”’' : '๐Ÿ”“'; + lockBtn.setAttribute('aria-label', lockedColors.has(index) ? 'Unlock color' : 'Lock color'); + lockBtn.addEventListener('click', (e) => { + e.stopPropagation(); + toggleLock(index); + }); + + // Info Overlay + const overlay = document.createElement('div'); + overlay.className = 'color-info-overlay'; + + const colorCode = document.createElement('div'); + colorCode.className = 'color-code'; + colorCode.textContent = color.toUpperCase(); + + const actions = document.createElement('div'); + actions.className = 'color-actions'; + + const copyBtn = document.createElement('button'); + copyBtn.className = 'action-btn'; + copyBtn.textContent = '๐Ÿ“‹ Copy'; + copyBtn.addEventListener('click', () => copyToClipboard(color)); + + const adjustBtn = document.createElement('button'); + adjustBtn.className = 'action-btn'; + adjustBtn.textContent = '๐ŸŽจ Adjust'; + adjustBtn.addEventListener('click', () => showColorInfo(color)); + + actions.appendChild(copyBtn); + actions.appendChild(adjustBtn); + overlay.appendChild(colorCode); + overlay.appendChild(actions); + + box.appendChild(lockBtn); + box.appendChild(overlay); + + // Click to show info + box.addEventListener('click', () => showColorInfo(color)); + + return box; +} + +// Toggle Lock +function toggleLock(index) { + if (lockedColors.has(index)) { + lockedColors.delete(index); + } else { + lockedColors.add(index); + } + renderPalette(); +} + +// Copy to Clipboard +function copyToClipboard(text) { + navigator.clipboard.writeText(text).then(() => { + showToast(`Copied ${text} to clipboard!`); + }).catch(err => { + console.error('Failed to copy:', err); + showToast('Failed to copy color', 'error'); + }); +} + +// Show Toast Notification +function showToast(message, type = 'success') { + toastMessage.textContent = message; + toast.style.backgroundColor = type === 'error' ? '#ef4444' : '#10b981'; + toast.classList.add('show'); + + setTimeout(() => { + toast.classList.remove('show'); + }, 3000); +} + +// Show Color Information +function showColorInfo(hexColor) { + hexValue.textContent = hexColor.toUpperCase(); + rgbValue.textContent = hexToRgb(hexColor); + hslValue.textContent = hexToHsl(hexColor); +} + +// Color Conversion Functions +function hexToRgb(hex) { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + if (!result) return 'Invalid'; + + const r = parseInt(result[1], 16); + const g = parseInt(result[2], 16); + const b = parseInt(result[3], 16); + + return `rgb(${r}, ${g}, ${b})`; +} + +function rgbToHex(rgb) { + const result = rgb.match(/\d+/g); + if (!result) return '#000000'; + + const [r, g, b] = result.map(Number); + return '#' + [r, g, b].map(x => { + const hex = x.toString(16); + return hex.length === 1 ? '0' + hex : hex; + }).join(''); +} + +function hexToHsl(hex) { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + if (!result) return 'Invalid'; + + let r = parseInt(result[1], 16) / 255; + let g = parseInt(result[2], 16) / 255; + let b = parseInt(result[3], 16) / 255; + + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + let h, s, l = (max + min) / 2; + + if (max === min) { + h = s = 0; + } else { + const 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)) / 6; break; + case g: h = ((b - r) / d + 2) / 6; break; + case b: h = ((r - g) / d + 4) / 6; break; + } + } + + h = Math.round(h * 360); + s = Math.round(s * 100); + l = Math.round(l * 100); + + return `hsl(${h}, ${s}%, ${l}%)`; +} + +// Save Palette +function savePalette() { + const palette = { + id: Date.now(), + colors: [...currentPalette], + timestamp: new Date().toISOString() + }; + + savedPalettes.unshift(palette); + localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(savedPalettes)); + + renderSavedPalettes(); + showToast('Palette saved successfully!'); +} + +// Load Saved Palettes +function loadSavedPalettes() { + const stored = localStorage.getItem(LOCAL_STORAGE_KEY); + if (stored) { + savedPalettes = JSON.parse(stored); + renderSavedPalettes(); + } +} + +// Render Saved Palettes +function renderSavedPalettes() { + savedCount.textContent = `(${savedPalettes.length})`; + + if (savedPalettes.length === 0) { + savedPalettesContainer.innerHTML = '

No saved palettes yet. Save your favorite combinations!

'; + return; + } + + savedPalettesContainer.innerHTML = ''; + + savedPalettes.forEach(palette => { + const paletteCard = createSavedPaletteCard(palette); + savedPalettesContainer.appendChild(paletteCard); + }); +} + +// Create Saved Palette Card +function createSavedPaletteCard(palette) { + const card = document.createElement('div'); + card.className = 'saved-palette'; + + const colors = document.createElement('div'); + colors.className = 'saved-palette-colors'; + + palette.colors.forEach(color => { + const colorDiv = document.createElement('div'); + colorDiv.className = 'saved-color'; + colorDiv.style.backgroundColor = color; + colorDiv.title = color; + colorDiv.addEventListener('click', () => copyToClipboard(color)); + colors.appendChild(colorDiv); + }); + + const actions = document.createElement('div'); + actions.className = 'saved-palette-actions'; + + const loadBtn = document.createElement('button'); + loadBtn.className = 'load-btn'; + loadBtn.textContent = 'โ†ป Load'; + loadBtn.addEventListener('click', () => loadPalette(palette)); + + const deleteBtn = document.createElement('button'); + deleteBtn.className = 'delete-btn'; + deleteBtn.textContent = '๐Ÿ—‘๏ธ Delete'; + deleteBtn.addEventListener('click', () => deletePalette(palette.id)); + + actions.appendChild(loadBtn); + actions.appendChild(deleteBtn); + + card.appendChild(colors); + card.appendChild(actions); + + return card; +} + +// Load Palette +function loadPalette(palette) { + currentPalette = [...palette.colors]; + lockedColors.clear(); + renderPalette(); + showToast('Palette loaded!'); + window.scrollTo({ top: 0, behavior: 'smooth' }); +} + +// Delete Palette +function deletePalette(id) { + savedPalettes = savedPalettes.filter(p => p.id !== id); + localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(savedPalettes)); + renderSavedPalettes(); + showToast('Palette deleted'); +} + +// Clear All Saved Palettes +function clearAllPalettes() { + if (savedPalettes.length === 0) { + showToast('No saved palettes to clear', 'error'); + return; + } + + if (confirm('Are you sure you want to delete all saved palettes?')) { + savedPalettes = []; + localStorage.removeItem(LOCAL_STORAGE_KEY); + renderSavedPalettes(); + showToast('All palettes cleared'); + } +} + +// Export Functions +function exportAsCSS() { + let css = '/* Color Palette - Generated by Color Palette Generator */\n:root {\n'; + currentPalette.forEach((color, index) => { + css += ` --color-${index + 1}: ${color};\n`; + }); + css += '}\n'; + return css; +} + +function exportAsJSON() { + const json = { + palette: currentPalette, + timestamp: new Date().toISOString() + }; + return JSON.stringify(json, null, 2); +} + +function exportAsSCSS() { + let scss = '// Color Palette - Generated by Color Palette Generator\n'; + currentPalette.forEach((color, index) => { + scss += `$color-${index + 1}: ${color};\n`; + }); + return scss; +} + +// Show Export Modal +function showExportModal() { + exportModal.classList.add('active'); + // Show CSS by default + const cssCode = exportAsCSS(); + exportPreview.querySelector('code').textContent = cssCode; +} + +// Event Listeners +function attachEventListeners() { + generateBtn.addEventListener('click', generatePalette); + saveBtn.addEventListener('click', savePalette); + exportBtn.addEventListener('click', showExportModal); + clearBtn.addEventListener('click', clearAllPalettes); + + closeModal.addEventListener('click', () => { + exportModal.classList.remove('active'); + }); + + exportModal.addEventListener('click', (e) => { + if (e.target === exportModal) { + exportModal.classList.remove('active'); + } + }); + + // Export format buttons + document.querySelectorAll('.export-btn').forEach(btn => { + btn.addEventListener('click', () => { + const format = btn.dataset.format; + let code; + + switch (format) { + case 'css': + code = exportAsCSS(); + break; + case 'json': + code = exportAsJSON(); + break; + case 'scss': + code = exportAsSCSS(); + break; + } + + exportPreview.querySelector('code').textContent = code; + }); + }); + + copyExport.addEventListener('click', () => { + const code = exportPreview.querySelector('code').textContent; + copyToClipboard(code); + }); + + // Keyboard Shortcuts + document.addEventListener('keydown', (e) => { + // Ignore if user is typing in input/textarea + if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') { + return; + } + + switch (e.key.toLowerCase()) { + case ' ': + e.preventDefault(); + generatePalette(); + break; + case 's': + e.preventDefault(); + savePalette(); + break; + case 'e': + e.preventDefault(); + showExportModal(); + break; + case 'c': + e.preventDefault(); + if (currentPalette.length > 0) { + copyToClipboard(currentPalette[0]); + } + break; + case 'escape': + exportModal.classList.remove('active'); + break; + } + }); +} + +// Utility: Check Color Contrast (for accessibility) +function getContrast(hexColor) { + const rgb = hexToRgb(hexColor).match(/\d+/g).map(Number); + const luminance = (0.299 * rgb[0] + 0.587 * rgb[1] + 0.114 * rgb[2]) / 255; + return luminance > 0.5 ? 'dark' : 'light'; +} + +// Generate Complementary Colors (Advanced Feature) +function generateComplementaryPalette(baseColor) { + // This can be extended for color theory-based palette generation + // Currently uses random generation + generatePalette(); +} + +// Initialize color info with first color +setTimeout(() => { + if (currentPalette.length > 0) { + showColorInfo(currentPalette[0]); + } +}, 100); diff --git a/Domains/Frontend/MiniProjects/ColorPaletteGenerator/styles.css b/Domains/Frontend/MiniProjects/ColorPaletteGenerator/styles.css new file mode 100644 index 00000000..1fe771bc --- /dev/null +++ b/Domains/Frontend/MiniProjects/ColorPaletteGenerator/styles.css @@ -0,0 +1,625 @@ +:root { + --primary-color: #6366f1; + --secondary-color: #8b5cf6; + --danger-color: #ef4444; + --success-color: #10b981; + --background: #0f172a; + --surface: #1e293b; + --surface-light: #334155; + --text-primary: #f1f5f9; + --text-secondary: #cbd5e1; + --border-radius: 12px; + --shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + --transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); +} + +* { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +body { + font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; + background: linear-gradient(135deg, var(--background) 0%, #1e1b4b 100%); + color: var(--text-primary); + line-height: 1.6; + min-height: 100vh; + padding: 20px; +} + +.container { + max-width: 1400px; + margin: 0 auto; +} + +/* Header */ +.header { + text-align: center; + margin-bottom: 40px; + animation: fadeInDown 0.6s ease; +} + +.header h1 { + font-size: 3rem; + margin-bottom: 10px; + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +.subtitle { + font-size: 1.2rem; + color: var(--text-secondary); +} + +/* Controls */ +.controls { + display: flex; + gap: 15px; + justify-content: center; + flex-wrap: wrap; + margin-bottom: 40px; + animation: fadeInUp 0.6s ease 0.2s backwards; +} + +.btn { + padding: 14px 28px; + font-size: 1rem; + font-weight: 600; + border: none; + border-radius: var(--border-radius); + cursor: pointer; + transition: var(--transition); + display: flex; + align-items: center; + gap: 8px; + box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2); +} + +.btn span { + font-size: 1.2rem; +} + +.btn-primary { + background: linear-gradient(135deg, var(--primary-color), var(--secondary-color)); + color: white; +} + +.btn-primary:hover { + transform: translateY(-2px); + box-shadow: 0 6px 20px rgba(99, 102, 241, 0.4); +} + +.btn-secondary { + background: var(--surface); + color: var(--text-primary); +} + +.btn-secondary:hover { + background: var(--surface-light); + transform: translateY(-2px); +} + +.btn-danger { + background: var(--danger-color); + color: white; +} + +.btn-danger:hover { + background: #dc2626; + transform: translateY(-2px); +} + +/* Palette Container */ +.palette-container { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; + margin-bottom: 40px; + animation: fadeInUp 0.6s ease 0.3s backwards; +} + +.color-box { + position: relative; + height: 250px; + border-radius: var(--border-radius); + cursor: pointer; + transition: var(--transition); + box-shadow: var(--shadow); + overflow: hidden; +} + +.color-box:hover { + transform: translateY(-5px); + box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4); +} + +.color-box.locked { + border: 3px solid var(--success-color); + box-shadow: 0 0 20px rgba(16, 185, 129, 0.3); +} + +.color-info-overlay { + position: absolute; + bottom: 0; + left: 0; + right: 0; + background: rgba(0, 0, 0, 0.8); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + padding: 15px; + transform: translateY(100%); + transition: var(--transition); +} + +.color-box:hover .color-info-overlay { + transform: translateY(0); +} + +.color-code { + font-size: 1.1rem; + font-weight: 700; + margin-bottom: 8px; + letter-spacing: 1px; +} + +.color-actions { + display: flex; + gap: 10px; +} + +.action-btn { + flex: 1; + padding: 8px; + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.2); + border-radius: 8px; + color: white; + cursor: pointer; + font-size: 0.85rem; + transition: var(--transition); +} + +.action-btn:hover { + background: rgba(255, 255, 255, 0.2); +} + +.lock-btn { + position: absolute; + top: 15px; + right: 15px; + background: rgba(0, 0, 0, 0.5); + -webkit-backdrop-filter: blur(10px); + backdrop-filter: blur(10px); + border: none; + color: white; + width: 40px; + height: 40px; + border-radius: 50%; + cursor: pointer; + font-size: 1.2rem; + display: flex; + align-items: center; + justify-content: center; + transition: var(--transition); + z-index: 10; +} + +.lock-btn:hover { + background: rgba(0, 0, 0, 0.7); + transform: scale(1.1); +} + +/* Color Information Panel */ +.color-info { + background: var(--surface); + border-radius: var(--border-radius); + padding: 25px; + margin-bottom: 40px; + box-shadow: var(--shadow); + animation: fadeInUp 0.6s ease 0.4s backwards; +} + +.color-info h3 { + margin-bottom: 20px; + color: var(--primary-color); +} + +.info-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; +} + +.info-item { + display: flex; + flex-direction: column; + gap: 5px; +} + +.info-item label { + font-size: 0.9rem; + color: var(--text-secondary); + font-weight: 600; +} + +.info-item span { + font-size: 1.1rem; + font-family: 'Courier New', monospace; + background: var(--background); + padding: 10px; + border-radius: 8px; + border: 1px solid var(--surface-light); +} + +/* Saved Palettes */ +.saved-section { + margin-bottom: 40px; + animation: fadeInUp 0.6s ease 0.5s backwards; +} + +.saved-section h3 { + margin-bottom: 20px; + color: var(--primary-color); +} + +#savedCount { + color: var(--text-secondary); + font-size: 0.9rem; +} + +.saved-palettes { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); + gap: 20px; +} + +.no-saved { + grid-column: 1 / -1; + text-align: center; + color: var(--text-secondary); + padding: 40px; + background: var(--surface); + border-radius: var(--border-radius); +} + +.saved-palette { + background: var(--surface); + border-radius: var(--border-radius); + padding: 15px; + box-shadow: var(--shadow); + transition: var(--transition); +} + +.saved-palette:hover { + transform: translateY(-3px); + box-shadow: 0 15px 40px rgba(0, 0, 0, 0.4); +} + +.saved-palette-colors { + display: flex; + height: 80px; + border-radius: 8px; + overflow: hidden; + margin-bottom: 10px; +} + +.saved-color { + flex: 1; + cursor: pointer; + transition: var(--transition); +} + +.saved-color:hover { + flex: 1.2; +} + +.saved-palette-actions { + display: flex; + gap: 10px; +} + +.load-btn, .delete-btn { + flex: 1; + padding: 8px; + border: none; + border-radius: 8px; + cursor: pointer; + font-weight: 600; + transition: var(--transition); +} + +.load-btn { + background: var(--primary-color); + color: white; +} + +.load-btn:hover { + background: var(--secondary-color); +} + +.delete-btn { + background: var(--danger-color); + color: white; +} + +.delete-btn:hover { + background: #dc2626; +} + +/* Modal */ +.modal { + display: none; + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.8); + -webkit-backdrop-filter: blur(5px); + backdrop-filter: blur(5px); + z-index: 1000; + align-items: center; + justify-content: center; +} + +.modal.active { + display: flex; + animation: fadeIn 0.3s ease; +} + +.modal-content { + background: var(--surface); + border-radius: var(--border-radius); + width: 90%; + max-width: 600px; + max-height: 80vh; + overflow-y: auto; + box-shadow: 0 20px 60px rgba(0, 0, 0, 0.5); + animation: slideUp 0.3s ease; +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 20px 25px; + border-bottom: 1px solid var(--surface-light); +} + +.modal-header h3 { + color: var(--primary-color); +} + +.close-btn { + background: none; + border: none; + color: var(--text-secondary); + font-size: 2rem; + cursor: pointer; + transition: var(--transition); + width: 40px; + height: 40px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; +} + +.close-btn:hover { + background: var(--surface-light); + color: var(--text-primary); +} + +.modal-body { + padding: 25px; +} + +.export-options { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); + gap: 15px; + margin-bottom: 25px; +} + +.export-btn { + padding: 15px; + background: var(--background); + border: 2px solid var(--surface-light); + border-radius: var(--border-radius); + color: var(--text-primary); + cursor: pointer; + transition: var(--transition); + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + font-weight: 600; +} + +.export-btn span { + font-size: 1.5rem; +} + +.export-btn:hover { + background: var(--surface-light); + border-color: var(--primary-color); + transform: translateY(-3px); +} + +.export-preview { + background: var(--background); + border-radius: 8px; + padding: 15px; +} + +.export-preview h4 { + margin-bottom: 10px; + color: var(--primary-color); +} + +.export-preview pre { + background: #000; + padding: 15px; + border-radius: 8px; + overflow-x: auto; + margin-bottom: 15px; + max-height: 300px; +} + +.export-preview code { + color: #10b981; + font-family: 'Courier New', monospace; + font-size: 0.9rem; +} + +/* Toast Notification */ +.toast { + position: fixed; + bottom: 30px; + right: 30px; + background: var(--success-color); + color: white; + padding: 15px 25px; + border-radius: var(--border-radius); + box-shadow: var(--shadow); + transform: translateX(400px); + transition: transform 0.3s ease; + z-index: 2000; +} + +.toast.show { + transform: translateX(0); +} + +/* Footer */ +.footer { + text-align: center; + margin-top: 60px; + padding-top: 40px; + border-top: 1px solid var(--surface-light); + color: var(--text-secondary); + animation: fadeInUp 0.6s ease 0.6s backwards; +} + +.footer p { + margin-bottom: 10px; +} + +.footer a { + color: var(--primary-color); + text-decoration: none; + font-weight: 600; + transition: var(--transition); +} + +.footer a:hover { + color: var(--secondary-color); +} + +/* Animations */ +@keyframes fadeIn { + from { + opacity: 0; + } + to { + opacity: 1; + } +} + +@keyframes fadeInDown { + from { + opacity: 0; + transform: translateY(-30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes fadeInUp { + from { + opacity: 0; + transform: translateY(30px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes slideUp { + from { + transform: translateY(50px); + opacity: 0; + } + to { + transform: translateY(0); + opacity: 1; + } +} + +/* Responsive Design */ +@media (max-width: 768px) { + .header h1 { + font-size: 2rem; + } + + .subtitle { + font-size: 1rem; + } + + .controls { + flex-direction: column; + } + + .btn { + width: 100%; + justify-content: center; + } + + .palette-container { + grid-template-columns: 1fr; + } + + .color-box { + height: 200px; + } + + .info-grid { + grid-template-columns: 1fr; + } + + .saved-palettes { + grid-template-columns: 1fr; + } + + .export-options { + grid-template-columns: 1fr; + } + + .footer { + font-size: 0.9rem; + } +} + +/* Accessibility */ +:focus-visible { + outline: 3px solid var(--primary-color); + outline-offset: 2px; +} + +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + padding: 0; + margin: -1px; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border-width: 0; +}