Zoeken...  ⌘K GitHub

CTAGradient CTA

Full-bleed gradient CTA met animated mesh blobs, noise texture overlay. Aanpasbare gradientkleuren.

/cta-gradient
src/components/cta/CTAGradient.astro
---
/**
 * CTAGradient
 * Full-bleed gradient CTA sectie met animated mesh achtergrond,
 * grote headline en dubbele knop. Noise texture overlay.
 * Puur CSS.
 */
interface Props {
  eyebrow?: string;
  headline: string;
  sub?: string;
  ctaLabel?: string;
  ctaHref?: string;
  secondaryLabel?: string;
  secondaryHref?: string;
  gradientFrom?: string;
  gradientTo?: string;
}

const {
  eyebrow,
  headline,
  sub,
  ctaLabel = 'Begin vandaag',
  ctaHref = '#',
  secondaryLabel,
  secondaryHref = '#',
  gradientFrom = '#6366f1',
  gradientTo = '#8b5cf6',
} = Astro.props;
---

<section
  class="cg"
  data-component="cta-gradient"
  style={`--cg-from:${gradientFrom};--cg-to:${gradientTo};`}
>
  <!-- Animated blobs -->
  <div class="cg__blob cg__blob--1" aria-hidden="true"></div>
  <div class="cg__blob cg__blob--2" aria-hidden="true"></div>

  <!-- Noise -->
  <div class="cg__noise" aria-hidden="true"></div>

  <div class="cg__content">
    {eyebrow && (
      <p class="cg__eyebrow">{eyebrow}</p>
    )}
    <h2 class="cg__headline" set:html={headline} />
    {sub && <p class="cg__sub">{sub}</p>}
    <div class="cg__actions">
      <a href={ctaHref} class="cg__cta-primary">{ctaLabel}</a>
      {secondaryLabel && (
        <a href={secondaryHref} class="cg__cta-secondary">
          {secondaryLabel}
          <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"/>
          </svg>
        </a>
      )}
    </div>
  </div>
</section>

<style>
  .cg {
    position: relative;
    padding: 7rem 1.5rem;
    overflow: hidden;
    background: linear-gradient(135deg, var(--cg-from, #6366f1) 0%, var(--cg-to, #8b5cf6) 100%);
    text-align: center;
  }

  .cg__blob {
    position: absolute;
    border-radius: 50%;
    filter: blur(80px);
    opacity: 0.3;
    pointer-events: none;
  }

  .cg__blob--1 {
    width: 60vw;
    height: 60vw;
    background: rgba(255,255,255,0.3);
    top: -30%;
    right: -20%;
    animation: cg-float-1 20s ease-in-out infinite;
  }

  .cg__blob--2 {
    width: 40vw;
    height: 40vw;
    background: rgba(0,0,0,0.2);
    bottom: -20%;
    left: -15%;
    animation: cg-float-2 16s ease-in-out infinite;
  }

  .cg__noise {
    position: absolute;
    inset: 0;
    opacity: 0.05;
    background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
    background-size: 256px;
    pointer-events: none;
  }

  .cg__content {
    position: relative;
    z-index: 1;
    max-width: 680px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0;
  }

  .cg__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    border: 1px solid rgba(255,255,255,0.3);
    background: rgba(255,255,255,0.1);
    padding: 0.375rem 1rem;
    border-radius: 999px;
    font-size: 0.8125rem;
    font-weight: 600;
    color: rgba(255,255,255,0.9);
    letter-spacing: 0.04em;
    margin-bottom: 1.75rem;
  }

  .cg__headline {
    font-size: clamp(2.25rem, 5vw, 4rem);
    font-weight: 900;
    line-height: 1.05;
    letter-spacing: -0.04em;
    color: #fff;
    margin-bottom: 1.25rem;
  }

  .cg__headline :global(em) {
    font-style: normal;
    opacity: 0.75;
  }

  .cg__headline :global(strong) {
    -webkit-text-stroke: 1.5px rgba(255,255,255,0.6);
    -webkit-text-fill-color: transparent;
    color: transparent;
  }

  .cg__sub {
    font-size: 1.0625rem;
    line-height: 1.65;
    color: rgba(255,255,255,0.7);
    max-width: 48ch;
    margin-bottom: 2.5rem;
  }

  .cg__actions {
    display: flex;
    align-items: center;
    gap: 1.25rem;
    flex-wrap: wrap;
    justify-content: center;
  }

  .cg__cta-primary {
    display: inline-flex;
    align-items: center;
    background: #fff;
    color: var(--cg-from, #6366f1);
    padding: 1rem 2.25rem;
    border-radius: var(--radius, 0.5rem);
    font-weight: 800;
    font-size: 0.9375rem;
    text-decoration: none;
    box-shadow: 0 4px 24px rgba(0,0,0,0.15);
    transition: transform 0.2s, box-shadow 0.2s;
  }

  .cg__cta-primary:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 32px rgba(0,0,0,0.2);
  }

  .cg__cta-secondary {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.9375rem;
    font-weight: 600;
    color: rgba(255,255,255,0.8);
    text-decoration: none;
    transition: color 0.2s, gap 0.2s;
  }

  .cg__cta-secondary:hover {
    color: #fff;
    gap: 0.75rem;
  }

  @keyframes cg-float-1 {
    0%, 100% { transform: translate(0, 0) scale(1); }
    50% { transform: translate(-5%, 8%) scale(1.05); }
  }

  @keyframes cg-float-2 {
    0%, 100% { transform: translate(0, 0) scale(1); }
    50% { transform: translate(6%, -5%) scale(1.08); }
  }

  @media (prefers-reduced-motion: reduce) {
    .cg__blob--1, .cg__blob--2 { animation: none; }
  }

  @media (max-width: 500px) {
    .cg { padding: 5rem 1.25rem; }
    .cg__actions { flex-direction: column; width: 100%; }
    .cg__cta-primary { width: 100%; justify-content: center; }
  }
</style>

Props

Prop Type Default Beschrijving
headline * string CTA headline — gebruik <em> voor gedimde variant
eyebrow string Pill label boven headline
sub string Ondertitel
gradientFrom string '#6366f1' Start gradient kleur
gradientTo string '#8b5cf6' Eind gradient kleur

* = verplicht