CTAGradient CTA
Full-bleed gradient CTA met animated mesh blobs, noise texture overlay. Aanpasbare gradientkleuren.
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