Zoeken...  ⌘K GitHub

ContentQuote content

Full-width citaat als content break. Decoratief aanhalingsteken, auteur met avatar. 4 achtergrondvarianten.

/content-quote
src/components/content/ContentQuote.astro
---
interface Props {
  quote: string;
  author?: string;
  role?: string;
  avatar?: string;
  accentColor?: string;
  size?: 'md' | 'lg' | 'xl';
  centered?: boolean;
  bg?: 'white' | 'light' | 'accent' | 'dark';
}

const {
  quote,
  author,
  role,
  avatar,
  accentColor,
  size = 'lg',
  centered = false,
  bg = 'white',
} = Astro.props;

const bgClass = `cqu__section--bg-${bg}`;
const sizeClass = `cqu__quote--size-${size}`;
const centeredClass = centered ? 'cqu__section--centered' : '';

// Override accent color via inline style if provided
const accentStyle = accentColor ? `--cqu-accent: ${accentColor};` : '';
---

<section class={`cqu__section ${bgClass} ${centeredClass}`} style={accentStyle}>
  <div class="cqu__inner cqu-reveal">
    <!-- Decorative quotation mark -->
    <span class="cqu__mark" aria-hidden="true">&ldquo;</span>

    <blockquote class={`cqu__quote ${sizeClass}`}>
      <p class="cqu__text">{quote}</p>

      {(author || role || avatar) && (
        <footer class="cqu__author">
          {avatar && (
            <img
              src={avatar}
              alt={author ?? ''}
              class="cqu__avatar"
              loading="lazy"
              decoding="async"
            />
          )}
          <div class="cqu__author-meta">
            {author && <span class="cqu__author-name">{author}</span>}
            {author && role && <span class="cqu__dot" aria-hidden="true">·</span>}
            {role && <span class="cqu__author-role">{role}</span>}
          </div>
        </footer>
      )}
    </blockquote>
  </div>
</section>

<script>
  const reveals = document.querySelectorAll<HTMLElement>('.cqu-reveal');

  const observer = new IntersectionObserver(
    (entries) => {
      entries.forEach((entry) => {
        if (entry.isIntersecting) {
          (entry.target as HTMLElement).classList.add('cqu-reveal--visible');
          observer.unobserve(entry.target);
        }
      });
    },
    { threshold: 0.2 }
  );

  reveals.forEach((el) => observer.observe(el));
</script>

<style>
  :root {
    --color-primary: #0a0a0a;
    --color-accent: #6366f1;
    --color-bg: #fff;
    --color-muted: #6b7280;
    --radius: 0.5rem;

    /* Internal override slot */
    --cqu-accent: var(--color-accent);
  }

  /* Section base */
  .cqu__section {
    position: relative;
    padding: clamp(4rem, 10vw, 8rem) clamp(1rem, 5vw, 2rem);
    overflow: hidden;
  }

  /* Background variants */
  .cqu__section--bg-white {
    background: var(--color-bg);
    color: var(--color-primary);
  }

  .cqu__section--bg-light {
    background: rgba(0, 0, 0, 0.02);
    color: var(--color-primary);
  }

  .cqu__section--bg-accent {
    background: var(--color-accent);
    color: #fff;
  }

  .cqu__section--bg-dark {
    background: #0a0a0a;
    color: #fff;
  }

  /* Inner wrapper */
  .cqu__inner {
    position: relative;
    max-width: 820px;
    margin: 0 auto;
  }

  .cqu__section--centered .cqu__inner {
    text-align: center;
  }

  .cqu__section--centered .cqu__author {
    justify-content: center;
  }

  .cqu__section--centered .cqu__mark {
    left: 50%;
    transform: translateX(-50%);
  }

  /* Decorative quote mark */
  .cqu__mark {
    position: absolute;
    top: -0.15em;
    left: -0.05em;
    font-size: clamp(6rem, 15vw, 12rem);
    line-height: 1;
    font-family: Georgia, 'Times New Roman', serif;
    font-weight: 900;
    color: var(--cqu-accent);
    opacity: 0.15;
    pointer-events: none;
    user-select: none;
    z-index: 0;
  }

  .cqu__section--bg-accent .cqu__mark,
  .cqu__section--bg-dark .cqu__mark {
    color: #fff;
  }

  /* Blockquote */
  .cqu__quote {
    position: relative;
    z-index: 1;
    margin: 0;
    padding: 0;
    border: none;
  }

  /* Quote text */
  .cqu__text {
    font-style: italic;
    font-weight: 600;
    line-height: 1.55;
    margin: 0 0 2rem;
  }

  /* Size variants */
  .cqu__quote--size-md .cqu__text {
    font-size: clamp(1.125rem, 2vw, 1.5rem);
  }

  .cqu__quote--size-lg .cqu__text {
    font-size: clamp(1.25rem, 2.5vw, 2rem);
  }

  .cqu__quote--size-xl .cqu__text {
    font-size: clamp(1.5rem, 3.5vw, 2.5rem);
  }

  /* Light BG: muted color for text */
  .cqu__section--bg-white .cqu__text,
  .cqu__section--bg-light .cqu__text {
    color: var(--color-primary);
  }

  .cqu__section--bg-accent .cqu__text,
  .cqu__section--bg-dark .cqu__text {
    color: #fff;
  }

  /* Author row */
  .cqu__author {
    display: flex;
    align-items: center;
    gap: 0.875rem;
  }

  .cqu__avatar {
    width: 48px;
    height: 48px;
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
    box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
  }

  .cqu__author-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.4rem;
    font-size: 0.9375rem;
  }

  .cqu__author-name {
    font-weight: 700;
  }

  .cqu__section--bg-white .cqu__author-name,
  .cqu__section--bg-light .cqu__author-name {
    color: var(--color-primary);
  }

  .cqu__section--bg-accent .cqu__author-name,
  .cqu__section--bg-dark .cqu__author-name {
    color: #fff;
  }

  .cqu__dot {
    color: var(--color-muted);
  }

  .cqu__section--bg-accent .cqu__dot,
  .cqu__section--bg-dark .cqu__dot {
    color: rgba(255, 255, 255, 0.5);
  }

  .cqu__author-role {
    color: var(--color-muted);
  }

  .cqu__section--bg-accent .cqu__author-role,
  .cqu__section--bg-dark .cqu__author-role {
    color: rgba(255, 255, 255, 0.65);
  }

  /* Scroll reveal — slides up + fades in */
  .cqu-reveal {
    opacity: 0;
    transform: translateY(2rem);
    transition: opacity 0.75s ease, transform 0.75s ease;
  }

  .cqu-reveal--visible {
    opacity: 1;
    transform: translateY(0);
  }

  @media (prefers-reduced-motion: reduce) {
    * {
      animation: none !important;
      transition: none !important;
    }
  }
</style>

Props

Prop Type Default Beschrijving
quote * string Het citaat
author string Auteur naam
role string Functie/rol
bg 'white' | 'light' | 'accent' | 'dark' 'white' Achtergrond variant
size 'md' | 'lg' | 'xl' 'md' Citaat grootte

* = verplicht