sections · pricing
3 offres tarifaires en cartes, dont une mise en avant, qui apparaissent en fondu au scroll. Même mécanisme que 'Pricing avec fondu au scroll' (liste), en version cartes.
Effets
<style>
.bn-pricing-cards {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
gap: 1.25rem;
align-items: start;
padding: 2rem;
font-family: 'Inter', sans-serif;
background: #0B0F10;
}
/* Fondu par défaut (peu visible), amené à 1 par la classe
"is-visible" que script.js ajoute au scroll */
.bn-pricing-cards__row {
opacity: 0.3;
transform: translateY(16px);
transition: opacity 0.6s ease, transform 0.6s ease;
}
.bn-pricing-cards.fx-fade-scroll .bn-pricing-cards__row.is-visible {
opacity: 1;
transform: translateY(0);
}
.bn-pricing-cards:not(.fx-fade-scroll) .bn-pricing-cards__row {
opacity: 1;
transform: none;
}
.bn-pricing-cards__card {
position: relative;
padding: 1.75rem;
border: 1px solid rgba(230, 228, 221, 0.12);
border-radius: 10px;
}
.bn-pricing-cards__card--highlight {
border-color: #E6E4DD;
background: rgba(230, 228, 221, 0.03);
}
.bn-pricing-cards__badge {
position: absolute;
top: -11px;
left: 50%;
transform: translateX(-50%);
padding: 0.2rem 0.8rem;
border-radius: 999px;
background: #E6E4DD;
color: #0B0F10;
font-size: 0.65rem;
font-weight: 700;
letter-spacing: 0.04em;
text-transform: uppercase;
}
.bn-pricing-cards__card h3 {
color: #E6E4DD;
font-size: 1rem;
margin: 0 0 0.75rem;
}
.bn-pricing-cards__price {
color: #E6E4DD;
font-size: 1.8rem;
font-weight: 700;
margin: 0 0 1.25rem;
}
.bn-pricing-cards__price span {
font-size: 0.8rem;
font-weight: 400;
color: #5C6160;
}
.bn-pricing-cards__card ul {
list-style: none;
margin: 0 0 1.5rem;
padding: 0;
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.bn-pricing-cards__card li {
color: #979793;
font-size: 0.8rem;
}
.bn-pricing-cards__btn {
display: block;
text-align: center;
padding: 0.65rem;
border: 1px solid rgba(230, 228, 221, 0.3);
border-radius: 999px;
color: #E6E4DD;
text-decoration: none;
font-size: 0.82rem;
font-weight: 600;
}
.bn-pricing-cards__btn:hover {
border-color: #E6E4DD;
}
.bn-pricing-cards__btn--primary {
background: #E6E4DD;
color: #0B0F10;
border-color: #E6E4DD;
}
</style>
<section class="bn-pricing-cards fx-fade-scroll" data-fx-root>
<article class="bn-pricing-cards__card bn-pricing-cards__row">
<h3>Basic</h3>
<p class="bn-pricing-cards__price">0€<span>/mois</span></p>
<ul>
<li>✓ Accès aux cours gratuits</li>
<li>✓ Accès communauté</li>
<li>✓ Projets limités</li>
</ul>
<a href="#" class="bn-pricing-cards__btn">Commencer</a>
</article>
<article class="bn-pricing-cards__card bn-pricing-cards__card--highlight bn-pricing-cards__row">
<span class="bn-pricing-cards__badge">Populaire</span>
<h3>Pro</h3>
<p class="bn-pricing-cards__price">19€<span>/mois</span></p>
<ul>
<li>✓ Tous les cours & contenus</li>
<li>✓ Retours sur projets</li>
<li>✓ Certificats</li>
<li>✓ Support prioritaire</li>
</ul>
<a href="#" class="bn-pricing-cards__btn bn-pricing-cards__btn--primary">Essai gratuit</a>
</article>
<article class="bn-pricing-cards__card bn-pricing-cards__row">
<h3>Équipe</h3>
<p class="bn-pricing-cards__price">49€<span>/mois</span></p>
<ul>
<li>✓ Tout Pro inclus</li>
<li>✓ Gestion d'équipe</li>
<li>✓ Analytique avancée</li>
<li>✓ Branding personnalisé</li>
</ul>
<a href="#" class="bn-pricing-cards__btn">Contacter les ventes</a>
</article>
</section>
<script>
/* ==========================================================
PRICING (CARTES) — FONDU AU SCROLL
Même mécanisme exactement que "Pricing avec fondu au scroll"
(liste) : écoute le scroll de la vraie page contenant ce
composant, révèle chaque carte quand elle entre dans l'écran.
========================================================== */
(function () {
const root = document.querySelector('.bn-pricing-cards[data-fx-root]');
if (!root) return;
const cards = document.querySelectorAll('.bn-pricing-cards__row');
const isInsideFrame = Boolean(window.frameElement);
const scrollTarget = isInsideFrame ? window.parent : window;
function frameOffsetTop() {
return isInsideFrame ? window.frameElement.getBoundingClientRect().top : 0;
}
function viewportHeight() {
return isInsideFrame ? window.parent.innerHeight : window.innerHeight;
}
function update() {
if (!root.classList.contains('fx-fade-scroll')) {
cards.forEach((card) => card.classList.add('is-visible'));
return;
}
const offset = frameOffsetTop();
cards.forEach((card) => {
const cardTop = offset + card.getBoundingClientRect().top;
card.classList.toggle('is-visible', cardTop < viewportHeight() * 0.9);
});
}
scrollTarget.addEventListener('scroll', update, { passive: true });
window.addEventListener('resize', update);
update();
})();
</script>