Zoeken...  ⌘K GitHub

HeroSplitDark Hero

Donkere split-hero: tekstpaneel met kicker, accent-kop, sub en twee CTA's links; full-bleed beeld met caption rechts.

/hero-split-dark
src/components/hero/HeroSplitDark.astro
---
/**
 * HeroSplitDark — donkere split-hero: links een tekstpaneel met kicker,
 * statement-kop met accent-woord, sub en twee CTA's; rechts een full-bleed
 * beeld met caption. Verticale marquee op de paneelgrens. Alleen dark.
 *
 * Props:
 * - kicker?: string
 * - headline?: string
 * - accent?: string            accent-woord binnen de kop (cursief gemarkeerd via <em>)
 * - sub?: string
 * - ctaPrimary?: { label: string; href: string }
 * - ctaSecondary?: { label: string; href: string }
 * - image?: string             src van het rechterbeeld
 * - imageAlt?: string
 * - caption?: string           caption-tekst rechtsonder op het beeld
 * - marquee?: string           tekst voor de verticale rand-marquee
 */
interface Props {
  kicker?: string;
  headline?: string;
  accent?: string;
  sub?: string;
  ctaPrimary?: { label: string; href: string };
  ctaSecondary?: { label: string; href: string };
  image?: string;
  imageAlt?: string;
  caption?: string;
  marquee?: string;
}

const {
  kicker = 'Online Marketing Bureau',
  headline = 'Resultaten die',
  accent = 'spreken',
  sub = 'Data-gedreven marketing zonder bullshit. Wij leveren ROAS, leads en groei, niet alleen rapporten.',
  ctaPrimary = { label: 'Ontdek meer', href: '#' },
  ctaSecondary = { label: 'Cases bekijken', href: '#' },
  image = '/img/ext/photo-1504384308090-c894fdcc538d-w1200-7ef4c6.jpg',
  imageAlt = 'Marketing resultaten',
  caption = 'Gemiddeld 4.2× ROAS voor onze klanten',
  marquee = 'Premium · Quality · Design · ',
} = Astro.props;
---

<section class:list={['bl-section', 'hsd']}>
  <div class="bl-inner hsd-inner">
    <!-- Left dark panel -->
    <div class="hsd__left">
      <div class="hsd__left-content">
        {kicker && (
          <p class="hsd__kicker">
            <span class="hsd__kicker-line"></span>
            {kicker}
          </p>
        )}
        <h1 class="hsd__headline">{headline}{accent && <> <em>{accent}</em></>}</h1>
        {sub && <p class="hsd__sub">{sub}</p>}
        {(ctaPrimary || ctaSecondary) && (
          <div class="hsd__actions">
            {ctaPrimary && <a href={ctaPrimary.href} class="hsd__cta">{ctaPrimary.label}</a>}
            {ctaSecondary && (
              <a href={ctaSecondary.href} class="hsd__secondary">
                {ctaSecondary.label}
                <svg width="14" height="14" viewBox="0 0 14 14" fill="none">
                  <path d="M2 7h10M8 3l4 4-4 4" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"></path>
                </svg>
              </a>
            )}
          </div>
        )}
      </div>
      {marquee && (
        <div class="hsd__v-label" aria-hidden="true">
          <span class="hsd__v-label-track">
            {Array.from({ length: 6 }).map(() => <span>{marquee}</span>)}
          </span>
        </div>
      )}
    </div>
    <!-- Right image panel -->
    <div class="hsd__right">
      <div class="hsd__img-wrap">
        {image && (
          <img data-allow-img src={image} alt={imageAlt} class="hsd__img" loading="eager" fetchpriority="high" decoding="async" />
        )}
        <div class="hsd__vignette"></div>
      </div>
      {caption && (
        <div class="hsd__caption">
          <span class="hsd__caption-dot"></span>
          {caption}
        </div>
      )}
    </div>
  </div>
</section>

