Zoeken...  ⌘K GitHub

HeroVideoGrid Hero

Donkere hero met mosaic image grid rechts. Stats onderaan, accent kleur, vertikal uitlijning.

/hero-video-grid
src/components/hero/HeroVideoGrid.astro
---
/**
 * HeroVideoGrid
 * Hero met headline links en mosaic image grid rechts.
 * Modern Retail — metrisch grid, accent kleur, stat badges.
 */
interface Props {
  eyebrow?: string;
  headline: string;
  sub: string;
  ctaLabel?: string;
  ctaHref?: string;
  stats?: { value: string; label: string }[];
  images: string[];
}

const {
  eyebrow,
  headline,
  sub,
  ctaLabel = 'Bekijk het werk',
  ctaHref = '#',
  stats = [],
  images = [],
} = Astro.props;

const gridImages = images.slice(0, 4);
---

<section class="hvg" data-component="hero-video-grid">
  <div class="hvg__inner">
    <!-- Left -->
    <div class="hvg__left">
      {eyebrow && <p class="hvg__eyebrow">{eyebrow}</p>}
      <h1 class="hvg__headline" set:html={headline} />
      <p class="hvg__sub">{sub}</p>
      <a href={ctaHref} class="hvg__cta">{ctaLabel} →</a>

      {stats.length > 0 && (
        <div class="hvg__stats">
          {stats.map(s => (
            <div class="hvg__stat">
              <div class="hvg__stat-value">{s.value}</div>
              <div class="hvg__stat-label">{s.label}</div>
            </div>
          ))}
        </div>
      )}
    </div>

    <!-- Right: mosaic grid -->
    <div class="hvg__grid">
      {gridImages.map((src, i) => (
        <div class:list={['hvg__cell', `hvg__cell--${i + 1}`]}>
          <img src={src} alt="" loading="eager" />
        </div>
      ))}
    </div>
  </div>
</section>

<style>
  .hvg {
    background: var(--color-primary, #0a0a0a);
    color: #fff;
    padding: 5rem 1.5rem;
    overflow: hidden;
  }

  .hvg__inner {
    max-width: 1200px;
    margin: 0 auto;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4rem;
    align-items: center;
  }

  .hvg__eyebrow {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--color-accent, #6366f1);
    margin-bottom: 1rem;
  }

  .hvg__headline {
    font-size: clamp(2.5rem, 4.5vw, 4rem);
    font-weight: 800;
    line-height: 1.05;
    letter-spacing: -0.04em;
    color: #fff;
    margin-bottom: 1.25rem;
  }

  .hvg__headline :global(em) {
    font-style: normal;
    color: var(--color-accent, #6366f1);
  }

  .hvg__sub {
    font-size: 1.0625rem;
    line-height: 1.65;
    color: rgba(255,255,255,0.65);
    margin-bottom: 2rem;
    max-width: 40ch;
  }

  .hvg__cta {
    display: inline-block;
    background: var(--color-accent, #6366f1);
    color: #fff;
    text-decoration: none;
    font-weight: 700;
    font-size: 0.9375rem;
    padding: 0.875rem 1.75rem;
    border-radius: var(--radius, 0.5rem);
    transition: filter 0.2s, transform 0.2s;
    margin-bottom: 2.5rem;
  }

  .hvg__cta:hover {
    filter: brightness(1.15);
    transform: translateY(-1px);
  }

  .hvg__stats {
    display: flex;
    gap: 2.5rem;
    padding-top: 1.5rem;
    border-top: 1px solid rgba(255,255,255,0.1);
  }

  .hvg__stat-value {
    font-size: 1.875rem;
    font-weight: 800;
    color: #fff;
    letter-spacing: -0.03em;
  }

  .hvg__stat-label {
    font-size: 0.8125rem;
    color: rgba(255,255,255,0.5);
    margin-top: 0.25rem;
  }

  /* Mosaic grid */
  .hvg__grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    grid-template-rows: 240px 240px;
    gap: 0.75rem;
  }

  .hvg__cell {
    border-radius: calc(var(--radius, 0.5rem) * 1.5);
    overflow: hidden;
  }

  .hvg__cell img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s ease;
  }

  .hvg__cell:hover img {
    transform: scale(1.04);
  }

  /* Tall first cell */
  .hvg__cell--1 {
    grid-row: 1 / 3;
    grid-column: 1;
  }

  @media (max-width: 768px) {
    .hvg {
      padding: 3.5rem 1.25rem;
    }

    .hvg__inner {
      grid-template-columns: 1fr;
      gap: 2.5rem;
    }

    .hvg__grid {
      grid-template-rows: 180px 180px;
    }

    .hvg__cell--1 {
      grid-row: 1 / 3;
    }

    .hvg__stats {
      gap: 1.5rem;
    }
  }
</style>

<script>
  document.addEventListener('DOMContentLoaded', () => {
    if (typeof gsap === 'undefined') return;
    gsap.from('[data-component="hero-video-grid"] .hvg__eyebrow, [data-component="hero-video-grid"] .hvg__headline, [data-component="hero-video-grid"] .hvg__sub, [data-component="hero-video-grid"] .hvg__cta', {
      opacity: 0,
      y: 30,
      duration: 0.6,
      stagger: 0.1,
      ease: 'power2.out',
    });
    gsap.from('[data-component="hero-video-grid"] .hvg__cell', {
      opacity: 0,
      scale: 0.92,
      duration: 0.7,
      stagger: 0.12,
      delay: 0.3,
      ease: 'power2.out',
    });
  });
</script>

Props

Prop Type Default Beschrijving
headline * string H1 — gebruik <em> voor accentkleur
sub * string Subtitel
images * string[] Array van 4 afbeelding-URLs voor mosaic grid
eyebrow string Kleine tekst boven headline
stats { value: string; label: string }[] Statistieken onderaan

* = verplicht