Boutons✨ Premium

Rotating Border

Un rectangle en dégradé conique indigo → fuchsia tourne sous une face sombre et n'apparaît que dans un anneau de 1 px — un tour en 3 s, sans survol, en CSS seul.

CSSAnimationBorder

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. Boutons comprend 27 effets, dont 2 gratuits. Découvrir la catégorie →

3 exemples d'usage

Essayer gratuitement — landing d'un produit IA ou SaaS — Rotating Border exemple 1

① Essayer gratuitement — landing d'un produit IA ou SaaS

QuandHero sombre : accroche, sous-titre, un bouton principal isolé au centre.
PourquoiL'indigo → fuchsia est la palette des produits IA de 2024-2026 et l'anneau animé attire l'œil sur le seul bouton de la page sans le remplir de couleur. À cette taille, les deux arcs qui parcourent le bord se lisent comme un balayage volontaire.
RéglagesTexte 16 px, padding: 18px 40px, rotation 4s pour un balayage plus calme, face #0d0d16 pour se détacher du fond.
Choisir Pro — plan recommandé d'une grille tarifaire — Rotating Border exemple 2

② Choisir Pro — plan recommandé d'une grille tarifaire

QuandTrois cartes de prix ; le plan du milieu porte le bouton animé, les autres un bouton neutre.
PourquoiUn seul élément en mouvement dans la grille désigne le plan recommandé sans étiquette. L'anneau de 1 px reste discret sur une carte dense ; réservez l'animation au plan que vous poussez.
RéglagesBouton pleine largeur de la carte, padding: 13px, dégradé aligné sur l'accent du plan, animation-duration: 5s.
Générer — barre de saisie d'un outil de génération — Rotating Border exemple 3

③ Générer — barre de saisie d'un outil de génération

QuandChamp de prompt avec des suggestions et un bouton d'envoi à droite.
PourquoiLe bouton animé signale l'action « magique » de l'interface tout en restant sombre comme le champ ; le mouvement permanent indique que l'outil est prêt. Sur un bouton court et presque carré, le rectangle qui tourne couvre l'anneau bien plus longtemps — l'effet est ici à son meilleur.
RéglagesBouton compact (padding: 12px 18px), border-radius: 10px sur les trois calques (12px / 9px pour les pseudo-éléments), rotation 2.5s.

Comment ça marche

Trois calques empilés. Le bouton .btn-rotate-border est transparent (background: 0 0), avec position: relative; overflow: hidden; border-radius: 12px hérités de .demo-btn et z-index: 1 qui crée un contexte d'empilement — indispensable pour que les pseudo-éléments à z-index négatif restent dans le bouton au lieu de passer derrière la page.