<style>
.hsd{background:#080810;color:#e8e8e8;overflow:hidden}
.hsd-inner{display:grid;grid-template-columns:1fr 1fr;gap:0;align-items:stretch}
.hsd__left{display:flex;align-items:stretch;position:relative;overflow:hidden}
.hsd__left-content{flex:1;display:flex;flex-direction:column;justify-content:center;padding:5rem 4rem 5rem 0;max-width:580px}
.hsd__kicker{display:flex;align-items:center;gap:.875rem;font-size:.75rem;font-weight:700;letter-spacing:.14em;text-transform:uppercase;color:#999;margin-bottom:1.75rem;animation:hsd-fade-up .5s .1s cubic-bezier(.22,1,.36,1) both}
.hsd__kicker-line{width:24px;height:1px;background:var(--color-accent, #6366f1);flex-shrink:0}
.hsd__headline{font-size:clamp(2.75rem, 5.5vw, 5rem);font-weight:900;line-height:.97;letter-spacing:-.045em;color:#fff;margin-bottom:1.5rem;animation:hsd-fade-up .7s .2s cubic-bezier(.22,1,.36,1) both}
.hsd__headline em{font-style:normal;color:var(--color-accent, #6366f1)}
.hsd__sub{font-size:1.0625rem;line-height:1.7;color:#aaa;max-width:40ch;margin-bottom:2.5rem;animation:hsd-fade-up .6s .35s cubic-bezier(.22,1,.36,1) both}
.hsd__actions{display:flex;align-items:center;gap:1.5rem;animation:hsd-fade-up .6s .45s cubic-bezier(.22,1,.36,1) both}
.hsd__cta{display:inline-flex;align-items:center;background:#fff;color:#080810;padding:.9375rem 2rem;border-radius:var(--radius, .5rem);font-weight:800;font-size:.9375rem;text-decoration:none;transition:background .2s,transform .2s}
.hsd__cta:hover{background:var(--color-accent, #6366f1);color:#fff;transform:translateY(-2px)}
.hsd__secondary{display:inline-flex;align-items:center;gap:.4rem;font-size:.9375rem;font-weight:600;color:#aaa;text-decoration:none;transition:color .2s,gap .2s}
.hsd__secondary:hover{color:#fff;gap:.65rem}
.hsd__v-label{width:32px;display:flex;align-items:center;justify-content:center;border-left:1px solid rgba(255,255,255,.06);overflow:hidden;flex-shrink:0}
.hsd__v-label-track{display:flex;flex-direction:column;white-space:nowrap;font-size:.6875rem;letter-spacing:.12em;text-transform:uppercase;color:#666;writing-mode:vertical-lr;animation:hsd-scroll-up 20s linear infinite;gap:1rem}
.hsd__right{position:relative;background:#0d0d18;overflow:hidden;min-height:520px;animation:hsd-scale-in 1s .1s cubic-bezier(.22,1,.36,1) both}
.hsd__img-wrap{position:absolute;inset:0}
.hsd__img{width:100%;height:100%;object-fit:cover;display:block;opacity:.85;mix-blend-mode:luminosity}
.hsd__right:hover .hsd__img{mix-blend-mode:normal;transition:mix-blend-mode 0s}
.hsd__vignette{position:absolute;inset:0;background:linear-gradient(to right,#080810 0%,transparent 20%),linear-gradient(to top,#080810 0%,transparent 20%);pointer-events:none}
.hsd__caption{position:absolute;bottom:1.5rem;right:1.5rem;z-index:1;display:flex;align-items:center;gap:.5rem;font-size:.8125rem;color:#aaa;letter-spacing:.05em}
.hsd__caption-dot{width:6px;height:6px;border-radius:50%;background:var(--color-accent, #6366f1);flex-shrink:0}

@keyframes hsd-fade-up{from{opacity:0;transform:translateY(16px)}to{opacity:1;transform:none}}
@keyframes hsd-scale-in{from{opacity:0;transform:scale(1.04)}to{opacity:1;transform:none}}
@keyframes hsd-scroll-up{from{transform:translateY(0)}to{transform:translateY(-50%)}}

@media (max-width: 768px){
  .hsd__kicker,.hsd__headline,.hsd__sub,.hsd__actions,.hsd__right,.hsd__v-label-track{animation:none}
  .hsd-inner{grid-template-columns:1fr}
  .hsd__right{height:55vw;min-height:280px;order:-1}
  .hsd__v-label{display:none}
  .hsd__left-content{padding:3rem 0;max-width:none}
}
</style>

Props

Prop Type Default Beschrijving
kicker string 'Online Marketing Bureau' Kicker-label boven de kop
headline string 'Resultaten die' H1-kop
accent string 'spreken' Accent-woord (gekleurd) binnen de kop
sub string - Ondertitel
ctaPrimary { label: string; href: string } - Primaire CTA-knop
ctaSecondary { label: string; href: string } - Secundaire tekst-CTA met pijl
image string - Src van het rechterbeeld
imageAlt string - Alt-tekst van het beeld
caption string - Caption rechtsonder op het beeld
marquee string - Tekst voor de verticale rand-marquee

* = verplicht