Aller au contenu
← 1001 Widgets
N° 003 · ●○○

Générateur de Palettes

Génère 5 couleurs harmonieuses. Hover pour voir le hex, click pour copier.

HTMLCSSJS
Ce qu'on apprend
  • Penser la couleur en HSL
  • Convertir HSL en hexadécimal
  • Copier dans le presse-papier avec l'API Clipboard
Pourquoi ce langage

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.

Comment ça marche
  1. 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)
  2. 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) % 360
  3. On garde saturation et lightness fixes pour que toutes les couleurs aient le même "poids" visuel

    return hslToHex(hue, 65, 55)
    })
    }
  4. 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 /= 100
  5. a = amplitude de la couleur entre lightness et saturation

    const a = s * Math.min(l, 1 - l)
  6. 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)
  7. 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)
    }
  8. 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(() => {
  9. 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 = msg
  10. setTimeout pour faire disparaître le message après 1.5s

    setTimeout(() => fb.textContent = '', 1500)
    }
Pièges & optimisations

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.

Fix

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...

Fix

Garde S=65% et L=55% fixes pour toute la palette. → Toutes les couleurs ont la même "force" visuelle.

Optimisation 1

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) ) }

Optimisation 2

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' }

→ React

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.

Personnaliser

Édite les variables CSS et observe le résultat en temps réel.

Variantes
Commentaires
0/1000

Chargement des commentaires…