Floating Indicator
Une pill indicatrice glisse sous l'onglet actif — position et largeur lues en temps réel via getBoundingClientRect(), interpolées par une cubic-bezier(0.44, 0, 0.56, 1).
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 (.floating-nav-indicator) est un <div> positionné en absolute à l'intérieur du conteneur .floating-nav (en relative). Il apparaît visuellement sous les labels grâce à un z-index: 1 sur les items. Sa couleur est un dégradé linéaire 135° de #6366f1 à #8b5cf6, complété par un halo box-shadow violet à 40 % d'opacité. Au chargement, le JS le positionne immédiatement sous le premier onglet sans transition visible.
Au clic sur un item, l'effet appelle getBoundingClientRect() sur l'item et sur le conteneur nav, puis calcule un décalage relatif en soustrayant les deux valeurs de left. Il écrit ensuite style.width et style.left sur l'indicateur. La mesure est faite en temps réel à chaque clic, ce qui rend l'effet insensible aux variations de largeur des labels — un label court vers un long produit une dilatation-glissade simultanée des deux propriétés.
Le CSS prend le relais via transition: .4s cubic-bezier(.44, 0, .56, 1) sur .floating-nav-indicator. Cette courbe est une ease-in-out légèrement plus vive que la courbe CSS native (ease-in-out = cubic-bezier(.42, 0, .58, 1)), ce qui donne à la pill une sensation de masse nette sans traîner. Les deux propriétés animées (left et width) déclenchent le layout à chaque frame — pour des perfs GPU-only, elles peuvent être remplacées par transform: translateX() + scaleX().
Accessibilité
- prefers-reduced-motion : non géré dans ce code — la transition CSS reste active même si l'OS demande à réduire le mouvement. Correctif minimal :
@media (prefers-reduced-motion: reduce) { .floating-nav-indicator { transition: none } }. - Les items sont des
<span>non focusables nativement — la nav n'est pas atteignable au clavier. Pour la conformité WCAG 2.1.1, remplacez-les par des<button>ou ajouteztabindex="0"avec un listenerkeydownsur Enter/Space. - Aucun rôle sémantique sur le conteneur — ajoutez
role="tablist"sur.floating-navetrole="tab"+aria-selected="true/false"sur chaque item pour une conformité lecteur d'écran complète. - Contraste texte inactif (
#a1a1aasur#12121a) : ratio ≈ 7,2:1 — WCAG AA et AAA ✓. - Contraste texte actif (
#ffffffsur le gradient#6366f1→#8b5cf6) : ratio ≈ 4,3:1 — légèrement en dessous du seuil AA de 4,5:1 pour le texte normal de taille standard.
Compatibilité navigateur
Utilise getBoundingClientRect() et les transitions CSS — disponibles dans tous les navigateurs modernes sans polyfill.
Sans JS, l'indicateur reste sur sa position initiale définie en HTML et aucune erreur n'est levée — la nav reste lisible mais statique.
Le code
Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :
<div class="floating-nav" id="floatingNav">
<div class="floating-nav-indicator" id="floatingIndicator"></div>
<span class="floating-nav-item active" data-index="0">Onglet 1</span>
<span class="floating-nav-item" data-index="1">Onglet 2</span>
<span class="floating-nav-item" data-index="2">Onglet 3</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 |
|---|---|---|
| --primary | #6366f1 | Couleur de départ du dégradé de la pill. Remplacez par votre couleur brand — la fin du dégradé (#8b5cf6) est codée en dur dans le CSS de .floating-nav-indicator. |
| --bg-card | #12121a | Fond du conteneur de navigation flottant. |
| --text-secondary | #a1a1aa | Couleur des labels inactifs. Assurez un ratio ≥ 4,5:1 avec votre couleur de fond. |
| --radius-lg | 0.75rem | Border-radius du conteneur nav et de la pill. Montez à 2rem pour une pill entièrement ronde. |
| --border | rgba(255,255,255,0.08) | Bordure du conteneur flottant — ajustez l'opacité pour l'effacer ou l'accentuer. |
| transition (.floating-nav-indicator) | .4s cubic-bezier(.44, 0, .56, 1) | Durée et courbe de la glissade. Passez à .25s ease pour une réponse plus nerveuse, ou .6s pour un effet plus ample. |
| padding (.floating-nav) | 6px | Espace interne entre la bordure du conteneur et les items — change la marge autour de la pill. |
| box-shadow (.floating-nav-indicator) | rgba(99,102,241,.4) 0 4px 15px | Halo lumineux sous la pill. Changez la couleur et l'intensité selon votre palette. |
FAQ
getBoundingClientRect(), qui lit les coordonnées au moment du clic. Si la fenêtre est redimensionnée ensuite, aucun recalcul n'est déclenché. Ajoutez window.addEventListener('resize', () => m(activeItem)) où activeItem est l'item actuellement marqué .active — ou utilisez un ResizeObserver sur le conteneur pour une réactivité plus précise.floatingNav et floatingIndicator dans un IIFE. Pour plusieurs instances, extrayez la logique en une fonction réutilisable : function initFloatingNav(navEl, indicatorEl) { const items = navEl.querySelectorAll('.floating-nav-item'); /* … */ } et appelez-la pour chaque paire, en passant les éléments directement plutôt que des IDs.left et width, deux propriétés qui provoquent un relayout. Pour une animation 100 % compositeur, remplacez l'indicateur par un élément de largeur 100 % et animez transform: translateX(offsetPx) scaleX(ratio) avec transform-origin: left — le navigateur composite la transformation sans toucher au layout. Le calcul du décalage et du ratio reste à faire en JS avec getBoundingClientRect().