interactions · scroll · reveal

Apparition au scroll

Chaque bloc apparaît en fondu + léger glissement quand il entre dans l'écran pendant le scroll. Utilise IntersectionObserver.

Effets

Voir le code source
<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>