GlassmorphismGratuit

Neu Button

Deux boutons de 180 × 54 px taillés dans la même matière que leur fond #e0e5ec : une ombre foncée en bas à droite, une ombre blanche en haut à gauche, et l'état enfoncé passe en ombres internes. Pur CSS, zéro JavaScript.

CSSNeumorphismHover

Mis à jour le

Survolez ou cliquez la scène pour interagir

Cet effet est gratuit — la catégorie Glassmorphism en contient 47 au total, dont 6 gratuits. Effect.Labs regroupe 840 effets vanilla. Découvrir la catégorie →

Exemples d'usage

Résidence Les Tilleuls · Bât. B

Appartement 12M. et Mme Lebrun · 3ᵉ étage

Appuyez sur Appeler pour sonner chez eux

① Touches d'appel d'un interphone vidéo — écran tactile de la platine d'immeuble

QuandLe visiteur a choisi l'appartement 12 sur la platine de rue : il reste deux grosses touches, Appeler et Raccrocher.
PourquoiLe relief imite les boutons métalliques des anciennes platines, et l'état enfoncé qui reste visible confirme que l'appel part sans écran supplémentaire. Deux touches de 180 × 54 px se visent sans hésiter, même avec des gants.
RéglagesTaille et ombres d'origine. Les deux boutons côte à côte dans .neu-btn-scene, qui porte la surface #e0e5ec au milieu d'une plaque inox. Au clic, la scène pose .neu-btn--pressed et aria-pressed="true" sur Appeler et met à jour une ligne d'état en role="status" ; Raccrocher remet les deux touches en relief.

Espresso

40 ml · intensité 4/5

Préparation… 14 s

② Marche et pause d'une machine à café à grain — écran tactile de la machine

