Pill Indicator
Pastille animée qui glisse vers l'onglet actif avec un rebond élastique — position calculée en temps réel via getBoundingClientRect, items ajoutables librement.
Vous aurez accès à la démo interactive avec un compte gratuit.
Cet effet fait partie d'Effect.Labs — 811 effets vanilla, une partie gratuite, une partie premium. Navigation comprend 36 effets, dont 4 gratuits. Découvrir la catégorie →
3 exemples d'usage



Comment ça marche
L'indicateur (.pill-indicator) est un <div> absolument positionné à l'intérieur du .pill-nav en position: relative. Quand un item est cliqué, le script lit getBoundingClientRect() de l'item et du conteneur, calcule la différence de left et copie le width — puis assigne ces deux valeurs en px sur les styles left et width de l'indicateur. CSS transition prend ensuite le relais pour animer le déplacement.
La transition porte la courbe cubic-bezier(0.175, 0.885, 0.32, 1.275) sur .3s — un easing de type back-out qui génère un léger dépassement (overshoot) avant de se stabiliser, d'où l'effet rebond sans aucune physique JS. Modifier uniquement la durée ou substituer ease-out supprime ce rebond.
Les .pill-link (spans) ont position: relative; z-index: 1 pour rester visuellement au-dessus de la pastille qui coulisse derrière eux. L'item actif reçoit la classe active (couleur #fff) ; les inactifs restent en --text-secondary. Le script initialise la position sur le premier item au chargement avec i.length > 0 && l(i[0]).
Accessibilité
- prefers-reduced-motion absent dans le code : aucune vérification n'est faite. Ajoutez
@media (prefers-reduced-motion: reduce) { .pill-indicator { transition: none } }dans votre feuille de style pour les utilisateurs sensibles au mouvement. - Les
.pill-linksont des<span>, non focusables au clavier par défaut. Pour l'accessibilité complète, ajoutezrole="tab",tabindex="0"et un gestionnairekeydown(Entrée/Espace) sur chaque item. - Le
.pill-navne porte pas derole="tablist"natif dans le code — à ajouter si l'indicateur pilote un contenu de panneau avecaria-selectedetaria-controls. - Contraste : le texte actif (blanc
#fff) sur fond pastille (#6366f1) atteint ~4,5:1 — limite WCAG AA pour le texte normal. Le texte inactif (#a1a1aasur#12121a) dépasse 7:1 — niveau AAA. - Aucun
aria-labeln'est posé sur le conteneur nav dans le code de base : à compléter selon votre usage (aria-label="Navigation principale").
Compatibilité navigateur
Utilise getBoundingClientRect, addEventListener et transitions CSS — disponibles dans tous les navigateurs modernes depuis 2019. Zéro dépendance externe.
Si <code>getElementById('pillNav')</code> ne trouve aucun élément, le bloc <code>if (e && n)</code> empêche toute erreur JS fatale ; les items restent cliquables mais sans indicateur animé.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="pill-nav" id="pillNav">
<div class="pill-indicator" id="pillIndicator"></div>
<span class="pill-link active" data-index="0">Accueil</span>
<span class="pill-link" data-index="1">À propos</span>
<span class="pill-link" data-index="2">Travaux</span>
</div>
HTML + CSS + JS complet, prêt à coller — avec des centaines d'effets premium.
Personnaliser
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
| background (.pill-indicator) | #6366f1 | Couleur de fond de la pastille — la valeur est codée en dur dans le CSS, remplacez-la directement par votre teinte brand. |
| --bg-card | #12121a | Fond du conteneur .pill-nav. |
| --radius-full | 9999px | Arrondi de la pastille et du conteneur. 6px donne un look onglet rectangulaire arrondi. |
| --text-secondary | #a1a1aa | Couleur du texte des items non sélectionnés. |
| transition (.pill-indicator) | .3s cubic-bezier(.175,.885,.32,1.275) | Durée et easing du glissement. Utilisez none pour supprimer toute animation, ease-out pour un glissement sans rebond. |
| padding (.pill-link) | 0.5rem 1.5rem | Espacement interne de chaque item. Réduisez à 0.4rem 1rem pour un nav compact. |
| supplémentaire | 3 items | Ajoutez autant de spans que nécessaire — getBoundingClientRect recalcule la position exacte à chaque clic, sans index codé en dur. |
FAQ
getBoundingClientRect — aucun index n'est codé en dur. Ajoutez ou retirez des <span class="pill-link"> librement : l'indicateur suit automatiquement.i.length > 0 && l(i[0]) initialise sur le premier item. Remplacez i[0] par l'index voulu (ex. i[2]) ou par un e.querySelector('.pill-link.active') pour lire l'état défini en HTML.getElementById('pillNav') — un seul élément par page. Pour plusieurs instances, donnez des ids distincts et dupliquez le bloc de logique pour chaque paire nav/indicateur, ou itérez sur document.querySelectorAll('.pill-nav') après avoir référencé l'indicateur par relation DOM (nav.querySelector('.pill-indicator')).