★★★★★
"The pure zero-JS philosophy with React 5 is game-changing. Our Core Web Vitals score hit 100 on every audited route without tweaking hydration."1 звезда на GitHub = 1 новый компонент.
Поставить звезду→Бенто-сетка отзывов клиентов с рейтингами, верифицированными бейджами и цитатами.
Discover why developers, indie hackers, and creative agencies rely on Astro Components Kit for production websites.
"The pure zero-JS philosophy with React 5 is game-changing. Our Core Web Vitals score hit 100 on every audited route without tweaking hydration.""Copying and pasting self-contained React components directly shaved two full sprints off our SaaS launch timeline."
"The glassmorphic dark palette with gold accents feels extraordinarily premium right out of the box. Highly recommended."
Скопируйте и вставьте этот код в ваш React-компонент:
import { TestimonialGridSection } from 'react-component-kit-ui/sections';
<TestimonialGridSection />Если вы не используете пакет npm, создайте файл и вставьте следующий код: src/components/sections/TestimonialGridSection.tsx + TestimonialGridSection.css:
'use client';
import './TestimonialGridSection.css';
/** class:list → className */
function cx(...args: unknown[]): string {
const out: string[] = [];
const walk = (v: unknown) => {
if (!v) return;
if (typeof v === 'string' || typeof v === 'number') out.push(String(v));
else if (Array.isArray(v)) v.forEach(walk);
else if (typeof v === 'object') for (const [k, on] of Object.entries(v as Record<string, unknown>)) if (on) out.push(k);
};
args.forEach(walk);
return out.join(' ');
}
/**
* TestimonialGridSection — Bento grid showcase of verified customer reviews with dark glass styling and star ratings.
*
* @param {string} eyebrow - Small uppercase category lead. Default is 'WALL OF LOVE'.
* @param {string} title - Section main headline. Default is 'Trusted by innovative engineering teams'.
* @param {string} subtitle - Subtitle description.
*/
export interface TestimonialGridSectionProps {
eyebrow?: string;
title?: string;
subtitle?: string;
}
export function TestimonialGridSection({
eyebrow = 'WALL OF LOVE',
title = 'Trusted by thousands of Astro builders',
subtitle = 'Discover why developers, indie hackers, and creative agencies rely on Astro Components Kit for production websites.',
}: TestimonialGridSectionProps) {
const testimonials = [
{
name: 'Elena Rostova',
role: 'Principal Architect @ Veloce Labs',
avatar: 'https://images.unsplash.com/photo-1534528741775-53994a69daeb?w=100&auto=format&fit=crop&q=80',
quote: 'The pure zero-JS philosophy with React 5 is game-changing. Our Core Web Vitals score hit 100 on every audited route without tweaking hydration.',
stars: 5,
highlight: true,
},
{
name: 'Marcus Vance',
role: 'Founder @ HyperSync',
avatar: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=100&auto=format&fit=crop&q=80',
quote: 'Copying and pasting self-contained React components directly shaved two full sprints off our SaaS launch timeline.',
stars: 5,
},
{
name: 'Sofia Chen',
role: 'Senior UI/UX Designer',
avatar: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=100&auto=format&fit=crop&q=80',
quote: 'The glassmorphic dark palette with gold accents feels extraordinarily premium right out of the box. Highly recommended.',
stars: 5,
},
];
return (
<>
<section className="testi-section" data-rk-6335bb="">
<div className="testi-section__header" data-rk-6335bb="">
<span className="testi-section__eyebrow" data-rk-6335bb="">{eyebrow}</span>
<h2 className="testi-section__title" data-rk-6335bb="">{title}</h2>
<p className="testi-section__subtitle" data-rk-6335bb="">{subtitle}</p>
</div>
<div className="testi-section__grid" data-rk-6335bb="">
{testimonials.map((t, __i: number) => (
<article key={__i} className={cx(['testi-card', { 'testi-card--highlight': t.highlight }])} data-rk-6335bb="">
<div className="testi-card__stars" aria-label={`${t.stars} out of 5 stars`} data-rk-6335bb="">
{Array.from({ length: t.stars }).map((_, __i: number) => (
<span key={__i} data-rk-6335bb="">★</span>
))}
</div>
<blockquote className="testi-card__quote" data-rk-6335bb="">
"{t.quote}"
</blockquote>
<div className="testi-card__author" data-rk-6335bb="">
<img src={t.avatar} alt={t.name} className="testi-card__avatar" loading="lazy" data-rk-6335bb="" />
<div className="testi-card__meta" data-rk-6335bb="">
<h5 className="testi-card__name" data-rk-6335bb="">{t.name}</h5>
<span className="testi-card__role" data-rk-6335bb="">{t.role}</span>
</div>
</div>
</article>
))}
</div>
</section>
</>
);
}
export default TestimonialGridSection;/* TestimonialGridSection — ported from astro-component-kit (sections/TestimonialGridSection.astro) */
.testi-section:where([data-rk-6335bb]) {
padding: var(--sp-12, 3rem) var(--sp-6, 1.5rem);
max-width: 1200px;
margin: 0 auto;
font-family: var(--font-sans, system-ui, sans-serif);
}
.testi-section__header:where([data-rk-6335bb]) {
text-align: center;
max-width: 640px;
margin: 0 auto 3rem auto;
}
.testi-section__eyebrow:where([data-rk-6335bb]) {
display: inline-block;
font-size: 0.75rem;
font-weight: 800;
letter-spacing: 0.12em;
color: var(--c-primary-light, #d4a017);
padding: 0.35rem 0.8rem;
border: 1px solid color-mix(in srgb, var(--c-primary, #b8860b) 30%, transparent);
border-radius: var(--r-sm, 8px);
margin-bottom: 1rem;
text-transform: uppercase;
}
.testi-section__title:where([data-rk-6335bb]) {
font-size: clamp(1.8rem, 4vw, 2.8rem);
font-weight: 900;
color: var(--c-text-1, #ffffff);
letter-spacing: -0.03em;
margin: 0 0 1rem 0;
}
.testi-section__subtitle:where([data-rk-6335bb]) {
font-size: 0.95rem;
color: var(--c-text-2, #a3a3a3);
line-height: 1.6;
margin: 0;
}
.testi-section__grid:where([data-rk-6335bb]) {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 1.5rem;
}
.testi-card:where([data-rk-6335bb]) {
display: flex;
flex-direction: column;
padding: var(--sp-8, 2rem);
background: rgb(var(--ink-rgb, 255 255 255) / 0.02);
backdrop-filter: blur(16px);
border: 1px solid rgb(var(--ink-rgb, 255 255 255) / 0.08);
border-radius: var(--r-xl, 24px);
transition: transform 0.3s ease, border-color 0.3s ease;
}
.testi-card:hover:where([data-rk-6335bb]) {
transform: translateY(-4px);
border-color: color-mix(in srgb, var(--c-primary, #b8860b) 30%, transparent);
}
.testi-card--highlight:where([data-rk-6335bb]) {
background: radial-gradient(circle at top left, color-mix(in srgb, var(--c-primary, #b8860b) 10%, transparent), rgb(var(--ink-rgb, 255 255 255) / 0.02) 70%);
border-color: color-mix(in srgb, var(--c-primary, #b8860b) 40%, transparent);
}
.testi-card__stars:where([data-rk-6335bb]) {
color: #fbbf24;
font-size: 1rem;
letter-spacing: 2px;
margin-bottom: 1rem;
}
.testi-card__quote:where([data-rk-6335bb]) {
font-size: 0.95rem;
color: var(--c-text-1, #ffffff);
line-height: 1.65;
margin: 0 0 1.5rem 0;
flex: 1;
font-style: italic;
}
.testi-card__author:where([data-rk-6335bb]) {
display: flex;
align-items: center;
gap: 12px;
margin-top: auto;
}
.testi-card__avatar:where([data-rk-6335bb]) {
width: 44px;
height: 44px;
border-radius: 50%;
object-fit: cover;
border: 2px solid color-mix(in srgb, var(--c-primary, #b8860b) 40%, transparent);
}
.testi-card__name:where([data-rk-6335bb]) {
font-size: 0.92rem;
font-weight: 700;
color: var(--c-text-1, #ffffff);
margin: 0 0 2px 0;
}
.testi-card__role:where([data-rk-6335bb]) {
font-size: 0.75rem;
color: var(--c-text-3, #737373);
}