ContentWithStats content
Tekst + grote statistieken naast elkaar. Cijfers in accent kleur, dunne scheidingslijn. Sterk voor social proof in context.
src/components/content/ContentWithStats.astro
---
interface Stat {
value: string;
label: string;
description?: string;
}
interface Props {
eyebrow?: string;
headline: string;
body: string;
image?: string;
stats: Stat[];
ctaLabel?: string;
ctaHref?: string;
}
const {
eyebrow,
headline,
body,
image,
stats = [],
ctaLabel,
ctaHref = '#',
} = Astro.props;
---
<section class="cws__section">
<div class="cws__inner">
<!-- Left: text -->
<div class="cws__text cws-reveal cws-reveal--left">
{eyebrow && <p class="cws__eyebrow">{eyebrow}</p>}
<h2 class="cws__headline" set:html={headline} />
<p class="cws__body">{body}</p>
{ctaLabel && (
<a href={ctaHref} class="cws__cta">{ctaLabel}</a>
)}
</div>
<!-- Right: stats -->
<div class="cws__stats-col cws-reveal cws-reveal--right">
{image && (
<div class="cws__thumb">
<img src={image} alt="" class="cws__thumb-img" loading="lazy" decoding="async" />
</div>
)}
<div class="cws__stats">
{stats.map((stat, i) => (
<div class="cws__stat">
<span class="cws__stat-value">{stat.value}</span>
<span class="cws__stat-label">{stat.label}</span>
{stat.description && (
<span class="cws__stat-desc">{stat.description}</span>
)}
{i < stats.length - 1 && <hr class="cws__stat-rule" />}
</div>
))}
</div>
</div>
</div>
</section>
<script>
const reveals = document.querySelectorAll<HTMLElement>('.cws-reveal');
const observer = new IntersectionObserver(
(entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
(entry.target as HTMLElement).classList.add('cws-reveal--visible');
observer.unobserve(entry.target);
}
});
},
{ threshold: 0.2 }
);
reveals.forEach((el) => observer.observe(el));
</script>
<style>
:root {
--color-primary: #0a0a0a;
--color-accent: #6366f1;
--color-bg: #fff;
--color-muted: #6b7280;
--radius: 0.5rem;
}
.cws__section {
background: var(--color-bg);
padding: clamp(3rem, 8vw, 7rem) clamp(1rem, 5vw, 2rem);
}
.cws__inner {
display: flex;
align-items: flex-start;
gap: clamp(2.5rem, 6vw, 6rem);
max-width: 1200px;
margin: 0 auto;
}
/* Left column */
.cws__text {
flex: 1 1 45%;
min-width: 0;
}
.cws__eyebrow {
font-size: clamp(0.65rem, 1.2vw, 0.75rem);
font-weight: 700;
letter-spacing: 0.12em;
text-transform: uppercase;
color: var(--color-accent);
margin: 0 0 0.75rem;
}
.cws__headline {
font-size: clamp(2rem, 3.5vw, 3rem);
font-weight: 800;
line-height: 1.15;
color: var(--color-primary);
margin: 0 0 1.25rem;
}
.cws__headline :global(em) {
font-style: italic;
color: var(--color-accent);
}
.cws__body {
font-size: clamp(1rem, 1.5vw, 1.125rem);
line-height: 1.7;
color: var(--color-muted);
margin: 0 0 2rem;
}
.cws__cta {
display: inline-flex;
align-items: center;
padding: 0.75rem 1.5rem;
border-radius: var(--radius);
font-size: 0.9375rem;
font-weight: 600;
text-decoration: none;
background: var(--color-accent);
color: #fff;
transition: opacity 0.2s ease, transform 0.2s ease;
}
.cws__cta:hover {
opacity: 0.85;
transform: translateY(-1px);
}
/* Right column */
.cws__stats-col {
flex: 1 1 45%;
min-width: 0;
}
.cws__thumb {
margin-bottom: 1.5rem;
border-radius: var(--radius);
overflow: hidden;
}
.cws__thumb-img {
display: block;
width: 100%;
height: 180px;
object-fit: cover;
border-radius: var(--radius);
}
.cws__stats {
display: flex;
flex-direction: column;
gap: 0;
}
.cws__stat {
display: flex;
flex-direction: column;
padding: 1.25rem 0;
}
.cws__stat-value {
font-size: clamp(2.5rem, 4vw, 4rem);
font-weight: 900;
line-height: 1;
color: var(--color-accent);
letter-spacing: -0.02em;
}
.cws__stat-label {
font-size: clamp(0.9rem, 1.2vw, 1rem);
font-weight: 700;
color: var(--color-primary);
margin-top: 0.25rem;
}
.cws__stat-desc {
font-size: 0.875rem;
color: var(--color-muted);
margin-top: 0.25rem;
line-height: 1.5;
}
.cws__stat-rule {
border: none;
border-top: 1px solid rgba(0, 0, 0, 0.08);
margin: 0;
}
/* Scroll reveal */
.cws-reveal {
opacity: 0;
transition: opacity 0.7s ease, transform 0.7s ease;
}
.cws-reveal--left {
transform: translateX(-2rem);
}
.cws-reveal--right {
transform: translateX(2rem);
transition-delay: 0.12s;
}
.cws-reveal--visible {
opacity: 1;
transform: translateX(0);
}
/* Responsive */
@media (max-width: 768px) {
.cws__inner {
flex-direction: column;
}
.cws__text,
.cws__stats-col {
flex: 1 1 100%;
width: 100%;
}
.cws__stats {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 1.25rem;
}
.cws__stat {
padding: 0;
}
.cws__stat-rule {
display: none;
}
.cws-reveal--left,
.cws-reveal--right {
transform: translateY(1.5rem);
}
.cws-reveal--visible {
transform: translateY(0);
}
}
@media (max-width: 480px) {
.cws__stats {
grid-template-columns: 1fr;
}
}
@media (prefers-reduced-motion: reduce) {
* {
animation: none !important;
transition: none !important;
}
}
</style>
Props
| Prop | Type | Default | Beschrijving |
|---|---|---|---|
headline * | string | — | Sectie headline |
body * | string | — | Tekst blok |
stats * | { value: string; label: string; description?: string }[] | — | Statistieken rechts |
eyebrow | string | — | Label boven headline |
ctaLabel | string | — | CTA tekst |
* = verplicht