Zoeken...  ⌘K GitHub

HeroFloatingTestimonial Hero

Hero met zwevende testimonial card over de afbeelding. Split-layout, badge, accent-kleur.

/hero-floating-testimonial
src/components/hero/HeroFloatingTestimonial.astro
---
/**
 * HeroFloatingTestimonial
 * Hero met zwevende testimonial card, badge, headline + CTA.
 * Modern Retail archetype — clean wit, accent kleur, subtiele animaties.
 */
interface Props {
  badge?: string;
  headline: string;
  sub: string;
  ctaLabel?: string;
  ctaHref?: string;
  ctaSecondary?: string;
  ctaSecondaryHref?: string;
  testimonialText: string;
  testimonialAuthor: string;
  testimonialRole?: string;
  testimonialAvatar?: string;
  imageSrc: string;
  imageAlt?: string;
}

const {
  badge,
  headline,
  sub,
  ctaLabel = 'Aan de slag',
  ctaHref = '#',
  ctaSecondary,
  ctaSecondaryHref = '#',
  testimonialText,
  testimonialAuthor,
  testimonialRole,
  testimonialAvatar,
  imageSrc,
  imageAlt = '',
} = Astro.props;
---

<section class="hft" data-component="hero-floating-testimonial">
  <div class="hft__inner">
    <!-- Left: text content -->
    <div class="hft__content">
      {badge && <span class="hft__badge">{badge}</span>}
      <h1 class="hft__headline" set:html={headline} />
      <p class="hft__sub">{sub}</p>
      <div class="hft__ctas">
        <a href={ctaHref} class="hft__cta hft__cta--primary">{ctaLabel}</a>
        {ctaSecondary && (
          <a href={ctaSecondaryHref} class="hft__cta hft__cta--ghost">{ctaSecondary}</a>
        )}
      </div>
    </div>

    <!-- Right: image + floating testimonial -->
    <div class="hft__visual">
      <div class="hft__img-wrap">
        <img src={imageSrc} alt={imageAlt} class="hft__img" loading="eager" />
        <div class="hft__testimonial">
          <svg class="hft__quote-icon" width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
            <path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z"/>
          </svg>
          <p class="hft__testimonial-text">{testimonialText}</p>
          <div class="hft__testimonial-author">
            {testimonialAvatar && (
              <img src={testimonialAvatar} alt={testimonialAuthor} class="hft__avatar" />
            )}
            <div>
              <div class="hft__author-name">{testimonialAuthor}</div>
              {testimonialRole && <div class="hft__author-role">{testimonialRole}</div>}
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</section>