QuandL'utilisateur a lancé un espresso : le bouton Marche reste enfoncé pendant la préparation, Pause attend à côté.
PourquoiSur un appareil, un bouton doit avoir l'air d'un bouton. Le creux qui reste visible dit que la préparation est en cours sans avoir à lire l'écran ; un bouton plat changerait seulement de couleur.
RéglagesBoutons empilés comme dans le code (colonne, 16 px d'écart), à droite d'un résumé de boisson. Marche démarre avec .neu-btn--pressed ; un clic sur Pause transfère l'état enfoncé et met la jauge de préparation à zéro. Libellé d'état en #6d28d9 (5,6:1) plutôt que le violet d'origine.

Désarmée

Portes et fenêtres fermées. Prête à armer.

4 zones · 2 détecteurs · garage inclus

③ Armer l'alarme en partant — clavier mural d'une alarme de maison

QuandEn sortant, l'habitant appuie sur Armer sur le clavier de l'entrée ; le voyant passe au rouge et le bouton reste enfoncé.
PourquoiL'état enfoncé qui persiste dit « armée » d'un coup d'œil, comme un interrupteur mécanique resté baissé. Le doublon texte + voyant évite de compter sur la seule ombre.
RéglagesSurface du clavier = .neu-btn-scene, posée sur un mur beige : le relief tient parce que la surface garde sa teinte propre. Armer et Désarmer s'excluent ; le voyant (vert/rouge) et le texte « Armée — sortie dans 30 s » sont pilotés par la scène.

Comment ça marche

Tout tient dans trois règles. .neu-btn fait 180 × 54 px, border-radius: 16px, sans bordure, avec le même fond que la surface (#e0e5ec) et deux ombres portées opposées : 8px 8px 16px #b8bec7 (la face à l'ombre) et -8px -8px 16px #ffffff (la face éclairée). C'est ce couple qui fait sortir le bouton de la matière : aucun contour, aucun dégradé. Au survol, les ombres se resserrent à 6 px / 12 px et le texte passe de #6b7280 au violet #8b5cf6 ; transition: all 0.2s ease lisse le passage. :active et la classe .neu-btn--pressed partagent la même règle : les deux ombres deviennent internes (inset 4px 4px 8px #b8bec7, inset -4px -4px 8px #ffffff) et le bouton paraît creusé. L'icône SVG (18 px, trait 2 px) suit currentColor, donc vire au violet avec le texte.

Le neumorphisme ne fonctionne que si l'ombre claire tombe sur la teinte du bouton : sur un fond noir, -8px -8px 16px #ffffff devient un halo blanc, sur un fond blanc elle disparaît. C'est pourquoi .neu-btn-scene déclare sa propre surface (background: #e0e5ec, padding: 32px, border-radius: 24px). Les 32 px contiennent la totalité des ombres (8 px de décalage + 16 px de flou = 24 px), si bien que l'effet garde son relief collé dans n'importe quelle page, claire ou sombre, sans toucher au body. Mesuré dans Chromium et Firefox : fond calculé rgb(224, 229, 236) sur le conteneur, aucun halo hors de la surface.

La classe .neu-btn--pressed est un état posé à la main : le CSS n'en change jamais tout seul. Pour un bouton qui reste enfoncé (appel en cours, alarme armée), ajoutez-la au clic et doublez-la de aria-pressed="true" — c'est ce que font les trois scènes ci-dessous avec une dizaine de lignes de JavaScript qui ne font pas partie de l'effet.

Accessibilité

  • Contraste du texte : #6b7280 sur #e0e5ec mesure 3,8:1 et le violet #8b5cf6 (survol, enfoncé) 3,3:1, pour un texte de 15,2 px en graisse 600 — sous le seuil de 4,5:1 dans les deux cas. Assombrissez le libellé en #4b5563 (6,0:1) et le violet en #6d28d9 (5,6:1) : le relief n'en dépend pas.
  • Limites du bouton : il n'existe que par ses ombres. #b8bec7 sur #e0e5ec fait 1,5:1, loin des 3:1 demandés aux composants (WCAG 1.4.11), et l'ombre blanche ne contraste pas du tout. Une personne malvoyante peut ne voir que du texte flottant : ajoutez une bordure 1px solid #9ca3af si votre public le demande, ou gardez l'icône et le texte bien lisibles.
  • État enfoncé : .neu-btn--pressed est purement visuel. Un lecteur d'écran annonce « bouton » sans dire s'il est enfoncé : posez aria-pressed="true" / "false" en même temps que la classe. L'information passe aussi par la couleur seule (violet) et l'ombre : un texte d'état à côté (« Appel en cours ») évite toute ambiguïté.
  • Clavier : ce sont de vrais <button>, atteignables par Tab et activés par Entrée ou Espace. Le code ne définit aucun style de focus : c'est l'anneau par défaut du navigateur qui s'affiche (vérifié dans Chromium et Firefox). Pour l'accorder au violet : .neu-btn:focus-visible { outline: 2px solid #8b5cf6; outline-offset: 3px }. Ajoutez type="button" si le bouton vit dans un formulaire, sinon il le soumet.
  • Mouvement : seule une transition de 0,2 s sur les ombres et la couleur, déclenchée par l'utilisateur. Rien ne bouge seul, prefers-reduced-motion n'a rien à couper.

Compatibilité navigateur

CSS seul : box-shadow multiple et inset, border-radius, transition, flexbox avec gap (Chrome 84, Firefox 63, Safari 14.1), SVG en ligne avec stroke: currentColor. Zéro JavaScript, zéro dépendance. Rendu identique mesuré dans Chromium, Firefox et WebKit (Playwright) : fond de surface rgb(224, 229, 236), ombres internes sur l'état enfoncé. La police 'Inter' n'est pas chargée par l'effet : sans elle, le navigateur prend la police sans empattement du système.

Chrome 84+✓ Complet
Firefox 63+✓ Complet
Safari 14.1+✓ Complet
Edge 84+✓ Complet
Mobile iOS 14.5+✓ Complet (:active visible pendant l'appui)
Android Chrome 84+✓ Complet

Sans box-shadow (navigateurs très anciens), les boutons restent des rectangles gris arrondis, lisibles et cliquables, sans relief. Avant le gap des conteneurs flex, les deux boutons se touchent et l'icône colle au texte : ajoutez des marges. Sur écran tactile, il n'y a pas de survol : l'état :active ne dure que le temps de l'appui, d'où la classe .neu-btn--pressed pour un état qui persiste.

Le code

Copiez les trois blocs dans votre page. Aucune dépendance.

HTML
<div class="neu-btn-scene">
    <button class="neu-btn">
      <svg viewBox="0 0 24 24"><path d="M5 12h14M12 5l7 7-7 7"/></svg>
      Continuer
    </button>
    <button class="neu-btn neu-btn--pressed">
      <svg viewBox="0 0 24 24"><path d="M20 6L9 17l-5-5"/></svg>
      Validé
    </button>
  </div>
CSS
.neu-btn-scene {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 16px;
  background: #e0e5ec;
  padding: 32px;
  border-radius: 24px;
}

.neu-btn {
  width: 180px;
  height: 54px;
  border-radius: 16px;
  background: #e0e5ec;
  box-shadow: 8px 8px 16px #b8bec7, -8px -8px 16px #ffffff;
  border: none;
  font-family: 'Inter', sans-serif;
  font-size: 0.95rem;
  font-weight: 600;
  color: #6b7280;
  cursor: pointer;
  transition: all 0.2s ease;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
}

.neu-btn:hover {
  box-shadow: 6px 6px 12px #b8bec7, -6px -6px 12px #ffffff;
  color: #8b5cf6;
}

.neu-btn:active,
.neu-btn--pressed {
  box-shadow: inset 4px 4px 8px #b8bec7, inset -4px -4px 8px #ffffff;
  color: #8b5cf6;
}

.neu-btn svg {
  width: 18px;
  height: 18px;
  stroke: currentColor;
  fill: none;
  stroke-width: 2;
}
JavaScript (fx-0311)

Personnaliser

Options passées à l'API ou attributs data-* :

Option / propriétéDéfautEffet
Teinte de la matière (CSS — #e0e5ec sur .neu-btn et .neu-btn-scene) #e0e5ec, ombres #b8bec7 et #ffffff Changez les trois couleurs ensemble : le fond, une ombre environ 15 % plus sombre et une ombre plus claire. Pour une matière sombre : fond #2a2d34, ombres #1d1f24 et #373b44. Le bouton et sa surface doivent toujours avoir la même teinte, sinon le relief se lit comme une tache.
Profondeur (CSS — 8px 8px 16px au repos, 6px 6px 12px au survol) 8 px de décalage, 16 px de flou Doublez décalage et flou (12 px / 24 px) pour un relief plus marqué, en augmentant le padding de .neu-btn-scene d'autant (il doit rester supérieur à décalage + flou). En dessous de 4 px / 8 px, le bouton se fond dans la surface.
État enfoncé (CSS — classe .neu-btn--pressed) inset 4px 4px 8px Posez la classe en JavaScript pour un état qui persiste, avec aria-pressed. Pour un creux plus profond : inset 6px 6px 12px #b8bec7, inset -6px -6px 12px #ffffff.
Taille (CSS — width: 180px; height: 54px; border-radius: 16px) 180 × 54 px Un bouton carré d'appareil (64 × 64 px, border-radius: 50%) garde le même rendu. Gardez au moins 44 px de haut pour le tactile.
Couleur d'accent (CSS — color: #8b5cf6 au survol et enfoncé) #8b5cf6 (3,3:1 sur la matière) #6d28d9 atteint 5,6:1 ; un vert d'appareil #166534 5,6:1. L'icône suit, puisqu'elle est en currentColor.

FAQ

Pourquoi mes boutons ont-ils un halo blanc sur mon site sombre ?

Parce que l'ombre claire -8px -8px 16px #ffffff est faite pour tomber sur la même teinte que le bouton. Le code vendu donne donc à .neu-btn-scene sa propre surface #e0e5ec avec 32 px de marge intérieure : gardez ce conteneur autour des boutons et le relief reste juste sur n'importe quel fond. Si vous retirez la surface, posez les boutons sur un bloc de la même couleur qu'eux.

Comment garder un bouton enfoncé après le clic ?

:active ne dure que le temps de l'appui. Ajoutez la classe en JavaScript : btn.addEventListener('click', () => { const on = btn.classList.toggle('neu-btn--pressed'); btn.setAttribute('aria-pressed', on); }). Pour un groupe où un seul bouton reste enfoncé (Marche / Pause), retirez d'abord la classe des autres.

Quelle différence avec Neu Toggle (fx-0313) ?

Neu Button est une action : on appuie, il s'enfonce, la page décide de la suite. Neu Toggle est un interrupteur à deux états avec une piste creusée et un bouton qui glisse en rebondissant, annoncé comme role="switch". Pour « allumer / éteindre » un réglage, prenez le toggle ; pour lancer ou arrêter quelque chose, le bouton.