::before (z-index: -2) est le dégradé : inset: -3px, soit un rectangle débordant de 3 px de chaque côté, rempli par conic-gradient(#6366f1, #d946ef, #6366f1) — indigo à 0°, fuchsia à 180°, retour à l'indigo, sans raccord visible. Il est animé par rotateBorder : transform: rotate(360deg) en 3s linear infinite. ::after (z-index: -1) est la face : inset: 1px; background: #0a0a0f; border-radius: 11px. Entre le bord du bouton (rogné à 12 px par overflow: hidden) et cette face, il reste un anneau de 1 px où le dégradé se voit.

Ce qui tourne n'est pas le dégradé mais le rectangle qui le porte, et c'est là que le code montre sa limite. Le bouton de démonstration mesure 190 × 54 px ; son ::before fait 196 × 60 px et pivote autour de son centre. À 0° et 180° il recouvre tout l'anneau ; dès qu'il tourne, son petit côté (60 px) ne couvre plus la largeur du bouton et l'anneau n'est plus alimenté sur les flancs. Mesuré dans Chromium à 45°, 90° et 135° : les pixels du bord gauche et du coin valent la couleur de page, seuls deux courts arcs restent visibles (haut-gauche et bas-droit), qui glissent le long du bord.

L'effet réel est donc un anneau qui s'allume en entier deux fois par tour (toutes les 1,5 s) et se réduit entre-temps à deux traits mobiles — plus proche d'un « scanner » que d'un dégradé qui tourne. Il n'y a aucune règle :hover ni :active : l'animation tourne dès le chargement, en permanence. La FAQ donne les deux façons de rendre l'anneau continu.

Accessibilité

  • prefers-reduced-motion absent du code : une rotation permanente dans le champ de vision. Ajoutez @media (prefers-reduced-motion: reduce) { .btn-rotate-border::before { animation: none } } — il reste un anneau dégradé fixe, complet (à 0° le rectangle couvre tout), parfaitement lisible.
  • Contraste du texte : blanc sur la face #0a0a0f = 19,8:1, niveau AAA. La face est opaque : la couleur de la page derrière ne change rien à la lisibilité.
  • Contraste de l'anneau (WCAG 1.4.11, composants d'interface, seuil 3:1) : #6366f1 sur #0a0a0f = 4,4:1 et #d946ef = 5,7:1 — le contour est perceptible, mais il ne fait que 1 px et disparaît sur les flancs pendant la plus grande partie du cycle. Ne comptez pas sur lui pour délimiter le bouton : la face sombre sur un fond sombre (#0a0a0f sur #0a0a0f) n'a aucun bord propre. Sur un fond de page identique, ajoutez ::after { background: #12121a } ou une bordure fixe de 1 px.
  • Lecteurs d'écran : les deux pseudo-éléments ont content: "" — rien n'est annoncé, le nom accessible est le libellé. Le bouton reste un <button> natif, focusable et activable au clavier ; l'animation n'a aucune dépendance au survol, elle est identique pour tous.
  • Focus : le code ne touche pas à outline, l'anneau de focus natif reste visible et n'est pas rogné par overflow: hidden (l'outline est dessiné hors de la boîte). Cible ≈ 54 px de haut (padding 18 px + texte), au-dessus du minimum de 44 px.

Compatibilité navigateur

Requiert la propriété raccourcie inset, conic-gradient() et une animation CSS de transform sur un pseudo-élément. Aucun JavaScript, zéro dépendance.

Chrome 87+✓ Complet
Firefox 83+✓ Complet
Safari 14.1+✓ Complet
Edge 87+✓ Complet
Mobile iOS 14.5+✓ Complet
Android Chrome✓ Complet

Sans inset (Safari avant 14.1, Firefox avant 66), les pseudo-éléments n'ont ni taille ni position : le bouton est un simple texte blanc sans fond ni contour. Remplacez inset par top/right/bottom/left explicites pour ces navigateurs. Sans conic-gradient (Firefox avant 83), le ::before n'a pas de fond : anneau invisible, face intacte.

Le code

Structure HTML à coller dans votre page (CSS + JS disponibles avec un compte premium) :

index.html — structure
🔒 Débloquer le code complet — à partir de 2,99 € le 1er mois

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éfautEffet
animation-duration (CSS — ::before, rotateBorder) 3s Durée d'un tour. L'anneau complet réapparaît deux fois par tour : 2s = un flash toutes les secondes ; 6s = balayage lent.
conic-gradient(#6366f1, #d946ef, #6366f1) (CSS — ::before) indigo → fuchsia → indigo Couleurs de l'anneau. Répétez la première couleur en dernier pour éviter un raccord net à 0°. Ajoutez des arrêts (#6366f1, #22d3ee, #d946ef, #6366f1) pour un anneau multicolore.
inset (CSS — ::after) 1px Épaisseur de l'anneau visible : c'est l'écart entre le bord du bouton et la face. 2px double l'anneau ; ajustez alors border-radius de la face (12 − inset).
background (CSS — ::after) #0a0a0f Couleur de la face. Identique au fond du catalogue ; sur une autre page, alignez-la sur votre surface ou éclaircissez-la (#12121a) pour que le bouton ait un bord propre.
inset (CSS — ::before) -3px Débord du rectangle qui tourne. Sans effet sur l'épaisseur (le bouton rogne à son bord) ; c'est la forme du ::before qui décide de la couverture de l'anneau — voir la FAQ pour un anneau continu.
border-radius (CSS — .demo-btn 12px / ::before 14px / ::after 11px) 12 / 14 / 11 px Arrondi des trois calques. Gardez face = bouton − inset de la face. 999px partout donne une pilule ; l'anneau y est couvert moins longtemps encore.
animation-play-state (CSS — à ajouter) running Mettez paused au repos et running sous :hover pour un anneau immobile qui se met à tourner au survol, sans repartir de zéro.

FAQ

Parce que c'est un rectangle qui tourne, pas le dégradé. Le ::before a la taille du bouton + 3 px ; une fois pivoté de 90°, son petit côté (≈ 60 px) ne couvre plus les 190 px de largeur, et l'anneau n'a plus rien à montrer sur les flancs. Deux remèdes. Soit un pseudo-élément carré plus grand que la diagonale : .btn-rotate-border::before { inset: auto; left: 50%; top: 50%; width: 200%; aspect-ratio: 1; translate: -50% -50% } — l'anneau est alors alimenté à tout angle. Soit ne plus tourner la boîte mais l'angle du dégradé : @property --a { syntax: '<angle>'; inherits: false; initial-value: 0deg }, conic-gradient(from var(--a), …) et @keyframes { to { --a: 360deg } } — plus propre, mais Firefox 128+ et Safari 16.4+ seulement.
Gardez la déclaration animation et ajoutez .btn-rotate-border::before { animation-play-state: paused } puis .btn-rotate-border:hover::before { animation-play-state: running }. L'anneau reste complet au repos (à 0° le rectangle couvre tout) et repart de là où il s'était arrêté. Si vous préférez qu'il reparte toujours de 0°, ne déclarez animation que sous :hover::before.
Les pseudo-éléments ont des z-index négatifs (−2 et −1). Sans contexte d'empilement sur le bouton, ils se placeraient derrière le fond de la page et deviendraient invisibles. z-index: 1 avec position: relative crée ce contexte : les calques négatifs se peignent au-dessus du fond du bouton mais sous son texte. Si vous retirez z-index, anneau et face disparaissent, seul le texte blanc reste.