<style>
  .hft {
    background: var(--color-bg, #fff);
    padding: 5rem 1.5rem 4rem;
    overflow: hidden;
  }

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

  .hft__badge {
    display: inline-block;
    background: color-mix(in srgb, var(--color-accent, #6366f1) 12%, transparent);
    color: var(--color-accent, #6366f1);
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 0.35rem 0.85rem;
    border-radius: 100px;
    margin-bottom: 1.25rem;
  }

  .hft__headline {
    font-size: clamp(2.25rem, 4vw, 3.5rem);
    font-weight: 800;
    line-height: 1.1;
    letter-spacing: -0.03em;
    color: var(--color-primary, #0a0a0a);
    margin-bottom: 1.25rem;
  }

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

  .hft__sub {
    font-size: 1.125rem;
    color: var(--color-muted, #6b7280);
    line-height: 1.65;
    margin-bottom: 2rem;
    max-width: 42ch;
  }

  .hft__ctas {
    display: flex;
    gap: 0.75rem;
    flex-wrap: wrap;
  }

  .hft__cta {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.8125rem 1.75rem;
    border-radius: var(--radius, 0.5rem);
    font-size: 0.9375rem;
    font-weight: 700;
    text-decoration: none;
    transition: all 0.2s;
  }

  .hft__cta--primary {
    background: var(--color-accent, #6366f1);
    color: #fff;
  }

  .hft__cta--primary:hover {
    filter: brightness(1.1);
    transform: translateY(-1px);
  }

  .hft__cta--ghost {
    color: var(--color-primary, #0a0a0a);
    border: 1.5px solid currentColor;
    opacity: 0.7;
  }

  .hft__cta--ghost:hover {
    opacity: 1;
  }

  /* Visual */
  .hft__visual {
    position: relative;
  }

  .hft__img-wrap {
    position: relative;
    border-radius: calc(var(--radius, 0.5rem) * 2);
    overflow: visible;
  }

  .hft__img {
    width: 100%;
    height: 480px;
    object-fit: cover;
    border-radius: calc(var(--radius, 0.5rem) * 2);
    display: block;
  }

  /* Floating testimonial card */
  .hft__testimonial {
    position: absolute;
    bottom: -2rem;
    left: -2.5rem;
    background: #fff;
    border: 1px solid rgba(0,0,0,0.07);
    border-radius: calc(var(--radius, 0.5rem) * 1.5);
    padding: 1.25rem 1.5rem;
    max-width: 300px;
    box-shadow: 0 20px 40px rgba(0,0,0,0.1);
  }

  .hft__quote-icon {
    color: var(--color-accent, #6366f1);
    margin-bottom: 0.625rem;
    opacity: 0.7;
  }

  .hft__testimonial-text {
    font-size: 0.875rem;
    line-height: 1.6;
    color: var(--color-primary, #0a0a0a);
    margin-bottom: 0.875rem;
  }

  .hft__testimonial-author {
    display: flex;
    align-items: center;
    gap: 0.625rem;
  }

  .hft__avatar {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    object-fit: cover;
  }

  .hft__author-name {
    font-size: 0.8125rem;
    font-weight: 700;
    color: var(--color-primary, #0a0a0a);
  }

  .hft__author-role {
    font-size: 0.75rem;
    color: var(--color-muted, #6b7280);
  }

  @media (max-width: 768px) {
    .hft__inner {
      grid-template-columns: 1fr;
      gap: 2rem;
    }

    .hft__testimonial {
      position: static;
      margin-top: 1rem;
      max-width: 100%;
    }

    .hft__img {
      height: 300px;
    }
  }
</style>

<script>
  import gsap from 'https://cdn.skypack.dev/gsap';
  import ScrollTrigger from 'https://cdn.skypack.dev/gsap/ScrollTrigger';

  if (typeof gsap !== 'undefined') {
    gsap.registerPlugin(ScrollTrigger);
    const section = document.querySelector('[data-component="hero-floating-testimonial"]');
    if (section) {
      gsap.from('[data-component="hero-floating-testimonial"] .hft__badge', {
        opacity: 0, y: 20, duration: 0.5, delay: 0.1
      });
      gsap.from('[data-component="hero-floating-testimonial"] .hft__headline', {
        opacity: 0, y: 30, duration: 0.6, delay: 0.2
      });
      gsap.from('[data-component="hero-floating-testimonial"] .hft__sub', {
        opacity: 0, y: 20, duration: 0.5, delay: 0.35
      });
      gsap.from('[data-component="hero-floating-testimonial"] .hft__ctas', {
        opacity: 0, y: 20, duration: 0.5, delay: 0.45
      });
      gsap.from('[data-component="hero-floating-testimonial"] .hft__img', {
        opacity: 0, x: 40, duration: 0.8, delay: 0.2, ease: 'power2.out'
      });
      gsap.from('[data-component="hero-floating-testimonial"] .hft__testimonial', {
        opacity: 0, y: 30, duration: 0.6, delay: 0.7, ease: 'back.out(1.5)'
      });
    }
  }
</script>

Props

Prop Type Default Beschrijving
headline * string H1 tekst. Gebruik <em> voor accent.
sub * string Ondertitel
imageSrc * string Hero afbeelding
testimonialText * string Quote tekst
testimonialAuthor * string Naam van auteur
badge string Kleine badge boven headline
ctaLabel string 'Aan de slag' Primaire CTA tekst

* = verplicht