Générateur de Palettes
Génère 5 couleurs harmonieuses. Hover pour voir le hex, click pour copier.
- Penser la couleur en HSL
- Convertir HSL en hexadécimal
- Copier dans le presse-papier avec l'API Clipboard
Pourquoi ce widget ? Parce que la couleur est le sujet le plus mal compris en développement front-end. La plupart des devs copient des hex au hasard et espèrent que ça marche.
Ce widget t'apprend le modèle HSL : le seul modèle de couleur qui a un sens pour un humain :
H (Hue) = la teinte sur la roue chromatique 0° = rouge, 120° = vert, 240° = bleu, 360° = rouge S (Saturation) = l'intensité de la couleur 0% = gris, 100% = couleur pure L (Lightness) = la luminosité 0% = noir, 50% = normal, 100% = blanc
Le secret des palettes harmonieuses : Si tu prends une teinte de base et que tu espaces les autres teintes de 72° (360°/5) sur la roue, tu obtiens toujours une palette visuellement cohérente. C'est de la mathématique appliquée à l'art.
GÉNÉRATION DE LA PALETTE On choisit une teinte de base aléatoire (0-360°) puis on espace les 4 autres de 72° (360°/5)
function generatePalette() { const base = Math.floor(Math.random() * 360)Array.from crée un tableau de 5 éléments Pour chaque index i (0 à 4), on calcule la teinte Le % 360 garantit qu'on reste dans la roue (0-360)
return Array.from({ length: 5 }, (_, i) => { const hue = (base + i * 72) % 360On garde saturation et lightness fixes pour que toutes les couleurs aient le même "poids" visuel
return hslToHex(hue, 65, 55) }) }
CONVERSION HSL → HEX Le navigateur comprend hsl() mais les designers veulent des hex. Cette fonction fait la conversion. L'algorithme est mathématique : retiens juste que s et l sont en pourcentages divisés par 100.
function hslToHex(h, s, l) { s /= 100 l /= 100a = amplitude de la couleur entre lightness et saturation
const a = s * Math.min(l, 1 - l)
f() calcule le composant RGB pour R (n=0), G (n=8), B (n=4)
const f = n => { const k = (n + h / 30) % 12 const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1)toString(16) convertit en hexadécimal padStart(2, '0') garantit 2 chiffres (ex: 'f' → '0f')
return Math.round(255 * color).toString(16).padStart(2, '0') } return '#' + f(0) + f(8) + f(4) }
COPIE DANS LE PRESSE-PAPIER navigator.clipboard est l'API moderne (HTTPS requis) Le .then/.catch gère le succès et l'échec
function copyToClipboard(hex) { navigator.clipboard.writeText(hex) .then(() => showFeedback('✓ ' + hex + ' copié !')) .catch(() => {Fallback pour les navigateurs anciens
const input = document.createElement('input') input.value = hex document.body.appendChild(input) input.select() document.execCommand('copy') document.body.removeChild(input) showFeedback('✓ Copié !') }) } function showFeedback(msg) { const fb = document.getElementById('feedback') fb.textContent = msgsetTimeout pour faire disparaître le message après 1.5s
setTimeout(() => fb.textContent = '', 1500) }
PIÈGE 1 : Couleurs aléatoires sans système ❌ const color = '#' + Math.floor(Math.random() * 16777215).toString(16) → RGB aléatoire = n'importe quoi. Souvent laid. → Pas de cohérence entre les couleurs.
HSL avec teintes espacées = toujours harmonieux. → Le cerveau humain perçoit les relations entre teintes.
PIÈGE 2 : Saturation et lightness variables ❌ Faire varier S et L en même temps que H → Tu obtiens des couleurs qui n'ont pas le même poids visuel : une très claire, une très sombre, une très vive...
Garde S=65% et L=55% fixes pour toute la palette. → Toutes les couleurs ont la même "force" visuelle.
Modes d'harmonie // Complémentaire : 2 couleurs opposées sur la roue (180°) function complementary(base) { return [hslToHex(base, 65, 55), hslToHex((base + 180) % 360, 65, 55)] }
// Triadique : 3 couleurs espacées de 120° function triadic(base) { return [0, 120, 240].map(offset => hslToHex((base + offset) % 360, 65, 55) ) }
// Analogue : couleurs voisines sur la roue (30°) function analogous(base) { return [-60, -30, 0, 30, 60].map(offset => hslToHex((base + offset + 360) % 360, 65, 55) ) }
Contraste WCAG // Pour savoir si du texte blanc ou noir est lisible // sur une couleur de fond, on calcule la luminance relative function getLuminance(hex) { const rgb = hex.match(/[a-f\d]{2}/gi) .map(x => parseInt(x, 16) / 255) .map(x => x <= 0.03928 ? x/12.92 : Math.pow((x+0.055)/1.055, 2.4)) return 0.2126 * rgb[0] + 0.7152 * rgb[1] + 0.0722 * rgb[2] }
function getContrastRatio(hex1, hex2) { const l1 = getLuminance(hex1) const l2 = getLuminance(hex2) const lighter = Math.max(l1, l2) const darker = Math.min(l1, l2) return (lighter + 0.05) / (darker + 0.05) }
// WCAG AA = ratio >= 4.5 pour texte normal // WCAG AAA = ratio >= 7 pour texte normal function getTextColor(bgHex) { const onWhite = getContrastRatio(bgHex, '#ffffff') const onBlack = getContrastRatio(bgHex, '#000000') return onWhite > onBlack ? '#ffffff' : '#000000' }
ce que tu viens d'apprendre // Ton code vanilla : let palette = generatePalette() function refresh() { palette = generatePalette() render() }
// Devient en React : const [palette, setPalette] = useState(() => generatePalette()) const refresh = () => setPalette(generatePalette())
// generatePalette() est une pure fonction : // elle ne dépend d'aucun état externe. // C'est le pattern idéal pour React : // état minimal, logique pure séparée.
Édite les variables CSS et observe le résultat en temps réel.
- Palette tirée d'une image
JS · Canvas
- Vérificateur de contraste
JS
- Générateur de dégradés
CSS · JS
Chargement des commentaires…