interactions · scroll · reveal
Chaque bloc apparaît en fondu + léger glissement quand il entre dans l'écran pendant le scroll. Utilise IntersectionObserver.
Effets
<style>
.bn-reveal-demo {
padding: 2.5rem;
font-family: 'Inter', sans-serif;
background: #0B0F10;
display: flex;
flex-direction: column;
gap: 1.5rem;
}
.bn-reveal-demo__hint {
color: #979793;
font-size: 0.85rem;
margin: 0 0 1rem;
}
.bn-reveal-item {
padding: 1.5rem;
border: 1px solid rgba(230, 228, 221, 0.1);
border-radius: 8px;
/* État caché par défaut : légèrement décalé vers le bas et
transparent. La classe "is-visible" (posée par script.js quand
le bloc entre dans l'écran) l'amène à sa position normale. */
opacity: 0;
transform: translateY(24px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.bn-reveal-demo.fx-fade-scroll .bn-reveal-item.is-visible {
opacity: 1;
transform: translateY(0);
}
/* Effet désactivé : tout reste visible sans animation */
.bn-reveal-demo:not(.fx-fade-scroll) .bn-reveal-item {
opacity: 1;
transform: none;
}
.bn-reveal-item__kicker {
display: block;
font-family: 'Bebas Neue', sans-serif;
font-size: 1rem;
color: #979793;
margin-bottom: 0.4rem;
}
.bn-reveal-item h3 {
font-family: 'Bebas Neue', sans-serif;
font-size: 1.3rem;
letter-spacing: 0.02em;
color: #E6E4DD;
margin: 0 0 0.4rem;
}
.bn-reveal-item p {
color: #979793;
font-size: 0.85rem;
line-height: 1.5;
margin: 0;
}
</style>
<div class="bn-reveal-demo fx-fade-scroll" data-fx-root>
<p class="bn-reveal-demo__hint">
Scrollez cette page : chaque bloc apparaît quand il entre dans l'écran.
</p>
<div class="bn-reveal-item">
<span class="bn-reveal-item__kicker">01</span>
<h3>Une exploration permanente</h3>
<p>Chaque projet est une tentative de saisir l'invisible.</p>
</div>
<div class="bn-reveal-item">
<span class="bn-reveal-item__kicker">02</span>
<h3>Questions plutôt que réponses</h3>
<p>Nous ne cherchons pas à tout expliquer, nous préférons ouvrir des pistes.</p>
</div>
<div class="bn-reveal-item">
<span class="bn-reveal-item__kicker">03</span>
<h3>Créer autrement</h3>
<p>Donner forme à ce qui n'existe pas encore, sans réponse toute faite.</p>
</div>
</div>
<script>
/* ==========================================================
APPARITION AU SCROLL
Sur un VRAI site, ce script utilise IntersectionObserver — la
façon standard et performante de détecter qu'un élément entre
dans l'écran.
DANS CET APERÇU uniquement (affiché à l'intérieur d'une petite
iframe qui ne scrolle jamais elle-même), IntersectionObserver ne
peut pas fonctionner normalement : on bascule alors sur le même
calcul de position que le parallax et le fondu au scroll du
pricing, basé sur le scroll de la VRAIE page qui contient
l'aperçu. Le code copié pour un vrai site n'a pas ce problème.
========================================================== */
(function () {
const root = document.querySelector('.bn-reveal-demo[data-fx-root]');
if (!root) return;
const items = root.querySelectorAll('.bn-reveal-item');
const isInsideFrame = Boolean(window.frameElement);
if (!isInsideFrame) {
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) entry.target.classList.add('is-visible');
});
},
{ threshold: 0.2 }
);
items.forEach((item) => observer.observe(item));
return;
}
function update() {
if (!root.classList.contains('fx-fade-scroll')) {
items.forEach((item) => item.classList.add('is-visible'));
return;
}
const frameTop = window.frameElement.getBoundingClientRect().top;
const viewportHeight = window.parent.innerHeight;
items.forEach((item) => {
const itemTop = frameTop + item.getBoundingClientRect().top;
const isVisible = itemTop < viewportHeight * 0.85;
item.classList.toggle('is-visible', isVisible);
});
}
window.parent.addEventListener('scroll', update, { passive: true });
window.addEventListener('resize', update);
update();
})();
</script>