@synapxlab/colorpicker

Sélecteur de couleur à palette fermée — zéro dépendance runtime.
Des couleurs exactes et reproductibles, une popup qui ne sort jamais de l'écran.

  • Palette limitée : chaque couleur est identifiable au premier coup d'œil
  • Popup en position: fixed dans body — jamais coupée par un overflow, un tableau ou une modale
  • Input texte ou hidden + pastille, mode inline, historique partagé
  • Accessible : boutons réels, navigation clavier, focus restitué
  • Port vanilla TypeScript d'evol-colorpicker — sans jQuery
TypeScript Vanilla ESM UMD 7 ko gz MIT
colorpicker.ts
import { ColorPicker, locales } from '@synapxlab/colorpicker'; import '@synapxlab/colorpicker/style'; // Un input hidden + une pastille, comme dans un ERP ColorPicker.init('[role="colorpicker"]', { showOn: 'button', strings: locales.fr, customTheme: ['#f44336', '#ff9800', '#4caf50', '#3f51b5', 'white', 'black'], onChange(color) { row.style.background = color; }, });

Démo interactive

Chaque carte reproduit une situation où un sélecteur classique sortait de l'écran ou se faisait couper. Ici la popup est rendue dans body en position: fixed et reste toujours visible.

1. Input texte, palette complète

Ouverture au focus ou par la pastille. Palette thème, palette web, historique et case transparente.

2. Lignes d'un tableau : input hidden + pastille

La couleur identifie la ligne. Le champ hidden part avec le formulaire, la pastille est le seul contrôle visible. Palette réduite à 9 couleurs.

Dupont FrèresDevis n° 2041
Garage MarinFacture n° 887
Atelier LenoirCommande n° 1290

3. Dans une boîte overflow: hidden

Un widget positionné en absolute serait coupé par la bordure pointillée.

4. Dans une zone scrollable

La popup suit l'ancre pendant le défilement et bascule au-dessus quand la place manque en dessous.

5. Mode inline

Sur un élément qui n'est pas un input, la palette est rendue en place et reste visible.

6. API

Cible : le picker n° 1. Historique partagé entre toutes les instances de la page.

🎯 Pourquoi une palette fermée ?

Un sélecteur à dégradé propose 16 millions de couleurs, et deux utilisateurs ne choisissent jamais la même. Ce composant propose volontairement une palette limitée et fixe : chaque couleur est exacte et reproductible. Une ligne, une étiquette ou un statut garde donc la même teinte partout où il apparaît et se reconnaît d'un coup d'œil. Le jeu de couleurs devient un vocabulaire, pas du bruit.

Couleurs exactes

Toujours #rrggbb en minuscules, tirées d'une liste connue. Pas de « presque rouge ».

Identifiables

Dix colonnes, des nuances contrastées : l'œil retrouve la couleur sans lire sa valeur.

Votre propre nuancier

customTheme remplace la palette par la liste de votre charte, 10 par ligne.

Saisie libre possible

Le champ texte accepte toute couleur CSS valide tapée au clavier, si vous l'autorisez.

📦 Installation

npm install @synapxlab/colorpicker

Ou via CDN (ESM, aucune installation) :

<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@synapxlab/colorpicker/dist/style.css">
<script type="module">
  import { ColorPicker } from 'https://cdn.jsdelivr.net/npm/@synapxlab/colorpicker/dist/colorpicker.es.js';
</script>

⚡ Démarrage rapide

import { ColorPicker, locales } from '@synapxlab/colorpicker';
import '@synapxlab/colorpicker/style';

const cp = new ColorPicker(document.querySelector('#color'), {
  strings: locales.fr,
  transparentColor: true,
  onChange(color) {
    console.log('choisi', color); // '#4f81bd'
  },
});
<!-- Le contrôle visible est la pastille ; la valeur part avec le formulaire -->
<input type="hidden" name="color" role="colorpicker" value="#f44336">

ColorPicker.init('[role="colorpicker"]', {
  showOn: 'button',
  displayIndicator: false,
  customTheme: ['#f44336', '#ff9800', '#ffc107', '#4caf50',
                '#00bcd4', '#3f51b5', '#9c27b0', 'white', 'black'],
});

// Plus tard, au remplissage du formulaire
ColorPicker.get(input)?.setValue(value || 'white');
<link rel="stylesheet" href="node_modules/@synapxlab/colorpicker/dist/style.css">
<script src="node_modules/@synapxlab/colorpicker/dist/colorpicker.umd.cjs"></script>
<script>
  const { ColorPicker } = window.SynapxColorPicker;
  ColorPicker.init('[role="colorpicker"]', { showOn: 'button' });
</script>

📖 Options

new ColorPicker(element, options) — un <input> texte ou hidden ouvre une popup ; tout autre élément reçoit la palette en mode inline.

