Zoeken...  ⌘K GitHub

HeroGradient Hero

Lichte gradient-hero met zwevende blobs: badge-pill, kop met gradient-accent, sub, CTA + tekstlink en social-proof.

/hero-gradient
src/components/hero/HeroGradient.astro
---
/**
 * HeroGradient
 * Lichte gradient-hero met zwevende blobs: badge-pill, kop met gradient-accent,
 * sub, primaire CTA + tekstlink, en social-proof (avatars + tekst). Centreert
 * binnen de pagina-rail (.bl-inner).
 *
 * Props: zie interface. Alle copy is prop-driven; gradient instelbaar via tokens.
 */
interface Props {
  badge?: string;
  headline: string;
  accent?: string;
  sub?: string;
  primaryCta?: { label: string; href: string };
  secondaryCta?: { label: string; href: string };
  proofText?: string;
  proofCount?: number;
  from?: string;
  to?: string;
  blob?: string;
}

const {
  badge,
  headline,
  accent,
  sub,
  primaryCta,
  secondaryCta,
  proofText,
  proofCount = 4,
  from = '#f5f3ff',
  to = '#ede9fe',
  blob = '#818cf8',
} = Astro.props;

const avatarHues = [220, 290, 360, 430];
---

<section class:list={['bl-section', 'hg']} data-component="hero-gradient" style={`--hg-from:${from};--hg-to:${to};--hg-blob:${blob};`}>
  <div class="hg__blobs" aria-hidden="true">
    <div class="hg__blob hg__blob--1"></div>
    <div class="hg__blob hg__blob--2"></div>
    <div class="hg__blob hg__blob--3"></div>
  </div>
  <div class="hg__noise" aria-hidden="true"></div>
  <div class="bl-inner bl-inner--narrow hg__content">
    {badge && (
      <div class="hg__badge-wrap">
        <span class="hg__badge">
          <span class="hg__badge-dot"></span>
          {badge}
        </span>
      </div>
    )}
    <h1 class="hg__headline">{headline}{accent && <em> {accent}</em>}</h1>
    {sub && <p class="hg__sub">{sub}</p>}
    {(primaryCta || secondaryCta) && (
      <div class="hg__actions">
        {primaryCta && <a href={primaryCta.href} class="hg__cta">{primaryCta.label}</a>}
        {secondaryCta && (
          <a href={secondaryCta.href} class="hg__secondary">
            {secondaryCta.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>
    )}
    {proofText && (
      <div class="hg__proof">
        <div class="hg__avatars">
          {avatarHues.slice(0, Math.min(proofCount, 4)).map((hue) => (
            <div class="hg__avatar" style={`background:hsl(${hue},60%,65%)`}></div>
          ))}
        </div>
        <p class="hg__proof-text" set:html={proofText}></p>
      </div>
    )}
  </div>
</section>

<style>
.hg{min-height:100vh;position:relative;display:flex;align-items:center;justify-content:center;overflow:hidden;background:linear-gradient(135deg,var(--hg-from, #f5f3ff) 0%,var(--hg-to, #ede9fe) 100%)}
.hg__blobs{position:absolute;inset:0;pointer-events:none}
.hg__blob{position:absolute;border-radius:50%;filter:blur(72px);opacity:.45}
.hg__blob--1{width:55vw;height:55vw;background:var(--hg-blob, #818cf8);top:-20%;left:-15%;animation:hg-float-1 18s ease-in-out infinite}
.hg__blob--2{width:40vw;height:40vw;background:#a78bfa;bottom:-10%;right:-10%;animation:hg-float-2 22s ease-in-out infinite}
.hg__blob--3{width:30vw;height:30vw;background:#f9a8d4;top:40%;right:20%;animation:hg-float-3 16s ease-in-out infinite}
.hg__noise{position:absolute;inset:0;opacity:.035;background-image:url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)'/%3E%3C/svg%3E");background-size:256px;pointer-events:none}
.hg__content{position:relative;z-index:1;text-align:center;display:flex;flex-direction:column;align-items:center;gap:0}
.hg__badge-wrap{margin-bottom:1.75rem;animation:hg-fade-up .5s .1s cubic-bezier(.22,1,.36,1) both}
.hg__badge{display:inline-flex;align-items:center;gap:.5rem;background:#ffffffa6;backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,.8);padding:.4375rem 1rem;border-radius:999px;font-size:.8125rem;font-weight:600;color:var(--color-primary, #0a0a0a);box-shadow:0 2px 8px #0000000f}
.hg__badge-dot{width:6px;height:6px;background:var(--color-accent, #6366f1);border-radius:50%;flex-shrink:0;animation:hg-pulse 2s ease-in-out infinite}
.hg__headline{font-size:clamp(3rem, 5vw, 5rem);font-weight:900;line-height:1;letter-spacing:-.05em;color:var(--color-primary, #0a0a0a);margin-bottom:1.5rem;animation:hg-fade-up .7s .2s cubic-bezier(.22,1,.36,1) both}
.hg__headline em{font-style:normal;background:linear-gradient(135deg,var(--color-accent, #6366f1),#a855f7);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
.hg__sub{font-size:1.125rem;line-height:1.65;color:#0a0a0acc;max-width:44ch;margin-bottom:2.5rem;animation:hg-fade-up .6s .35s cubic-bezier(.22,1,.36,1) both}
.hg__actions{display:flex;align-items:center;gap:1.25rem;flex-wrap:wrap;justify-content:center;margin-bottom:2.5rem;animation:hg-fade-up .6s .45s cubic-bezier(.22,1,.36,1) both}
.hg__cta{display:inline-flex;align-items:center;background:var(--color-primary, #0a0a0a);color:#fff;padding:1rem 2.25rem;border-radius:var(--radius, .5rem);font-weight:700;font-size:.9375rem;text-decoration:none;box-shadow:0 4px 24px #0000002e;transition:transform .2s,box-shadow .2s}
.hg__cta:hover{transform:translateY(-3px);box-shadow:0 8px 32px #00000038}
.hg__secondary{display:inline-flex;align-items:center;gap:.4rem;font-size:.9375rem;font-weight:600;color:var(--color-primary, #0a0a0a);text-decoration:none;opacity:.7;transition:opacity .2s,gap .2s}
.hg__secondary:hover{opacity:1;gap:.65rem}
.hg__proof{display:flex;align-items:center;gap:.875rem;animation:hg-fade-up .5s .6s cubic-bezier(.22,1,.36,1) both}
.hg__avatars{display:flex}
.hg__avatar{width:32px;height:32px;border-radius:50%;border:2px solid rgba(255,255,255,.9);margin-left:-8px;flex-shrink:0}
.hg__avatars .hg__avatar:first-child{margin-left:0}
.hg__proof-text{font-size:.9375rem;color:#0a0a0acc}
.hg__proof-text strong{color:var(--color-primary, #0a0a0a);font-weight:700}
@media (prefers-reduced-motion: reduce){
  .hg__blob--1,.hg__blob--2,.hg__blob--3,.hg__badge-dot,.hg__badge-wrap,.hg__headline,.hg__sub,.hg__actions,.hg__proof{animation:none}
}
@media (max-width:640px){
  .hg__actions{flex-direction:column;width:100%}
  .hg__cta{width:100%;justify-content:center}
}
</style>

Props

Prop Type Default Beschrijving
badge string - Badge-pill boven de kop
headline * string - H1-kop
accent string - Gradient-accent direct na de kop
sub string - Ondertitel
primaryCta { label: string; href: string } - Primaire knop
secondaryCta { label: string; href: string } - Secundaire tekstlink
proofText string - Social-proof tekst (HTML toegestaan voor <strong>)
proofCount number 4 Aantal avatars (max 4)
from string '#f5f3ff' Gradient startkleur
to string '#ede9fe' Gradient eindkleur
blob string '#818cf8' Blob-kleur

* = verplicht