Fournil Lemaire · boulangerie en ligne
Pain aux graines
4,20 €
Ingrédients
Farine de blé T80, eau, levain naturel, (sésame, lin, tournesol), sel de Guérande.
Une bulle qui éclot depuis sa pointe, juste au-dessus du bouton qui l'ouvre : un vrai popover HTML (Échap et clic à l'extérieur la ferment sans script), une entrée écrite avec @starting-style — opacité et scale de 0,6 à 1 en 0,4 s sur une courbe à rebond —, un ancrage CSS par position-area avec repli en JavaScript, et un fondu de 0,15 s quand l'utilisateur réduit les animations.
Mis à jour le
Cet effet est gratuit — la catégorie Transitions en contient 52 au total, dont 9 gratuits. Effect.Labs regroupe 840 effets vanilla. Découvrir la catégorie →
Fournil Lemaire · boulangerie en ligne
4,20 €
Farine de blé T80, eau, levain naturel, (sésame, lin, tournesol), sel de Guérande.
alcalinstransitionnon-métauxgaz nobles
Touchez une case en surbrillance
Exercice 3 · masse molaireQuelle est la masse de 0,25 mol de fer ? Arrondir au dixième de gramme.
Le loyer est fixé à 640 € par mois, auquel s'ajoutent 55 € de provisions sur charges, régularisées chaque année.
Il est révisé chaque année à la date anniversaire du bail, selon l'indice de référence des loyers (IRL).
À la signature, le locataire verse un de 640 €, restitué après la remise des clés.
Le loyer est payable par virement avant le 5 de chaque mois.
Le HTML tient en deux éléments : un <button popovertarget="popoverBloomBox"> et une div.popover-bloom-box qui porte l'attribut popover. Le navigateur fait le reste : un clic sur le bouton ouvre la bulle, un deuxième clic, la touche Échap ou un clic n'importe où ailleurs la referme (mode « auto », mesuré dans Chromium 151, Firefox 153 et WebKit 26.5). Ouverte, la bulle passe dans la couche supérieure de la page : aucun overflow: hidden d'un parent ne la coupe et aucun z-index voisin ne passe devant. Pour la poser au-dessus du bouton, le CSS remet inset: auto et margin: 0 0 10px puis demande position-area: top : la bulle se centre au-dessus de son ancre, à 10 px de son bord (mesuré dans les trois moteurs, écart horizontal 0 px).
L'éclosion repose sur deux états. Au repos, la bulle vaut opacity: 0 et scale: 0.6, avec transform-origin: bottom center : elle grandit depuis sa pointe, là où se trouve le mot touché. Ouverte (:popover-open), elle vaut 1 et 1. Comme un popover passe de display: none à affiché, il n'a pas d'état « avant » : la règle @starting-style lui en donne un (0 et 0,6), et la transition de 0,4 s sur cubic-bezier(0.34, 1.56, 0.64, 1) joue l'entrée avec un léger dépassement. Mesuré 70 ms après le clic : opacité 0,49 à 0,62 et échelle 0,80 à 0,85 selon le moteur. La sortie demande en plus display et overlay en allow-discrete, pour que la bulle reste affichée et dans la couche supérieure le temps de rétrécir : Chromium la joue (opacité 0,33 à 80 ms), Firefox 153 et WebKit 26.5 ne connaissent pas encore overlay et ferment la bulle d'un coup.
Le script (2,3 Ko) n'ouvre ni ne ferme rien. Il pose aria-expanded sur le bouton à chaque évènement toggle, et donne à chaque bulle de la page un nom d'ancre propre (--popover-bloom-1, -2…) : plusieurs bulles cohabitent sans se disputer le même bouton. Là où position-area manque, il place la bulle lui-même juste avant son ouverture (beforetoggle) à partir du rectangle du bouton, et la suit au défilement et au redimensionnement — mesuré en simulant l'absence d'ancrage : même écart de 10 px, bulle toujours au-dessus du bouton après défilement. Là où l'API Popover manque entièrement, il bascule une classe .popover-bloom-open et écoute Échap ; le clic à l'extérieur n'est alors pas géré.
<button> — Entrée ou Espace ouvre la bulle, Échap la ferme, le focus reste sur le bouton (mesuré dans Chromium et Firefox ; Safari ne donne le focus aux boutons au clic que si l'utilisateur l'a réglé). Le bouton annonce son état : aria-expanded passe de false à true à l'ouverture et revient à la fermeture, quelle qu'en soit la cause (clic, Échap, clic extérieur).display: none et n'est pas lue. Pour une information indispensable (un allergène, par exemple), écrivez-la aussi dans la page : une bulle reste un complément.prefers-reduced-motion, l'échelle reste à 1 et la bulle apparaît par un fondu de 0,15 s (mesuré dans les trois moteurs : opacité 0,58 à 0,74 et échelle 1 à 70 ms).Attribut popover et popovertarget, @starting-style, propriété scale, transition-behavior: allow-discrete, ancrage CSS (position-area) + 2,3 Ko de JavaScript, aucune dépendance. Mesuré dans Chromium 151, Firefox 153 et WebKit 26.5 : ouverture, ancrage à 10 px, Échap, clic extérieur, aria-expanded et deux bulles dans la même page identiques ; sortie animée seulement dans Chromium, seul à connaître la propriété overlay. Repères de version publics : API Popover depuis Chrome 114, Firefox 125 et Safari 17 ; @starting-style depuis Chrome 117, Firefox 129 et Safari 17.5 ; position-area depuis Chrome 129, Firefox 147 et Safari 26 — avant, le script place la bulle.
Sans @starting-style (Firefox 125-128, Safari 17.0-17.4), la bulle s'ouvre et se ferme d'un coup, sans éclosion. Sans ancrage CSS, le script la place au-dessus du bouton et la suit au défilement. Sans API Popover (Safari avant 17, Firefox avant 125), le script bascule une classe : la bulle éclot toujours et Échap la ferme, mais un clic à l'extérieur ne la ferme pas et elle peut passer sous un élément voisin. Sans JavaScript, le popover natif s'ouvre et se ferme quand même ; il ne manque que l'ancrage là où le navigateur ne le connaît pas, et aria-expanded.
Copiez les trois blocs dans votre page. Aucune dépendance.
<div class="popover-bloom-stage">
<button class="popover-bloom-btn" type="button" popovertarget="popoverBloomBox">Afficher la bulle</button>
<div class="popover-bloom-box" id="popoverBloomBox" popover>La bulle éclot depuis son ancre</div>
</div>
.popover-bloom-stage,
.popover-bloom-stage *,
.popover-bloom-stage *::before,
.popover-bloom-stage *::after {
box-sizing: border-box
}
.popover-bloom-stage {
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 250px;
overflow: hidden;
position: relative;
background: rgb(26, 26, 36);
color: rgb(255, 255, 255);
font-family: sans-serif;
}
.popover-bloom-btn {
padding: 10px 24px;
font-size: 0.85rem;
font-weight: 600;
border: none;
border-radius: 8px;
cursor: pointer;
font-family: inherit;
background: #6366f1;
color: white;
transition: background 0.2s, transform 0.1s;
}
.popover-bloom-btn:hover {
background: #4f46e5;
}
.popover-bloom-btn:active {
transform: scale(0.96);
}
/* La bulle est un popover : elle vit dans la couche supérieure (jamais coupée par un overflow: hidden,
jamais sous un z-index voisin) et se place au-dessus du bouton qui l'ouvre grâce à l'ancrage CSS
(position-area). Sans ancrage CSS, le script la place lui-même. */
.popover-bloom-box {
position: fixed;
inset: auto;
margin: 0 0 10px;
position-area: top;
border: 0;
overflow: visible;
background: linear-gradient(135deg, #6366f1, #8b5cf6);
color: white;
padding: 14px 22px;
border-radius: 12px;
font-family: sans-serif;
font-size: 0.85rem;
font-weight: 500;
white-space: nowrap;
box-shadow: 0 10px 40px rgba(99, 102, 241, 0.4);
opacity: 0;
scale: 0.6;
pointer-events: none;
transform-origin: bottom center;
transition: opacity 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
scale 0.4s cubic-bezier(0.34, 1.56, 0.64, 1),
display 0.4s allow-discrete,
overlay 0.4s allow-discrete;
}
.popover-bloom-box::after {
content: '';
position: absolute;
top: 100%;
left: 50%;
transform: translateX(-50%);
border: 8px solid transparent;
border-top-color: #8b5cf6;
}
/* Ouverte : état d'arrivée. @starting-style donne l'état de départ de l'entrée (le popover
passe de display: none à affiché, il n'a pas d'état « avant » sans cette règle). */
.popover-bloom-box:popover-open {
opacity: 1;
scale: 1;
pointer-events: auto;
}
/* Repli des navigateurs sans API Popover (règle séparée : un sélecteur :popover-open inconnu
invaliderait toute la liste). */
.popover-bloom-box.popover-bloom-open {
opacity: 1;
scale: 1;
pointer-events: auto;
}
@starting-style {
.popover-bloom-box:popover-open {
opacity: 0;
scale: 0.6;
}
}
@media (prefers-reduced-motion: reduce) {
.popover-bloom-box {
scale: 1;
transition: opacity 0.15s ease, display 0.15s allow-discrete, overlay 0.15s allow-discrete;
}
@starting-style {
.popover-bloom-box:popover-open {
scale: 1;
}
}
}
(function() {
/* Popover Bloom : l'attribut popovertarget ouvre et ferme la bulle sans script (Échap et clic
à l'extérieur compris). Ce script ajoute aria-expanded, ancre chaque bulle à son bouton et
prend le relais là où le navigateur ne connaît pas l'ancrage CSS ou l'API Popover. */
var hasPopover = HTMLElement.prototype.hasOwnProperty('popover');
var hasAnchor = !!(window.CSS && CSS.supports('position-area', 'top'));
var count = 0;
document.querySelectorAll('.popover-bloom-box').forEach(function(box) {
var btn = box.id && document.querySelector('[popovertarget="' + box.id + '"]');
if (!btn) return;
btn.setAttribute('aria-expanded', 'false');
function place() {
var r = btn.getBoundingClientRect();
box.style.left = (r.left + r.width / 2) + 'px';
box.style.top = (r.top - 10) + 'px';
box.style.translate = '-50% -100%';
box.style.margin = '0';
}
if (!hasPopover) {
/* Navigateur sans API Popover : bascule par classe, Échap ferme. */
box.setAttribute('aria-hidden', 'true');
var setOpen = function(open) {
if (open) place();
box.classList.toggle('popover-bloom-open', open);
box.setAttribute('aria-hidden', String(!open));
btn.setAttribute('aria-expanded', String(open));
};
btn.addEventListener('click', function() {
setOpen(!box.classList.contains('popover-bloom-open'));
});
document.addEventListener('keydown', function(e) {
if (e.key === 'Escape') setOpen(false);
});
return;
}
if (hasAnchor) {
/* Nom d'ancre propre à chaque bulle : plusieurs bulles cohabitent dans la page. */
var name = '--popover-bloom-' + (++count);
btn.style.setProperty('anchor-name', name);
box.style.setProperty('position-anchor', name);
}
box.addEventListener('beforetoggle', function(e) {
if (e.newState === 'open' && !hasAnchor) place();
});
box.addEventListener('toggle', function(e) {
btn.setAttribute('aria-expanded', String(e.newState === 'open'));
});
if (!hasAnchor) {
var follow = function() {
if (box.matches(':popover-open')) place();
};
window.addEventListener('scroll', follow, true);
window.addEventListener('resize', follow);
}
});
})();
Options passées à l'API ou attributs data-* :
| Option / propriété | Défaut | Effet |
|---|---|---|
Côté (CSS — position-area et transform-origin) |
top et bottom center : au-dessus, éclosion depuis la pointe |
bottom avec transform-origin: top center ouvre la bulle sous le bouton ; déplacez alors la flèche (::after en bottom: 100%, border-bottom-color). Le repli du script place toujours au-dessus : adaptez place() si vous changez de côté. |
Écart avec le bouton (CSS — margin: 0 0 10px) |
10 px | Gardez au moins la hauteur de la flèche (8 px). Le repli du script retire 10 px au haut du bouton : changez les deux valeurs ensemble. |
Rebond (CSS — cubic-bezier(0.34, 1.56, 0.64, 1), 0,4 s) |
la bulle monte à l'échelle 1,04 avant de se poser à 1 | cubic-bezier(0.2, 0, 0, 1) donne une ouverture nette sans rebond ; au-delà de 0,5 s, la bulle paraît lente pour une simple aide. |
Échelle de départ (CSS — scale: 0.6 dans la règle de base et dans @starting-style) |
0,6 | 0,85 pour une éclosion discrète, 0,3 pour une bulle qui sort presque d'un point. Changez les deux règles ensemble, sinon l'entrée et la sortie diffèrent. |
Fermeture (HTML — popover ou popover="manual") |
popover (auto) : Échap et clic extérieur ferment, une seule bulle ouverte à la fois |
popover="manual" laisse plusieurs bulles ouvertes ensemble et ne ferme qu'au bouton : utile pour une visite guidée, à éviter pour une aide ponctuelle. |
Texte long (CSS — white-space: nowrap) |
une ligne | Pour une définition, passez à white-space: normal avec une largeur fixe (240 à 300 px) : la bulle reste centrée sur le mot. |
Couleurs (CSS — background et border-top-color de la flèche) |
#6366f1 → #8b5cf6, flèche #8b5cf6 |
Donnez à la flèche la couleur de la fin du dégradé, sinon une marche apparaît sous la bulle. |
Pour s'animer en sortant, un popover doit rester affiché et rester dans la couche supérieure pendant qu'il rétrécit. Le code le demande avec display et overlay en allow-discrete. Chromium connaît les deux ; Firefox 153 et WebKit 26.5 ne connaissent pas overlay et retirent la bulle d'un coup (mesuré : opacité 0 dès 80 ms). L'entrée, elle, est animée partout grâce à @starting-style. Rien à changer de votre côté : la sortie s'animera quand ces navigateurs ajouteront overlay.
Morph Transition anime un élément qui reste dans la page et passe vraiment en display: none : un champ de formulaire, un bandeau, un panneau de filtres qui libèrent leur place. Popover Bloom ouvre une bulle par-dessus la page, ancrée à un bouton, avec la fermeture native du popover (Échap, clic extérieur). Choisissez Popover Bloom pour une information ponctuelle liée à un mot ou une case ; Morph Transition pour un bloc qui apparaît ou disparaît dans le flux.
Une div en position: absolute est coupée par le premier parent en overflow: hidden (une carte, un tableau qui défile) et peut passer sous un voisin qui a un z-index. Le popover s'affiche dans la couche supérieure : rien ne le coupe ni ne le recouvre. En prime, le navigateur gère Échap et le clic à l'extérieur, et une seule bulle « auto » reste ouverte à la fois. Le prix : sans ancrage CSS, il faut le placer en JavaScript, ce que fait le script fourni.