Résidence Les Tilleuls · Bât. B
Appartement 12M. et Mme Lebrun · 3ᵉ étage
Appuyez sur Appeler pour sonner chez eux
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.
Mis à jour le
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 →
Résidence Les Tilleuls · Bât. B
Appartement 12M. et Mme Lebrun · 3ᵉ étage
Appuyez sur Appeler pour sonner chez eux
Espresso
Désarmée
Portes et fenêtres fermées. Prête à armer.
4 zones · 2 détecteurs · garage inclus
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.
#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.#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..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é.<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.prefers-reduced-motion n'a rien à couper.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.
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.
Copiez les trois blocs dans votre page. Aucune dépendance.
<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>
.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;
}
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
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. |
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.
: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.
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.