OptionTypeDéfautDescription
colorstring | nullinput.valueValeur initiale
customThemestring[]Remplace la palette thème par votre liste, 10 par ligne. Les entrées invalides sont écartées
showOn'focus' | 'button' | 'both' | 'manual''both'Ce qui ouvre la popup. Un input hidden passe toujours par la pastille
hideButtonbooleanfalsePas de pastille, l'input seul ouvre
displayIndicatorbooleantrueIndicateurs « couleur courante / survolée » en bas de la popup
transparentColorbooleanfalseAjoute une case transparente hachurée
transparentValuestring'transparent'Valeur stockée pour transparent. evol utilisait '#0000ffff', accepté en compatibilité
historybooleantrueHistorique partagé entre tous les pickers (28 entrées, sans doublon)
initialHistorystring[]Alimente l'historique au départ
defaultPalette'theme' | 'web''theme'Page affichée à l'ouverture
stringsobject | stringlocales.enLibellés : objet partiel, locales.fr, ou la chaîne CSV d'evol. Le français est choisi seul si un ancêtre porte lang="fr"
containerHTMLElementdocument.bodyParent de la popup. Passez un <dialog> ouvert pour rester dans le top-layer
zIndexnumber10000Au-dessus des modales Bootstrap (1055)
offsetnumber4Espace ancre → popup (px)
viewportMarginnumber8Distance minimale aux bords de l'écran (px)
placement'auto' | 'bottom' | 'top''auto'Côté préféré. auto = dessous, ou dessus s'il y a plus de place
onChange, onHover, onOpen, onClosefunctionRappels équivalents aux événements ci-dessous

🔔 Événements & méthodes

Les événements sont des CustomEvent qui remontent depuis l'élément, avec detail.color et detail.instance. Un choix déclenche aussi les événements natifs input puis change sur l'input : formulaires et frameworks réagissent sans code supplémentaire.

input.addEventListener('colorpicker:change', (e) => console.log(e.detail.color));
input.addEventListener('colorpicker:hover',  (e) => preview.style.background = e.detail.color);
input.addEventListener('colorpicker:open',   () => {});
input.addEventListener('colorpicker:close',  () => {});
MéthodeRôle
getValue() / setValue(color, { silent })Lit ou écrit la couleur. silent n'émet aucun événement
show() / hide() / toggle() / isOpen()Pilotage de la popup
enable() / disable() / isDisabled()Verrouillage
clear()Ferme et vide la valeur
destroy()Retire tout le DOM ajouté et les écouteurs, l'input retrouve sa place
ColorPicker.init(selector, options)Instancie sur chaque élément trouvé
ColorPicker.get(element)Instance attachée à un élément
ColorPicker.historyHistorique partagé, lecture seule

📐 Positionnement

La popup n'est jamais dans l'arbre de l'input : elle est ajoutée à body en position: fixed, donc aucun parent overflow, transform ou contain ne peut la couper. Sa position se calcule dans le repère de la fenêtre :

  • par défaut sous l'ancre, alignée à gauche ;
  • pas la place dessous et plus de place dessus → au-dessus ;
  • recalée horizontalement pour rester à viewportMargin des bords ;
  • fenêtre plus petite que la popup → hauteur contrainte, la popup défile en interne ;
  • recalcul au scroll, au redimensionnement et quand la popup change de taille.

Le calcul est une fonction pure exportée, testable sans DOM :

import { computePosition } from '@synapxlab/colorpicker';

computePosition(
  { top: 690, left: 1200, width: 20, height: 20 }, // ancre en bas à droite
  { width: 220, height: 260 },                     // popup
  { width: 1280, height: 720 },                    // fenêtre
);
// → { top: 426, left: 1052, placement: 'top' }
Modales : dans une modale Bootstrap 5, zIndex 10000 et le rendu dans body suffisent. Pour un <dialog> natif, passez-le en container pour que la popup reste dans le top-layer.

🔁 Migration depuis evol-colorpicker

Mêmes palettes, mêmes pages, mêmes options : la migration se résume à retirer jQuery UI et à remplacer l'appel du widget.

$("[role='colorpicker']").colorpicker({
  showOn: "button",
  displayIndicator: false,
  customTheme: ["#f44336", "#ff9800", "white", "black"],
  strings: "Couleur,base,Plus,Moins,Palette,",
});
$(input).colorpicker("val", value);
$(input).on("change.color", (e, color) => …);
ColorPicker.init('[role="colorpicker"]', {
  showOn: 'button',
  displayIndicator: false,
  customTheme: ['#f44336', '#ff9800', 'white', 'black'],
  strings: 'Couleur,base,Plus,Moins,Palette,', // la chaîne CSV reste acceptée
});
ColorPicker.get(input).setValue(value);
input.addEventListener('colorpicker:change', (e) => …);
evol-colorpicker@synapxlab/colorpicker
.colorpicker(options)new ColorPicker(el, options) ou ColorPicker.init(sel, options)
.colorpicker("val", c)setValue(c)
.colorpicker("showPalette") / "hidePalette"show() / hide()
"enable" / "disable" / "clear" / "destroy"mêmes noms de méthodes
change.color / mouseover.colorcolorpicker:change / colorpicker:hover
transparent = '#0000ffff''transparent', ou transparentValue: '#0000ffff'
classes .evo-*classes .sxcp-*, variables --sxcp-*