Les chips paraissent inoffensives jusqu’au jour où elles mangent votre mise en page sur un petit écran, cachent le bouton « Appliquer » et transforment votre tableau de bord analytique en scène de crime à défilement horizontal. Alors vous recevez un ticket : « Filtres cassés sur mobile. » C’est toujours « cassé », jamais « légèrement sous-optimal ».
Ceci est le guide orienté production pour les chips et les barres de filtres : comment choisir entre wrap et scroll, comment gérer l’overflow sans tromper les utilisateurs, et comment implémenter des états sélectionnés qui n’accumulent pas de dette d’accessibilité.
Ce que nous construisons réellement (chips, tags, barres de filtres)
Chips (ou tags) sont des éléments d’interface compacts représentant une catégorie, un attribut ou un filtre choisi. Ils ont généralement un label, parfois une icône, parfois un « x » pour supprimer, et presque toujours une opinion sur le padding.
Barres de filtres sont des conteneurs qui hébergent des chips (et souvent d’autres contrôles) pour permettre aux utilisateurs d’affiner un jeu de données. Si vous construisez un logiciel B2B, votre barre de filtres est un mini système d’exploitation : elle doit survivre à des labels longs, des dizaines de sélections, la localisation et l’utilisateur qui met le zoom du navigateur à 200 % parce qu’il tient à ses yeux.
Le contrat caché
Les chips ne sont pas que des décorations. Elles forment un contrat :
- État : sélectionné vs non sélectionné doit être sans ambiguïté.
- Capacité : le conteneur doit gérer N chips gracieusement, où N n’est jamais le nombre que vous avez testé.
- Contrôle : les utilisateurs doivent pouvoir ajouter/supprimer des sélections sans perdre le contexte.
- Stabilité : la barre ne doit pas se reflow de façon à provoquer des « sauts » de page et des clics manqués.
Base d’opinion
Si vous construisez une barre de filtres pour un produit dense (analytique, billetterie, inventaire, sécurité), par défaut :
- Wrap sur desktop lorsque la barre se trouve au-dessus du contenu et que l’espace vertical est peu coûteux.
- Défilement horizontal sur une seule ligne sur mobile avec des affordances visibles et un contrôle de secours « Plus de filtres ».
- Résumé collapsé quand les sélections deviennent volumineuses (« Filtres (12) ») plus un panneau dédié pour éditer.
Faits et historiques utiles pour argumenter
- Fait 1 : les patterns UI « chip » se sont popularisés dans les design systems au milieu des années 2010 comme tokens compacts, optimisés pour le tactile — à moitié bouton, à moitié label.
- Fait 2 : le défilement horizontal existait bien avant le mobile ; il était simplement mal vu parce que les molette de souris et les trackpads n’étaient pas uniformes.
- Fait 3 : le responsive initial favorisait le wrapping car les patterns CSS d’overflow étaient rugueux et le momentum scroll mobile n’était pas stable partout.
- Fait 4 : la montée des headers sticky et des webviews in-app a poussé les barres de filtres vers des surfaces « toujours présentes » — excellent pour l’UX, dangereux pour la mise en page.
- Fait 5 : les « pills » (tags arrondis) existent depuis les toolkits desktop ; la forme n’est pas nouvelle, c’est la densité d’usage qui l’est.
- Fait 6 : la troncature avec ellipse est devenue courante en partie parce que les polices à largeur variable et la localisation rendent les labels à largeur fixe irréalistes.
- Fait 7 : les patterns d’accessibilité pour les « groupes de boutons bascule » ont mûri avec l’évolution des rôles ARIA ; les chips se comportent souvent comme des toggles, pas comme des liens.
- Fait 8 : avec les apps rendues côté client, l’état des filtres s’est lié aux URL ; les chips sont devenues des artefacts de navigation autant que visuels.
- Fait 9 : les chips sont devenues des événements d’analytics (« filtre appliqué »), ce qui signifie que votre bug UI peut devenir un bug de données. Voilà comment les dirigeants crient sur « la santé du pipeline ».
Citation (idée paraphrasée) : « L’espoir n’est pas une stratégie. » — souvent attribué aux responsables fiabilité/ops ; traitez cela comme un état d’esprit, pas une citation littérale.
Un fait de plus qu’on ne met pas sur une diapo : le nombre de chips est corrélé au chaos organisationnel. Quand les équipes ne se mettent pas d’accord sur la taxonomie, elles ajoutent un filtre. C’est la version UI d’ajouter des disques parce que quelqu’un a oublié de supprimer des logs.
Prendre les décisions difficiles : wrap vs scroll vs collapse
La gestion du débordement n’est pas un choix CSS. C’est une décision produit avec une implémentation CSS. Décidez du mode d’échec que vous préférez quand le nombre de chips explose.
Option A : Wrap (multi-lignes)
Mieux quand : les chips sont secondaires, l’espace vertical est disponible et vous voulez la visibilité complète des labels sans gestes.
Coûts : saut de contenu, shift de mise en page et « où est passée la table ? » lorsque l’utilisateur ajoute la 9e chip.
Réalité opérationnelle : le wrap interagit avec les headers sticky. Si la barre s’agrandit en étant sticky, elle peut couvrir le contenu ou le pousser bizarrement. Vous aurez des bugs « impossible de cliquer la première ligne ». Ils auront raison.
Option B : Une seule ligne défilable (horizontal)
Mieux quand : l’espace vertical est limité (mobile), les chips sont fréquemment utilisés et vous pouvez montrer une affordance de défilement claire.
Coûts : découvrabilité et complexité de navigation au clavier. Aussi : il est facile de créer un piège de défilement involontaire.
Règle : si vous choisissez le scroll, vous devez fournir un indice visuel qu’il y a plus de contenu (masque dégradé, chip partiellement coupée, boutons flèche ou menu « Plus »).
Option C : Collapse (résumé + panneau)
Mieux quand : les sélections peuvent devenir nombreuses, les filtres sont complexes et vous avez besoin d’une hauteur de layout déterministe.
Coûts : un clic de plus, et vous devez concevoir un panneau qui ne ressemble pas à une punition.
Règle : collapse n’est pas « cacher les sélections ». Affichez un résumé : le compte, et peut-être 1–2 chips en aperçu.
Ma hiérarchie préférée
- Desktop : wrap jusqu’à 2 lignes, puis basculer en « +N de plus » ou un menu overflow.
- Mobile : défilement sur une seule ligne + bouton « Filtres » ouvrant un panneau plein écran.
- Partout : ne laissez jamais les contrôles « Appliquer/Effacer » être poussés hors écran.
Blague #1 : le défilement horizontal, c’est comme une dette : parfois nécessaire, toujours avec un plan de remboursement.
Patterns de gestion du débordement (et ce qui casse)
Pattern 1 : Wrap avec clamp de lignes pour les labels
Faire wrap des chips sur plusieurs lignes est simple jusqu’à ce que les labels deviennent trop longs. La bonne approche est généralement de troncer les labels à l’intérieur de la chip, pas les chips elles‑mêmes. Les utilisateurs voient toujours des tokens distincts ; ils n’obtiennent le label complet que via un tooltip ou un long-press.
Mode d’échec : des chaînes localisées longues créent des chips géantes qui dominent la ligne, forçant tout le reste sur la ligne suivante. Ce n’est pas du « responsive » ; c’est votre UI qui se fait dominer.
Pattern 2 : Wrap avec lignes max + indicateur d’overflow
Laissez le conteneur wrap, mais plafonnez-le à (par exemple) deux lignes. Puis affichez une chip ou un bouton « +N de plus ». Cela maintient la stabilité du layout tout en préservant la lisibilité.
Mode d’échec : implémenter « +N de plus » en mesurant la largeur du DOM à chaque redimensionnement et à chaque mise à jour de chip peut thrash le layout. Si vous le faites, débouchez-le (debounce) et ne l’exécutez pas à chaque frappe dans un champ de recherche.
Pattern 3 : Conteneur scrollable avec momentum
Pour le mobile, une seule ligne avec overflow-x auto est la norme. Mais « norme » n’est pas synonyme de « sûr ». Vous devez assurer :
- le défilement n’empêche pas le défilement vertical de la page (touch-action importe)
- le focus clavier reste visible (scrollIntoView lors du tabbing)
- il existe un indice que le contenu déborde
Mode d’échec : une région de scroll imbriquée dans un header sticky peut donner l’impression d’une page cassée. Les utilisateurs tentent de scroller la page ; la ligne de chips vole le geste.
Pattern 4 : Menu d’overflow (« Plus »)
Quand les chips représentent beaucoup d’options, envisagez un contrôle « Plus » qui ouvre un menu ou un tiroir listant le reste. C’est effectivement une stratégie de virtualisation pour votre UI : gardez le flux principal rapide, déplacez les cas marginaux hors du chemin critique.
Mode d’échec : si « Plus » contient des chips sélectionnées mais que la barre principale n’indique pas qu’elles sont sélectionnées, vous créez un état invisible. L’état invisible crée des tickets.
Pattern 5 : Switch responsive (wrap → scroll)
Changer de comportement selon un breakpoint est acceptable, mais évitez les surprises de « même balisage, physique différente ». Une ligne de chips qui wrap sur desktop mais scroll sur mobile doit préserver l’état de sélection, la sémantique clavier et l’ordre de focus. Sinon, on a l’impression de deux composants collés l’un à l’autre.
Ne comptez pas sur « ça ne déborderera probablement pas »
Supposer que le débordement n’arrivera pas, c’est l’équivalent UI de supposer que les disques ne se rempliront pas. Vous aurez tort, et la personne ouvrant le ticket joindra une capture d’écran avec 47 chips étiquetées « Enterprise – North America – West – Secondary ».
États sélectionnés : UX, accessibilité et « pourquoi cette chip est bleue ? »
Définissez le type de chip avant de le styliser
« Chip » est une forme. Le comportement importe davantage. Comportements courants :
- Chips toggle : sélectionné/désélectionné ; le clic bascule l’état.
- Chips action : exécutent une action (ajouter une ligne de filtre, ouvrir une modale).
- Chips input : représentent une saisie utilisateur (comme des destinataires sélectionnés) et sont supprimables.
- Chips navigation : se comportent comme des onglets/liens (attention : ne pas confondre avec des toggles).
L’état sélectionné doit correspondre au comportement. Ne stylisez pas une chip de navigation comme une chip toggle. Les utilisateurs apprennent des patterns. Et ils vous puniront quand vous les briserez.
Sélectionné ≠ actif ≠ focus
Trois états qui se confondent souvent :
- Sélectionné : fait partie de l’ensemble de filtres courant.
- Actif/pressé : en train d’être cliqué/touché (transitoire).
- Focus : focus clavier (doit être visible même quand sélectionné).
Ne réutilisez pas le même traitement visuel pour sélectionné et focus. C’est ainsi que les utilisateurs clavier se perdent, et que QA ouvre des tickets « clavier cassé » difficiles à reproduire à moins d’utiliser réellement un clavier. Ce que vous devriez faire.
La couleur ne suffit pas
L’état sélectionné doit être percevable sans s’appuyer uniquement sur la couleur. Options pratiques :
- icône coche (avec nom accessible)
- variation de graisse de police (subtile mais utile)
- changement de bordure
- changement de forme (moins courant ; peut provoquer un shift de layout)
Aussi : gardez un contraste raisonnable. Une « bleu clair sur bleu un peu plus clair » a l’air moderne et échoue en conditions réelles.
La suppression (« x ») est une cible distincte
Pour les chips input supprimables, le bouton de suppression doit être un élément interactif distinct. Sinon vous créez un comportement ambigu : avez-vous sélectionné la chip ou l’avez-vous supprimée ? Les deux sont de mauvaises surprises.
Blague #2 : Si votre état sélectionné dépend d’une bordure d’1px, félicitations — vous avez construit le filtre de Schrödinger.
Réalités mobiles : pouces, barres sticky et zones sûres
Barres de filtres sticky : traitez-les comme de l’infrastructure
Les headers sticky sont des UI persistantes. L’UI persistante doit être ennuyeusement correcte. Si la barre de chips est sticky :
- limitez sa hauteur maximale (surtout en mode wrap)
- évitez le redimensionnement dynamique pendant le scroll
- réservez de l’espace pour que le contenu ne soit pas couvert
Une barre de chips sticky qui grandit est comme un fichier log sans rotation : elle finira par tout consommer.
Tailles de cible et espacements
Les chips nécessitent une taille de toucher adéquate. Mais n’augmentez pas bêtement le padding ; cela augmente la hauteur de ligne et aggrave l’overflow. Meilleures approches :
- gardez le corps de la chip confortable, mais déplacez les interactions denses dans un panneau
- utilisez un seul contrôle « Filtres » qui ouvre une feuille dédiée pour la sélection complexe
- limitez les labels des chips à ce qui est nécessaire ; affichez le détail complet dans le panneau
Zones sûres et encoches
Si votre barre de filtres est proche du bas (fréquent sur mobile), respectez les insets de safe area. Sinon « Effacer » vivra sous l’indicateur home. Les utilisateurs le découvriront principalement en jurant.
Quand basculer vers un panneau
Ma règle : si un utilisateur peut raisonnablement sélectionner plus d’environ 8 chips dans un flux typique, la barre principale devrait devenir un résumé et une passerelle vers un panneau. Laissez les chips de la barre agir comme des bascules rapides pour les filtres fréquents ; poussez la longue traîne dans le panneau.
Accessibilité et comportement clavier qui ne vous feront pas honte
Choisir la sémantique délibérément
Ne laissez pas votre bibliothèque de composants décider de la sémantique par accident. Mappings courants :
- Chips toggle : boutons avec
aria-pressed="true|false"(ou sémantique checkbox si c’est clairement une liste). - Groupe de chips single-select : radiogroup + boutons radio peuvent convenir, mais seulement si le comportement est véritablement radio.
- Chips de navigation : liens réels ou onglets, selon le comportement d’échange de contenu.
Quoi que vous choisissiez, gardez les styles de focus visibles et cohérents entre états sélectionné/non sélectionné.
Lignes de chips scrollables et visibilité du focus
Si les chips se trouvent dans une région à défilement horizontal, les utilisateurs clavier doivent toujours voir la chip focusée. Cela signifie que votre JS doit scroller la chip focusée dans la vue quand le focus change.
Et oui, c’est un problème de production. Un indicateur de focus caché est comme un log d’erreur caché : il existe, mais n’aide personne.
Annoncer les changements
Quand la sélection d’une chip change les résultats, les utilisateurs d’un lecteur d’écran bénéficient d’une annonce (« Résultats mis à jour »). Restez bref. Ne racontez pas tout votre plan de requête.
Ne pas piéger le scroll
Les régions de défilement imbriquées peuvent emprisonner le tactile et le clavier. Si vous avez une ligne de chips scrollable à l’intérieur d’une page scrollable, vérifiez :
- les gestes tactiles peuvent toujours scroller la page
- les flèches se comportent de façon prévisible dans le groupe de chips
- Tab avance logiquement et ne reste pas bloqué en boucle
Performance et fiabilité : les chips comme surface de production
Pourquoi les SRE devraient s’en soucier
Les barres de filtres ressemblent à du fluff frontend jusqu’à ce que vous les connectiez à un backend vivant et au comportement réel des utilisateurs :
- Chaque bascule peut déclencher un appel API.
- Chaque état sélectionné peut être sérialisé dans une URL.
- Chaque label de chip peut provenir de contenu généré par l’utilisateur.
Votre barre de chips devient alors en partie UI, en partie constructeur de requêtes, en partie générateur de clefs de cache. Si elle dysfonctionne, ce n’est pas « cosmétique ». C’est charge, latence et parfois panne.
Debounce et batch des modifications
Pour les filtres multi‑sélection, évitez de lancer une requête à chaque bascule si les utilisateurs sélectionnent plusieurs éléments à la suite. Options :
- bouton Appliquer explicite
- auto-apply débouncé (mais assurez l’annulation)
- mises à jour par lot (appliquer quand l’utilisateur fait une pause)
Du point de vue ops : le batching réduit les tempêtes de requêtes et stabilise le comportement du cache.
Surveillez vos clefs de cache
Les chips mappent souvent à des paramètres de requête. Si l’ordre est incohérent (ex. tag=a&tag=b vs tag=b&tag=a), les caches les traitent comme distincts. Triez les sélections avant de générer les URLs et les payloads.
La virtualisation ne concerne pas que les listes
Si vous avez des centaines de chips possibles (fréquent en e‑commerce ou recherche de logs), ne les rendez pas toutes dans une liste horizontale. Rendez un petit sous‑ensemble et déplacez le reste dans un panneau consultable. Cela réduit la taille du DOM et rend le layout plus prévisible.
Trois mini-histoires d’entreprise issues du terrain
Mini-histoire 1 : L’incident causé par une mauvaise hypothèse
L’équipe produit a ajouté des « filtres rapides » sur une page de reporting. De jolies chips en haut : Région, Segment, Statut. C’était livré vite parce que l’ensemble de données était petit en staging et tout le monde testait avec des labels en anglais.
Puis un grand client entreprise a activé un jeu d’étiquettes personnalisées tirées de leur taxonomie interne. Soudain la liste de chips est passée de 8 éléments à « aussi nombreuses que leur organigramme ». Les labels étaient longs, localisés et contenaient de la ponctuation. La barre a wrap sur cinq lignes, a poussé la table vers le bas et le header sticky a commencé à couvrir les premières lignes.
Des tickets support ont décrit des « données manquantes » parce que les premières lignes étaient littéralement cachées derrière la barre de filtres sticky étendue. Les utilisateurs ne réalisaient pas qu’ils pouvaient scroller ; l’UI avait l’air de s’arrêter au header. L’incident n’était pas une panne, mais un événement de confiance. Pire : personne ne vous fait pager, mais les clients s’en souviennent.
L’hypothèse erronée était simple : « les filtres ne dépasseront pas une ligne. » La correction fut tout aussi simple mais demanda de la discipline : plafonner la barre à deux lignes, ajouter « +N de plus » et déplacer les filtres de longue traîne dans un panneau. Et arrêter de laisser les conteneurs sticky grandir automatiquement. Sticky veut dire stable.
Mini-histoire 2 : L’optimisation qui a mal tourné
Une équipe frontend a remarqué que la mesure du layout coûtait cher au redimensionnement. Ils ont implémenté un algorithme malin d’overflow de chips : mesurer les largeurs des chips, en empaqueter autant que possible et cacher le reste derrière une chip « Plus ». Ça fonctionnait bien sur leurs machines.
Sur des appareils bas de gamme et certaines webviews embarquées, la mesure tournait en boucle pendant le scroll parce que le layout se recalculait suite au chargement de polices et aux changements dynamiques du viewport. La barre de chips est devenue une petite attaque par déni de service contre le thread principal. Le scroll saccadait. La latence d’entrée a explosé. Les utilisateurs décrivaient « la page se fige quand j’essaye de filtrer ».
En monitoring production, la latence backend semblait correcte. Le thread UI était le goulot. L’optimisation a échoué parce qu’elle considérait la mesure du DOM comme déterministe et bon marché. Ce n’est pas le cas. Surtout pas lors des moments bizarres : changement d’orientation, swap de police, zoom et ouverture/fermeture du clavier.
La correction finale fut ennuyeuse : supprimer les mesures par frame, autoriser un pattern plus simple wrap-with-max-lines, et ne recomputer l’overflow que sur des événements explicites (liste de filtres modifiée, breakpoint traversé) avec debounce. Ils ont aussi ajouté une entrée manuelle « Filtres » pour le panneau. La performance s’est améliorée et le composant est devenu plus simple. Leçon : « malin » et « fiable » partagent rarement un meeting.
Mini-histoire 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une autre équipe avait une habitude : chaque composant UI susceptible d’overflow est livré avec un « mode torture » en staging. C’est juste un toggle qui injecte des labels absurdes et 50+ chips. Personne n’aime ça, mais tout le monde l’utilise juste avant la mise en production.
Pendant une refonte, un développeur a remplacé le conteneur de filtres de CSS grid à flex et a supprimé min-width: 0 d’un enfant car cela « semblait inutile ». Avec des données normales, rien ne cassait.
Le mode torture a immédiatement montré la régression : les labels longs refusaient de se réduire, débordaient du conteneur et créaient une barre de défilement horizontale sur toute la page. Ce type de bug est coûteux s’il atteint la production car il apparaît seulement sur certains contenus et largeurs de viewport.
Ils ont restauré la contrainte manquante, ajouté un test de régression par capture d’écran pour les breakpoints courants et sont passés à autre chose. Pas d’incident. Pas de drama. Juste la satisfaction discrète d’un système qui se comporte. L’ennui est une qualité. En ops et UI, l’ennui est une fonctionnalité.
Tâches pratiques : 12+ commandes pour diagnostiquer le goulot
Voici des tâches opérationnelles réelles à exécuter quand une barre de chips « semble cassée » en production. L’objectif n’est pas de deviner. L’objectif est de localiser le goulot : CSS/layout, runtime JS, réseau/API ou explosion des requêtes backend.
Tâche 1 : Vérifier les accès Nginx pour les toggles de filtre
cr0x@server:~$ sudo tail -n 20 /var/log/nginx/access.log
10.0.12.34 - - [29/Dec/2025:14:01:12 +0000] "GET /api/search?tag=region%3Ana&tag=status%3Aopen HTTP/2.0" 200 8123 "-" "Mozilla/5.0"
10.0.12.34 - - [29/Dec/2025:14:01:13 +0000] "GET /api/search?tag=region%3Ana&tag=status%3Aopen&tag=segment%3Aenterprise HTTP/2.0" 200 8451 "-" "Mozilla/5.0"
Ce que cela signifie : chaque bascule de chip déclenche une requête. Si vous voyez plusieurs requêtes séquentielles par interaction utilisateur, votre UI peut appliquer automatiquement trop agressivement.
Décision : ajouter du batching (bouton Appliquer) ou du debounce ; normaliser/trier les paramètres tag pour que les caches fonctionnent.
Tâche 2 : Compter le taux de requêtes par endpoint pour repérer les tempêtes
cr0x@server:~$ sudo awk '{print $7}' /var/log/nginx/access.log | cut -d'?' -f1 | sort | uniq -c | sort -nr | head
8421 /api/search
1190 /api/filters
402 /static/app.js
Ce que cela signifie : /api/search domine. Si cela a bondi après un changement UI, la barre de filtres a probablement augmenté le churn de requêtes.
Décision : vérifier le comportement UI (auto-apply) et implémenter du caching ou du rate limiting si nécessaire.
Tâche 3 : Inspecter la cardinalité de la query string (tueur de cache)
cr0x@server:~$ sudo grep -o 'GET /api/search?[^ ]*' /var/log/nginx/access.log | head -n 5
GET /api/search?tag=region%3Ana&tag=status%3Aopen
GET /api/search?tag=status%3Aopen&tag=region%3Ana
GET /api/search?tag=region%3Ana&tag=status%3Aopen&sort=desc
GET /api/search?sort=desc&tag=region%3Ana&tag=status%3Aopen
GET /api/search?tag=region%3Ana&tag=status%3Aopen&tag=segment%3Aenterprise
Ce que cela signifie : l’ordre des paramètres varie, produisant plusieurs clefs de cache pour la même requête logique.
Décision : canonicaliser l’ordre des paramètres côté client ; éventuellement canonicaliser côté serveur avec des redirections GET.
Tâche 4 : Vérifier le ratio HIT/MISS du CDN/cache (si présent)
cr0x@server:~$ sudo grep -E 'HIT|MISS' /var/log/nginx/access.log | tail -n 10
10.0.10.9 - - [29/Dec/2025:14:02:11 +0000] "GET /api/search?tag=region%3Ana&tag=status%3Aopen HTTP/2.0" 200 8123 "-" "Mozilla/5.0" cache=MISS
10.0.10.9 - - [29/Dec/2025:14:02:12 +0000] "GET /api/search?tag=status%3Aopen&tag=region%3Ana HTTP/2.0" 200 8123 "-" "Mozilla/5.0" cache=MISS
Ce que cela signifie : des MISS sur des requêtes logiquement identiques indiquent généralement des params non canonicalisés ou un TTL bas.
Décision : canonicaliser et envisager de mettre en cache les résultats de recherche pour de courtes fenêtres si acceptable.
Tâche 5 : Mesurer la distribution de latence backend (pas seulement la moyenne)
cr0x@server:~$ sudo awk '{print $NF}' /var/log/nginx/access.log | tail -n 5
rt=0.091
rt=0.104
rt=1.902
rt=0.088
rt=0.095
Ce que cela signifie : vous avez de la latence en queue (1.9s) même si la majorité des requêtes est à ~100ms. L’UI de chips rend la latence tail très visible car les utilisateurs la martèlent.
Décision : optimiser les requêtes pires cas et envisager des mises à jour optimistes ou des états de chargement qui n’entrainent pas de thrash de layout.
Tâche 6 : Identifier les requêtes postgres lentes (exemple)
cr0x@server:~$ sudo -u postgres psql -c "select calls, mean_exec_time, query from pg_stat_statements order by mean_exec_time desc limit 5;"
calls | mean_exec_time | query
-------+----------------+------------------------------------------------
412 | 988.123 | select * from events where tags @> $1 ...
1022 | 212.044 | select distinct tag from events_tags where ...
Ce que cela signifie : les filtres pilotés par chips se traduisent souvent par des requêtes sur les tags ; les patterns les plus lents apparaissent ici.
Décision : ajouter des index (GIN pour JSONB/array tags), réécrire les requêtes ou contraindre les filtres coûteux derrière un « Appliquer ».
Tâche 7 : Vérifier la saturation CPU (les symptômes côté client peuvent aussi venir du serveur)
cr0x@server:~$ top -b -n 1 | head -n 12
top - 14:05:21 up 12 days, 3:18, 1 user, load average: 5.21, 4.98, 4.10
Tasks: 212 total, 2 running, 210 sleeping, 0 stopped, 0 zombie
%Cpu(s): 86.2 us, 3.1 sy, 0.0 ni, 10.3 id, 0.0 wa, 0.0 hi, 0.4 si, 0.0 st
MiB Mem : 32123.8 total, 1120.4 free, 18002.3 used, 13001.1 buff/cache
Ce que cela signifie : CPU élevé ; si cela coïncide avec l’usage des filtres, votre travail backend par bascule est trop cher.
Décision : réduire la fréquence des requêtes et/ou optimiser la stratégie d’index/requête.
Tâche 8 : Vérifier rapidement le p95 avec systemd journal (service exemple)
cr0x@server:~$ sudo journalctl -u search-api --since "30 min ago" | grep "request_time=" | tail -n 5
request_time=0.112 path=/api/search
request_time=0.138 path=/api/search
request_time=1.744 path=/api/search
request_time=0.121 path=/api/search
request_time=0.109 path=/api/search
Ce que cela signifie : vous voyez des pics ; corrélez-les avec des tags ou combinaisons spécifiques.
Décision : ajouter des métriques de requête au niveau des ensembles de filtres normalisés.
Tâche 9 : Confirmer gzip/brotli pour les payloads lourds de filtres
cr0x@server:~$ curl -sI -H "Accept-Encoding: gzip" https://app.example.internal/api/filters | grep -i -E "content-encoding|content-length"
Content-Encoding: gzip
Content-Length: 18234
Ce que cela signifie : les définitions de filtres peuvent être lourdes (labels, comptes, métadonnées). La compression compte.
Décision : assurer que la compression est activée ; réduire les champs du payload pour la barre de chips vs le panneau complet.
Tâche 10 : Valider que le bundle frontend n’a pas gonflé
cr0x@server:~$ ls -lh /srv/www/static/ | grep app
-rw-r--r-- 1 www-data www-data 1.9M Dec 29 13:40 app.js
-rw-r--r-- 1 www-data www-data 255K Dec 29 13:40 app.css
Ce que cela signifie : si votre composant de chips a importé une dépendance massive (icônes, libs de mesure), la taille du bundle peut nuire à la latence d’interaction.
Décision : tree-shake, code-split le panneau de filtres et gardez la barre légère.
Tâche 11 : Repérer les plaintes de layout shift via les logs d’erreur frontend
cr0x@server:~$ sudo tail -n 20 /var/log/frontend-errors.log
2025-12-29T14:03:09Z WARN ui layout_shift chipbar_resize loops=34 route=/reports
2025-12-29T14:03:10Z WARN ui long_task 247ms route=/reports action=toggle_chip
Ce que cela signifie : redimensionnements répétés de la barre de chips et longues tâches indiquent du thrash côté client.
Décision : supprimer les mesures par frame ; réduire les reflows ; plafonner la hauteur ; éviter d’animer width/height.
Tâche 12 : Vérifier que « Effacer les filtres » est toujours accessible (check synthétique)
cr0x@server:~$ node /opt/synthetics/check-filterbar.js
PASS route=/reports viewport=390x844 clear_button_visible=true chips_overflow=true
PASS route=/reports viewport=768x1024 clear_button_visible=true chips_overflow=true
FAIL route=/reports viewport=1280x720 clear_button_visible=false chips_overflow=true
Ce que cela signifie : à 1280×720, l’overflow masque le bouton clear. C’est un vrai bug avec une vraie douleur utilisateur.
Décision : épingler les boutons d’action, contraindre la largeur du conteneur de chips ou déplacer les actions dans une zone fixe.
Tâche 13 : Chercher des pics HTTP 429/5xx après une release UI
cr0x@server:~$ sudo awk '$9 ~ /429|500|502|503/ {print $9, $7}' /var/log/nginx/access.log | sort | uniq -c | sort -nr | head
184 429 /api/search
27 503 /api/search
Ce que cela signifie : votre UI de filtres génère assez de trafic pour déclencher des limites ou surcharger.
Décision : revenir sur l’auto-apply, ajouter debounce/batching et corriger les requêtes coûteuses.
Tâche 14 : Confirmer que la taille du payload de requête n’explose pas (POST filters)
cr0x@server:~$ sudo grep "Content-Length" /var/log/nginx/access.log | tail -n 3
"POST /api/search HTTP/2.0" 200 9123 "-" "Mozilla/5.0" cl=842
"POST /api/search HTTP/2.0" 200 9130 "-" "Mozilla/5.0" cl=9421
"POST /api/search HTTP/2.0" 200 9201 "-" "Mozilla/5.0" cl=18822
Ce que cela signifie : les utilisateurs peuvent sélectionner tellement de chips que les corps de requête deviennent volumineux. Cela augmente la latence et peut atteindre des limites.
Décision : appliquer des limites de sélection, ou passer d’« envoyer tous les tags » à des jeux de filtres sauvegardés côté serveur.
Playbook de diagnostic rapide
Quand quelqu’un dit « les chips de filtres sont cassées », ne débattez pas de l’esthétique. Trouvez vite le goulot.
Première étape : déterminer la classe de défaillance
- Échec de layout : chips qui se chevauchent, actions disparues, barres de défilement étranges.
- Échec d’interaction : taps qui ne s’enregistrent pas, bouton supprimer qui se déclenche mal, focus perdu.
- Échec de performance : jank UI, saccades, gel après bascule.
- Échec de données : chips sélectionnées qui ne correspondent pas aux résultats, mismatch d’URL, comptes incorrects.
Deuxième étape : reproduire sous « stress »
- petit viewport + texte agrandi (zoom navigateur / échelle de police OS)
- labels longs (localisation, tags utilisateurs)
- nombreux sélections (20+)
- réseau lent (simulez si possible)
Troisième étape : localiser où le temps est passé
- Thread principal client : longues tâches autour de la bascule ? suspecter mesures de layout, rerenders, mises à jour d’état lourdes.
- Réseau : requêtes multiples par bascule ? suspecter auto-apply, manque de debounce, absence de batching.
- Backend : tail latency ou pics CPU ? suspecter requêtes/index, cache misses.
Que vérifier en 1re/2e/3e position (ordre pratique)
- Premièrement : la zone d’action (Appliquer/Effacer) est‑elle stable et toujours visible ? Si non, corrigez les contraintes de layout avant toute chose.
- Deuxièmement : une interaction déclenche‑t‑elle une seule requête ? Si non, corrigez le debounce/batching et la canonicalisation URL.
- Troisièmement : la latence p95 backend est‑elle acceptable pour des bascules interactives ? Si non, indexez/optimisez et envisagez « Appliquer ».
Erreurs courantes : symptômes → cause racine → correction
1) La page obtient une barre de défilement horizontale après sélection de chips
Symptômes : la page entière défile latéralement ; le contenu semble « décalé ».
Cause racine : un enfant flex refuse de rétrécir parce que min-width par défaut l’en empêche, ou les labels longs ne sont pas tronqués.
Correction : autoriser le shrink (min-width: 0 sur l’élément flex contenant les chips) ; tronquer les labels de chips ; éviter les largeurs fixes sur le contenu des chips.
2) « Effacer » ou « Appliquer » disparaît quand les chips wrap
Symptômes : actions passent à une nouvelle ligne ou sont poussées hors de la vue.
Cause racine : les actions sont dans le même conteneur wrap que les chips, sans zone épinglée.
Correction : séparer les régions de layout : zone chips (wrap/scroll) + zone actions fixe. Si nécessaire, permettez aux chips d’overflow pendant que les actions restent fixes.
3) La ligne de chips vole le scroll et emprisonne les utilisateurs
Symptômes : l’utilisateur tente de scroller la page ; seules les chips bougent ; la page semble bloquée.
Cause racine : région de scroll horizontale dans un header sticky avec gestion tactile agressive.
Correction : assurez que le scroll vertical reste dominant ; ajustez touch-action ; réduisez la région de chips scrollable ; fournissez un panneau alternatif « Filtres ».
4) L’état sélectionné est flou ou incohérent
Symptômes : les utilisateurs ne savent pas ce qui est appliqué ; tickets support « résultats erronés ».
Cause racine : le style repose sur des changements de couleur subtils ; focus et états sélectionnés se chevauchent ; chips sélectionnées cachées par l’overflow.
Correction : ajouter des indices non colorés (coche/bordure), garder l’anneau de focus distinct et garantir que les chips sélectionnées sont visibles dans le résumé (« Filtres (N) »).
5) Cliquer sur la croix de suppression bascule la sélection à la place
Symptômes : la chip bascule quand l’utilisateur essaie de supprimer ; changements de filtre accidentels.
Cause racine : l’icône de suppression n’est pas un bouton séparé, ou la propagation d’événements n’est pas gérée.
Correction : faire de la suppression un bouton dédié avec un libellé propre ; stop propagation ; garder des cibles adaptées au tactile.
6) Le backend fond quand les utilisateurs jouent avec les filtres
Symptômes : pics 429/5xx ; CPU qui grimpe ; latences en queue qui s’aggravent.
Cause racine : auto-apply déclenche une requête par bascule ; mauvais caching dû à des params non canonicalisés ; requêtes tags coûteuses.
Correction : debounce/batch, canonicaliser la sérialisation des filtres, ajouter des index et envisager des jeux de filtres sauvegardés côté serveur.
7) La localisation casse la mise en page
Symptômes : des labels allemands ou finlandais transforment les chips en briques ; la troncature ne marche pas.
Cause racine : chips dimensionnées pour l’anglais ; pas de max-width ; pas de troncature ; icônes consommant trop d’espace.
Correction : imposer une largeur max pour les labels, s’assurer que l’ellipsis fonctionne et tester avec pseudo-localisation et chaînes longues.
Checklists / plan étape par étape
Checklist : Choisir votre stratégie de débordement (ne pas trop réfléchir, mais décider)
- La barre de filtres est‑elle sticky ? Si oui, plafonnez la hauteur et privilégiez un layout stable (wrap max 2 lignes ou scroll).
- Les utilisateurs sélectionnent-ils souvent de nombreux filtres ? Si oui, fournissez un panneau et affichez un compte résumé.
- L’espace vertical est‑il précieux (mobile) ? Si oui, privilégiez le scroll sur une seule ligne plus un panneau.
- Les labels sont‑ils longs ou localisés ? Si oui, imposez la troncature et des largeurs max pour les labels.
- Avez‑vous besoin d’un bouton « Appliquer » ? Si le coût backend est élevé, oui. Sinon, envisagez un auto-apply débouncé.
Checklist : Implémenter états sélectionné/focus/pressé proprement
- L’état sélectionné inclut un indice non coloré (icône, bordure, graisse).
- L’anneau de focus est toujours visible et distinct du style sélectionné.
- L’état pressé/actif est transitoire et ne ressemble pas à sélectionné.
- Le bouton de suppression est séparé et accessible au clavier quand applicable.
- Les attributs ARIA correspondent au comportement (boutons toggle utilisent
aria-pressed).
Étape par étape : Livrer une barre de chips résiliente
- Définir la sémantique : toggle vs navigation vs input chips. Écrivez‑le.
- Choisir le mode d’overflow par breakpoint : wrap sur desktop (lignes max) et scroll sur mobile, ou collapse en résumé.
- Réserver l’espace pour les actions : Appliquer/Effacer doivent être épinglés, pas à la merci du wrap.
- Contraindre les labels : max-width + ellipsis ; tooltip/long-press pour le texte complet si nécessaire.
- Gérer les nombreuses sélections : afficher un résumé de compte et un panneau pour éditer l’ensemble complet.
- Normaliser la sérialisation des filtres : ordre stable pour les params URL et les payloads.
- Contrôler le taux de requêtes : debounce ou Appliquer ; annuler les requêtes en vol sur changement.
- Tester les cas de stress : labels longs, 50 chips, zoom 200 %, petit viewport, réseau lent.
- Ajouter des hooks de monitoring : compter les bascules par session, les bursts de requêtes, les longues tâches UI et le p95 backend.
- Écrire un plan de rollback : feature-flaggez l’auto-apply et revenez à Appliquer si le coût backend explose.
FAQ
1) Les chips doivent‑elles wrapper ou défiler par défaut ?
Wrap sur desktop si l’espace vertical est acceptable. Scroll sur mobile si vous pouvez fournir une affordance évidente et éviter le piège de scroll. Pour des workflows à haute sélection, collapsez en résumé avec un panneau.
2) Combien de chips est « trop » ?
Plus que ce qui tient confortablement sans cacher les actions principales. Concrètement : si les utilisateurs peuvent sélectionner fréquemment plus de 8–12, la barre principale devrait devenir un résumé et une passerelle vers un panneau.
3) « +N de plus » est‑il meilleur que le défilement horizontal ?
Souvent oui sur desktop car cela garde la page stable et évite des sélections cachées qui dérivent hors vue. Sur mobile, le défilement horizontal est acceptable, mais considérez toujours « Filtres (N) » comme contrôle principal.
4) Comment éviter les layout shift quand on ajoute/retire des chips ?
Plafonnez la hauteur du conteneur (lignes max), évitez d’animer la hauteur et gardez les actions dans une région fixe. Si vous devez animer, privilégiez l’opacité/transforms plutôt que height.
5) Pourquoi les labels longs cassent la troncature dans les layouts flex ?
Les éléments flex peuvent refuser de rétrécir à moins que vous ne l’autorisiez. La correction classique est min-width: 0 sur l’enfant flex qui contient le texte, plus un max-width sur la zone de label.
6) Les chips sélectionnées doivent‑elles être supprimables avec un « x » ?
Seulement pour les chips de type input représentant des sélections que l’utilisateur « possède » (comme des destinataires choisis ou des filtres appliqués dans une liste supprimable). Pour les chips toggle, cliquer pour désélectionner suffit souvent. Si vous ajoutez un « x », il doit être un bouton séparé.
7) Auto-apply ou bouton Appliquer ?
Si les requêtes backend sont bon marché et que vous pouvez debouncer et annuler les requêtes en vol, l’auto-apply peut fonctionner. Si les requêtes sont coûteuses ou si les utilisateurs sélectionnent plusieurs filtres à la suite, le bouton Appliquer est la solution fiable. C’est aussi plus simple à raisonner en cas d’incident.
8) Comment rendre une ligne de chips scrollable accessible ?
Utilisez une sémantique de bouton appropriée, maintenez un anneau de focus visible et assurez que les chips focusées se scrollent dans la vue lors du tabbing. Fournissez aussi un point d’entrée alternatif (panneau Filtres) pour que les utilisateurs ne soient pas forcés au défilement horizontal.
9) La sélection des chips doit‑elle être reflétée dans l’URL ?
Oui pour le partage et la résilience au rechargement, surtout dans les outils B2B. Canonicalisez l’ordre des paramètres pour éviter la fragmentation du cache et un comportement confus du bouton Retour.
10) Quel est le design le plus sûr pour un filtrage de qualité entreprise ?
Un header stable avec un résumé (« Filtres (N) »), quelques chips rapides pour les filtres fréquents et un panneau dédié de filtres avec recherche, regroupement et actions claires. La prévisibilité bat l’ingéniosité.
Conclusion : prochaines étapes à livrer
Les chips et les barres de filtres échouent de manières prévisibles : l’overflow cache des contrôles, l’état sélectionné devient ambigu et les « bascule rapides » DDoS silencieusement votre propre backend. Rien de tout cela n’est mystérieux. Ce sont juste des contraintes négligées.
Prochaines étapes qui rapportent immédiatement :
- Décidez votre stratégie d’overflow par breakpoint (wrap avec lignes max, scroll avec affordance ou collapse en résumé).
- Épinglez vos contrôles d’action pour qu’ils ne soient jamais victimes du wrap.
- Rendez les états sélectionné/focus/pressé distincts et accessibles sans s’appuyer uniquement sur la couleur.
- Canonicalisez la sérialisation des filtres et batchez les requêtes pour éviter tempêtes et cache misses.
- Ajoutez un mode stress test (labels longs, nombreuses chips, texte agrandi) et exécutez‑le avant chaque release.
Si vous faites ces cinq choses, votre barre de chips cesse d’être un générateur intermittent de rapports d’incident et redevient ce qu’elle aurait dû être : ennuyeuse, rapide et fiable.