Zoeken...  ⌘K GitHub

ReviewStars Social Proof

Samengevatte sterrenwaardering met score en recentste reviews.

/review-stars
src/components/social-proof/ReviewStars.astro
---
/**
 * ReviewStars
 * Samengevatte sterrenwaardering met score en aantal reviews.
 */
interface Props {
  score: number;
  maxScore?: number;
  reviewCount: number;
  platform?: string;
  reviews?: { text: string; author: string; rating?: number }[];
  bg?: 'white' | 'light';
}
const { score, maxScore = 5, reviewCount, platform = 'Google', reviews = [], bg = 'white' } = Astro.props;
const fullStars = Math.floor(score);
const hasHalf = score % 1 >= 0.5;
---
<section class={`rvs rvs--${bg}`}>
  <div class="rvs-inner">
    <div class="rvs-summary">
      <div class="rvs-score-block">
        <span class="rvs-score">{score.toFixed(1)}</span>
        <span class="rvs-max">/{maxScore}</span>
      </div>
      <div class="rvs-detail">
        <div class="rvs-stars">
          {Array.from({length: fullStars}).map(() => <span class="rvs-star">★</span>)}
          {hasHalf && <span class="rvs-star rvs-star--half">★</span>}
        </div>
        <p class="rvs-count">{reviewCount} beoordelingen op {platform}</p>
      </div>
    </div>
    {reviews.length > 0 && (
      <div class="rvs-reviews">
        {reviews.map(r => (
          <div class="rvs-review">
            {r.rating && (
              <div class="rvs-review-stars">
                {Array.from({length: r.rating}).map(() => <span class="rvs-star rvs-star--sm">★</span>)}
              </div>
            )}
            <p class="rvs-review-text">"{r.text}"</p>
            <span class="rvs-review-author">— {r.author}</span>
          </div>
        ))}
      </div>
    )}
  </div>
</section>
<style>
  .rvs { padding: 4rem 2rem; }
  .rvs--white { background: #fff; border-top: 1px solid #e5e7eb; }
  .rvs--light { background: #f8fafc; border-top: 1px solid #e5e7eb; }
  .rvs-inner { max-width: 1100px; margin: 0 auto; }
  .rvs-summary { display: flex; align-items: center; gap: 1.5rem; justify-content: center; margin-bottom: 3rem; }
  .rvs-score-block { display: flex; align-items: baseline; gap: 0.125rem; }
  .rvs-score { font-size: 4rem; font-weight: 900; color: #0a0a0a; line-height: 1; letter-spacing: -0.04em; }
  .rvs-max { font-size: 1.25rem; color: #9ca3af; }
  .rvs-detail { }
  .rvs-stars { display: flex; gap: 0.25rem; margin-bottom: 0.375rem; }
  .rvs-star { color: #f59e0b; font-size: 1.5rem; line-height: 1; }
  .rvs-star--half { opacity: 0.4; }
  .rvs-star--sm { font-size: 0.875rem; }
  .rvs-count { font-size: 0.875rem; color: #6b7280; margin: 0; }
  .rvs-reviews { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); gap: 1.5rem; }
  .rvs-review { background: #fff; border: 1px solid #e5e7eb; border-radius: 0.75rem; padding: 1.5rem; }
  .rvs--white .rvs-review { background: #f8fafc; }
  .rvs-review-stars { display: flex; gap: 0.2rem; margin-bottom: 0.75rem; }
  .rvs-review-text { font-size: 0.9375rem; color: #374151; line-height: 1.6; font-style: italic; margin: 0 0 0.75rem; }
  .rvs-review-author { font-size: 0.8125rem; color: #9ca3af; font-weight: 600; }
</style>

Props

Prop Type Default Beschrijving
score * number Gemiddelde score
reviewCount * number Totaal aantal reviews
reviews { text: string; author: string; rating?: number }[] Recente reviews
maxScore number 5 Maximale score
platform string Platform naam
bg 'white' | 'light' Achtergrond variant

* = verplicht