06인용 카드 섞어 배치
후기 카드가 격자 사이사이에 섞여 있어 광고처럼 몰아서 보이지 않는다.
쓰임고객 후기, 파트너 로고와 함께
따옴표 아이콘별점 옵션1칸 폭

<main class="stage">
<section class="testi">
<div class="testi__tile testi__tile--quote is-demo">
<p class="testi__text">"덕분에 온보딩 시간이 반으로 줄었어요"</p>
<p class="testi__by">김도현 · 프로덕트 매니저</p>
</div>
<div class="testi__tile testi__tile--avatar is-demo" aria-hidden="true"></div>
<div class="testi__tile testi__tile--stars is-demo" aria-hidden="true">★★★★★</div>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop; // 세 칸이 스치듯 들어오는 한 주기
$easing: $ease-spring; // 등장 곡선
$color: $subject-blue; // 별점 색
@include stage($stage-yellow);
.testi {
width: clamp(240px, 82vw, 360px);
display: grid;
grid-template-columns: 1.6fr 1fr;
grid-template-rows: 1fr 1fr;
gap: clamp(8px, 2.4vw, 12px);
}
.testi__tile {
border-radius: $r-card;
background: $subject-cream;
box-shadow: $shadow-raised;
padding: clamp(10px, 3vw, 14px);
animation: driftIn $duration $easing infinite;
}
.testi__tile--quote {
grid-row: 1 / 3;
display: flex;
flex-direction: column;
justify-content: center;
gap: 8px;
animation-delay: 0s;
}
.testi__tile--avatar {
position: relative;
animation-delay: -0.6s;
}
.testi__tile--avatar::before {
content: "";
position: absolute;
inset: 0;
margin: auto;
width: 26px;
height: 26px;
border-radius: 50%;
background: $color;
}
.testi__tile--stars {
display: grid;
place-items: center;
color: $subject-blue-deep; // 밝은 면 위 파란 글자 — AA 4.5 (blog-design 글자 대비)
letter-spacing: 2px;
font-size: clamp(11px, 2.8vw, 13px);
animation-delay: -1.2s;
}
.testi__text {
margin: 0;
color: $subject-ink;
font-weight: 600; font-size: clamp(11px, 2.8vw, 13px); line-height: 1.5;
}
.testi__by {
margin: 0;
color: $subject-ink;
opacity: .65;
font-weight: 500; font-size: clamp(9.5px, 2.2vw, 11px); line-height: 1.3;
}
// 데모: 세 칸이 아래서 살짝 미끄러져 들어왔다 멈췄다 사라진다 (시차)
@keyframes driftIn {
0% { opacity: 0; transform: translateY(12px); }
20% { opacity: 1; transform: translateY(0); }
70% { opacity: 1; transform: translateY(0); }
100% { opacity: 0; transform: translateY(12px); }
}
@media (prefers-reduced-motion: reduce) {
.testi__tile {
animation: none !important;
opacity: 1;
transform: none;
}
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #ffd23f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.testi {
width: clamp(240px, 82vw, 360px);
display: grid;
grid-template-columns: 1.6fr 1fr;
grid-template-rows: 1fr 1fr;
gap: clamp(8px, 2.4vw, 12px);
}
.testi__tile {
border-radius: 16px;
background: #fff7e6;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
padding: clamp(10px, 3vw, 14px);
animation: driftIn 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.testi__tile--quote {
grid-row: 1/3;
display: flex;
flex-direction: column;
justify-content: center;
gap: 8px;
animation-delay: 0s;
}
.testi__tile--avatar {
position: relative;
animation-delay: -0.6s;
}
.testi__tile--avatar::before {
content: "";
position: absolute;
inset: 0;
margin: auto;
width: 26px;
height: 26px;
border-radius: 50%;
background: #2f6df6;
}
.testi__tile--stars {
display: grid;
place-items: center;
color: #0a4fe8;
letter-spacing: 2px;
font-size: clamp(11px, 2.8vw, 13px);
animation-delay: -1.2s;
}
.testi__text {
margin: 0;
color: #17141a;
font-weight: 600;
font-size: clamp(11px, 2.8vw, 13px);
line-height: 1.5;
}
.testi__by {
margin: 0;
color: #17141a;
opacity: 0.65;
font-weight: 500;
font-size: clamp(9.5px, 2.2vw, 11px);
line-height: 1.3;
}
@keyframes driftIn {
0% {
opacity: 0;
transform: translateY(12px);
}
20% {
opacity: 1;
transform: translateY(0);
}
70% {
opacity: 1;
transform: translateY(0);
}
100% {
opacity: 0;
transform: translateY(12px);
}
}
@media (prefers-reduced-motion: reduce) {
.testi__tile {
animation: none !important;
opacity: 1;
transform: none;
}
}import type { CSSProperties } from "react";
import styles from "./BentoTestimonials.module.scss";
export type BentoTestimonialsProps = {
quote?: string;
by?: string;
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function BentoTestimonials({
quote = "Onboarding time dropped by half for us.",
by = "Kim Do-hyun · Product Manager",
duration = 2000,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#2f6df6",
}: BentoTestimonialsProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.testi} style={vars}>
<div className={`${styles.tile} ${styles.quote}`}>
<p className={styles.text}>“{quote}”</p>
<p className={styles.by}>{by}</p>
</div>
<div className={`${styles.tile} ${styles.avatar}`} aria-hidden="true" />
<div className={`${styles.tile} ${styles.stars}`} aria-hidden="true">★★★★★</div>
</section>
);
}06 인용 카드 섞어 배치 — 측정 누적면적 33.467% · 움직인 프레임 16/23GODRICH 원작 · zip 에 SCSS·React 포함 · 벤토 그리드 디자인 — 요즘 핀터레스트에서 유행하는 부품 9개
이 데모가 속한 글
벤토 그리드 디자인 — 요즘 핀터레스트에서 유행하는 부품 9개벤토 그리드 디자인은 도시락 칸처럼 크기가 다른 사각형 타일을 한 격자에 채워 화면을 구성하는 방식으로, 핀터레스트·드리블에서 포트폴리오와 SaaS 랜딩 페이지를 중심으로 유행하고 있습니다. 특징은 다섯 가지입니다. 이 글의 데모 전부 보기


