Sélecteur de fichiers multimédias enrichis (sélection personnalisable)

Fonctionnalités Web : <select> personnalisable

L'élément <select> natif était historiquement difficile à styliser et ne pouvait contenir que des options de texte brut. La propriété appearance: base-select offre un moyen déclaratif, basé uniquement sur CSS, d'activer un état personnalisable pour l'élément <select>. Cela permet aux développeurs d'inclure du contenu HTML enrichi (images, SVG et mises en page complexes) dans les éléments <option>, tout en conservant l'accessibilité native au clavier et l'intégration des formulaires. Utilisez ce modèle pour remplacer les composants de sélection lourds et personnalisés par des éléments natifs standards.

Implémentation

Pour implémenter un sélecteur de fichiers multimédias enrichi à l'aide de l'API Customizable Select :

  1. Activer les styles de base : appliquez appearance: base-select à l'élément <select> et à son sélecteur interne à l'aide du pseudo-élément ::picker(select). Cela modifie l'analyseur HTML du navigateur pour le contenu de <select>.
  2. Définir le contenu du bouton : utilisez des éléments <button> et <selectedcontent> standards dans <select> pour définir ce qui s'affiche lorsque le sélecteur est fermé. L'élément <selectedcontent> reflète automatiquement le contenu de l'option sélectionnée. Cette option est obligatoire si vous souhaitez afficher le contenu enrichi actuellement sélectionné dans le bouton.
  3. Utiliser du contenu enrichi dans les options : vous pouvez désormais placer des images, des SVG et d'autres balises HTML dans les balises <option>. Avant cette API, le navigateur supprimait les balises des balises <option> et n'affichait que du texte brut.
  4. Appliquer un style au popover : appliquez un style à la liste d'options déroulante en ciblant le pseudo-élément ::picker(select). Il s'affiche dans la couche supérieure, ce qui signifie que vous n'avez pas à vous battre avec z-index. Les options sont positionnées de manière native à l'aide de l'API Anchor Positioning.

Exemple de code : sélecteur de rôle enrichi

<label for="role-picker">Select your role</label>
<select class="custom-select" name="role" id="role-picker">
  <button>
    <selectedcontent></selectedcontent> <!-- Mirrors the selected option's content automatically so you do not need JS to update the button -->
  </button>

  <!-- Define concise aria-label values on options whose mirrored rich content would otherwise read awkwardly as a concatenated string -->
  <option value="frontend" aria-label="Frontend Developer">
    <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
      <rect x="2" y="3" width="20" height="14" rx="2" ry="2"></rect>
    </svg>
    <div class="option-text">
      <span class="option-title">Frontend Developer</span>
      <span class="option-desc">React, Vue, CSS</span>
    </div>
  </option>

  <option value="backend" aria-label="Backend Developer">
    <svg aria-hidden="true" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor">
      <rect x="2" y="12" width="20" height="14" rx="2" ry="2"></rect>
    </svg>
    <div class="option-text">
      <span class="option-title">Backend Developer</span>
      <span class="option-desc">Node.js, Python</span>
    </div>
  </option>
</select>
select.custom-select,
select.custom-select::picker(select) {
  appearance: base-select; /* MUST opt-in both the select and its picker to enable the customizable state, otherwise browser standard rendering applies */
}

select.custom-select {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding: 12px;
  background-color: #1e293b;
  border: 1px solid #334155;
  border-radius: 8px;
  color: #f1f5f9;
  cursor: pointer;
}

select.custom-select::picker(select) {
  background-color: #0f172a;
  border: 1px solid #334155;
  border-radius: 8px;
  padding: 8px;
  box-shadow: 0 10px 25px -5px rgba(0, 0, 0, 0.5);
  width: anchor-size(width); /* Uses Anchor Positioning API to keep the dropdown precisely aligned to the button trigger width */
}

select.custom-select option {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px;
  border-radius: 4px;
  cursor: pointer;
}

select.custom-select option:hover {
  background-color: #1e293b;
}

/* Remove standard OS checkmark for base-select */
select.custom-select option::before {
  display: none;
}

/* MANDATORY: Provide multiple visual indicators (e.g., prominent background color and bold title font) to communicate the checked state cleanly */
select.custom-select option:checked {
  background-color: #3b82f6;
  color: #ffffff;
}
select.custom-select option:checked .option-title {
  font-weight: 700;
}

Implémentation stratégique et bonnes pratiques

  • À FAIRE : Utilisez appearance: base-select lorsque vous avez besoin de mises en page d'options complexes (icônes, descriptions, images) qui n'étaient auparavant possibles qu'avec des frameworks d'UI JavaScript lourds.
  • N'UTILISEZ PAS d'éléments ad hoc si vous constatez des ralentissements des performances. Le navigateur gère le focus clavier natif de manière native.
  • À FAIRE : tenez compte du rendu de la couche supérieure. Le sélecteur s'affiche dans la couche supérieure, ce qui signifie qu'il remplace le z-index relatif du contenu de la page.
  • À FAIRE : Masquez les détails secondaires (comme les descriptions) dans l'état du bouton s'ils prennent trop de place, en appliquant un style à .custom-select selectedcontent .option-desc { display: none; }.
  • Tester le comportement de la mise en page La définition de appearance: base-select supprime le comportement par défaut du navigateur, qui consiste à dimensionner l'élément select en fonction de la largeur de l'option la plus longue. Vous devrez peut-être définir une largeur fixe ou utiliser des contraintes flex/grid pour éviter les décalages de mise en page.
  • Assurez-vous que votre <select> comporte un attribut name et un <label> associé. Cela garantit que, même avec une UI personnalisée, le composant reste accessible aux lecteurs d'écran et fonctionne correctement avec les envois de formulaires standards.
  • Utilisez les attributs value standards sur les options pour vous assurer que l'envoi du formulaire fonctionne exactement comme avant.

Stratégies de remplacement

Solutions de remplacement et compatibilité des navigateurs pour l'élément <select> personnalisable

Pour les navigateurs qui ne sont pas encore compatibles avec appearance: base-select, l'élément <select> se dégrade de manière fluide en un menu déroulant standard du système d'exploitation.

  • Contenu non textuel ignoré : les anciens navigateurs suppriment les balises HTML (comme <svg> ou <div>) à l'intérieur des balises <option> et n'affichent que les nœuds de texte. Assurez-vous que le contenu textuel de <option> est lisible et compréhensible en lui-même.
  • Gestion de la structure HTML : les analyseurs standards peuvent ignorer les balises <button> et <selectedcontent> dans <select> ou les traiter comme non valides. Aucun polyfill JavaScript lourd n'est strictement requis pour l'amélioration progressive si vous considérez le texte standard comme une solution de remplacement lisible.
document.addEventListener("DOMContentLoaded", () => {
  // Check if browser supports base-select value
  if (!CSS.supports("appearance", "base-select")) {
    // Custom select overrides are not supported natively.
  }
});