03アイコンカードグリッド
白いカード4枚がごく薄いグレーの影だけをまとってグリッドに並び、アイコンを収めた青い円はホバーで少し拡大して濃くなります。色はすべて同じ青一色です。
使いどころ機能紹介グリッド・サービスカード
단일 브랜드 컬러scale 호버2열→1열

<main class="stage">
<div class="cardgrid">
<div class="cardgrid__card cardgrid__card--a">
<span class="cardgrid__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M216 56v56c0 96-88 120-88 120s-88-24-88-120V56a8 8 0 0 1 8-8h160a8 8 0 0 1 8 8" opacity=".2"/><path d="M208 40H48a16 16 0 0 0-16 16v56c0 52.72 25.52 84.67 46.93 102.19c23.06 18.86 46 25.26 47 25.53a8 8 0 0 0 4.2 0c1-.27 23.91-6.67 47-25.53C198.48 196.67 224 164.72 224 112V56a16 16 0 0 0-16-16m0 72c0 37.07-13.66 67.16-40.6 89.42a129.3 129.3 0 0 1-39.4 22.2a128.3 128.3 0 0 1-38.92-21.81C61.82 179.51 48 149.3 48 112V56h160ZM82.34 141.66a8 8 0 0 1 11.32-11.32L112 148.69l50.34-50.35a8 8 0 0 1 11.32 11.32l-56 56a8 8 0 0 1-11.32 0Z"/></g></svg></span>
<p class="cardgrid__label">セキュリティ</p>
</div>
<div class="cardgrid__card cardgrid__card--b">
<span class="cardgrid__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="m96 240l16-80l-64-24L160 16l-16 80l64 24Z" opacity=".2"/><path d="M215.79 118.17a8 8 0 0 0-5-5.66L153.18 90.9l14.66-73.33a8 8 0 0 0-13.69-7l-112 120a8 8 0 0 0 3 13l57.63 21.61l-14.62 73.25a8 8 0 0 0 13.69 7l112-120a8 8 0 0 0 1.94-7.26M109.37 214l10.47-52.38a8 8 0 0 0-5-9.06L62 132.71l84.62-90.66l-10.46 52.38a8 8 0 0 0 5 9.06l52.8 19.8Z"/></g></svg></span>
<p class="cardgrid__label">スピード</p>
</div>
<div class="cardgrid__card cardgrid__card--c">
<span class="cardgrid__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M224 64v144H32V48h176a16 16 0 0 1 16 16" opacity=".2"/><path d="M232 208a8 8 0 0 1-8 8H32a8 8 0 0 1-8-8V48a8 8 0 0 1 16 0v108.69l50.34-50.35a8 8 0 0 1 11.32 0L128 132.69L180.69 80H160a8 8 0 0 1 0-16h40a8 8 0 0 1 8 8v40a8 8 0 0 1-16 0V91.31l-58.34 58.35a8 8 0 0 1-11.32 0L96 123.31l-56 56V200h184a8 8 0 0 1 8 8"/></g></svg></span>
<p class="cardgrid__label">成長</p>
</div>
<div class="cardgrid__card cardgrid__card--d">
<span class="cardgrid__icon" aria-hidden="true"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M208 88H48a8 8 0 0 0-8 8v112a8 8 0 0 0 8 8h160a8 8 0 0 0 8-8V96a8 8 0 0 0-8-8m-80 72a20 20 0 1 1 20-20a20 20 0 0 1-20 20" opacity=".2"/><path d="M208 80h-32V56a48 48 0 0 0-96 0v24H48a16 16 0 0 0-16 16v112a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16V96a16 16 0 0 0-16-16M96 56a32 32 0 0 1 64 0v24H96Zm112 152H48V96h160zm-80-96a28 28 0 0 0-8 54.83V184a8 8 0 0 0 16 0v-17.17a28 28 0 0 0-8-54.83m0 40a12 12 0 1 1 12-12a12 12 0 0 1-12 12"/></g></svg></span>
<p class="cardgrid__label">セキュリティ認証</p>
</div>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-loop;
$easing: $ease-out;
$color: $subject-blue;
$trust-bluetint: #eaf1ff;
$trust-ink: $subject-ink;
$trust-blue: $subject-blue;
@include stage($trust-bluetint);
.cardgrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: $sp-2;
width: 200px;
}
.cardgrid__card {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: $sp-1;
padding: $sp-2;
border-radius: $r-card;
background: $stage-paper;
box-shadow: 0 8px 16px rgba($trust-ink, .08);
animation: cardLift $duration $easing infinite;
}
.cardgrid__card--a { animation-delay: 0s; }
.cardgrid__card--b { animation-delay: $duration * -0.75; }
.cardgrid__card--c { animation-delay: $duration * -0.5; }
.cardgrid__card--d { animation-delay: $duration * -0.25; }
.cardgrid__icon {
display: grid;
place-items: center;
width: 36px;
height: 36px;
border-radius: 50%;
background: rgba($trust-blue, .14);
color: $trust-blue;
font-size: 18px;
animation: iconScale $duration $easing infinite;
}
.cardgrid__card--a .cardgrid__icon { animation-delay: 0s; }
.cardgrid__card--b .cardgrid__icon { animation-delay: $duration * -0.75; }
.cardgrid__card--c .cardgrid__icon { animation-delay: $duration * -0.5; }
.cardgrid__card--d .cardgrid__icon { animation-delay: $duration * -0.25; }
.cardgrid__label {
margin: 0;
color: $trust-ink;
font-weight: 600; font-size: 12px; line-height: 1.2;
font-family: inherit;
}
// 데모: 카드 네 장이 시작 시각을 어긋내 순서대로 살짝 뜨고(호버 시 들리는 느낌), 아이콘 원은 배경이 진해지며 함께 커진다
@keyframes cardLift {
0%, 60%, 100% { transform: translateY(0); box-shadow: 0 8px 16px rgba(23, 20, 26, .08); }
20% { transform: translateY(-5px); box-shadow: 0 14px 22px rgba(23, 20, 26, .12); }
}
@keyframes iconScale {
0%, 60%, 100% { transform: scale(1); background: rgba(47, 109, 246, .14); }
20% { transform: scale(1.12); background: rgba(47, 109, 246, .22); }
}
@media (max-width: 340px), (max-height: 220px) {
.cardgrid { width: 176px; gap: $sp-1; }
.cardgrid__card { padding: $sp-1; }
.cardgrid__icon { width: 28px; height: 28px; font-size: 14px; }
.cardgrid__label { font-size: 10px; }
}
@media (prefers-reduced-motion: reduce) {
.cardgrid__card, .cardgrid__icon { animation: none !important; transform: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #eaf1ff;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.cardgrid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 8px;
width: 200px;
}
.cardgrid__card {
box-sizing: border-box;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
padding: 8px;
border-radius: 16px;
background: #ffffff;
box-shadow: 0 8px 16px rgba(23, 20, 26, 0.08);
animation: cardLift 2s ease-out infinite;
}
.cardgrid__card--a {
animation-delay: 0s;
}
.cardgrid__card--b {
animation-delay: -1.5s;
}
.cardgrid__card--c {
animation-delay: -1s;
}
.cardgrid__card--d {
animation-delay: -0.5s;
}
.cardgrid__icon {
display: grid;
place-items: center;
width: 36px;
height: 36px;
border-radius: 50%;
background: rgba(47, 109, 246, 0.14);
color: #2f6df6;
font-size: 18px;
animation: iconScale 2s ease-out infinite;
}
.cardgrid__card--a .cardgrid__icon {
animation-delay: 0s;
}
.cardgrid__card--b .cardgrid__icon {
animation-delay: -1.5s;
}
.cardgrid__card--c .cardgrid__icon {
animation-delay: -1s;
}
.cardgrid__card--d .cardgrid__icon {
animation-delay: -0.5s;
}
.cardgrid__label {
margin: 0;
color: #17141a;
font-weight: 600;
font-size: 12px;
line-height: 1.2;
font-family: inherit;
}
@keyframes cardLift {
0%, 60%, 100% {
transform: translateY(0);
box-shadow: 0 8px 16px rgba(23, 20, 26, 0.08);
}
20% {
transform: translateY(-5px);
box-shadow: 0 14px 22px rgba(23, 20, 26, 0.12);
}
}
@keyframes iconScale {
0%, 60%, 100% {
transform: scale(1);
background: rgba(47, 109, 246, 0.14);
}
20% {
transform: scale(1.12);
background: rgba(47, 109, 246, 0.22);
}
}
@media (max-width: 340px), (max-height: 220px) {
.cardgrid {
width: 176px;
gap: 4px;
}
.cardgrid__card {
padding: 4px;
}
.cardgrid__icon {
width: 28px;
height: 28px;
font-size: 14px;
}
.cardgrid__label {
font-size: 10px;
}
}
@media (prefers-reduced-motion: reduce) {
.cardgrid__card, .cardgrid__icon {
animation: none !important;
transform: none;
}
}import type { CSSProperties, ReactNode } from "react";
import styles from "./CardGridTrust.module.scss";
type Item = { label: string; icon: ReactNode };
export type CardGridTrustProps = {
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
const ICONS: Item[] = [
{
label: "보안",
icon: (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M216 56v56c0 96-88 120-88 120s-88-24-88-120V56a8 8 0 0 1 8-8h160a8 8 0 0 1 8 8" opacity=".2"/><path d="M208 40H48a16 16 0 0 0-16 16v56c0 52.72 25.52 84.67 46.93 102.19c23.06 18.86 46 25.26 47 25.53a8 8 0 0 0 4.2 0c1-.27 23.91-6.67 47-25.53C198.48 196.67 224 164.72 224 112V56a16 16 0 0 0-16-16m0 72c0 37.07-13.66 67.16-40.6 89.42a129.3 129.3 0 0 1-39.4 22.2a128.3 128.3 0 0 1-38.92-21.81C61.82 179.51 48 149.3 48 112V56h160ZM82.34 141.66a8 8 0 0 1 11.32-11.32L112 148.69l50.34-50.35a8 8 0 0 1 11.32 11.32l-56 56a8 8 0 0 1-11.32 0Z"/></g></svg>
),
},
{
label: "속도",
icon: (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="m96 240l16-80l-64-24L160 16l-16 80l64 24Z" opacity=".2"/><path d="M215.79 118.17a8 8 0 0 0-5-5.66L153.18 90.9l14.66-73.33a8 8 0 0 0-13.69-7l-112 120a8 8 0 0 0 3 13l57.63 21.61l-14.62 73.25a8 8 0 0 0 13.69 7l112-120a8 8 0 0 0 1.94-7.26M109.37 214l10.47-52.38a8 8 0 0 0-5-9.06L62 132.71l84.62-90.66l-10.46 52.38a8 8 0 0 0 5 9.06l52.8 19.8Z"/></g></svg>
),
},
{
label: "성장",
icon: (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M224 64v144H32V48h176a16 16 0 0 1 16 16" opacity=".2"/><path d="M232 208a8 8 0 0 1-8 8H32a8 8 0 0 1-8-8V48a8 8 0 0 1 16 0v108.69l50.34-50.35a8 8 0 0 1 11.32 0L128 132.69L180.69 80H160a8 8 0 0 1 0-16h40a8 8 0 0 1 8 8v40a8 8 0 0 1-16 0V91.31l-58.34 58.35a8 8 0 0 1-11.32 0L96 123.31l-56 56V200h184a8 8 0 0 1 8 8"/></g></svg>
),
},
{
label: "보안 인증",
icon: (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M208 88H48a8 8 0 0 0-8 8v112a8 8 0 0 0 8 8h160a8 8 0 0 0 8-8V96a8 8 0 0 0-8-8m-80 72a20 20 0 1 1 20-20a20 20 0 0 1-20 20" opacity=".2"/><path d="M208 80h-32V56a48 48 0 0 0-96 0v24H48a16 16 0 0 0-16 16v112a16 16 0 0 0 16 16h160a16 16 0 0 0 16-16V96a16 16 0 0 0-16-16M96 56a32 32 0 0 1 64 0v24H96Zm112 152H48V96h160zm-80-96a28 28 0 0 0-8 54.83V184a8 8 0 0 0 16 0v-17.17a28 28 0 0 0-8-54.83m0 40a12 12 0 1 1 12-12a12 12 0 0 1-12 12"/></g></svg>
),
},
];
export default function CardGridTrust({ duration = 2000, easing = "ease-out", color = "#2f6df6" }: CardGridTrustProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
const delays = ["a", "b", "c", "d"] as const;
return (
<div className={styles.grid} style={vars}>
{ICONS.map((it, i) => (
<div key={it.label} className={`${styles.card} ${styles[delays[i]]}`}>
<span className={`${styles.icon} ${styles[delays[i]]}`}>{it.icon}</span>
<p className={styles.label}>{it.label}</p>
</div>
))}
</div>
);
}03 アイコンカードグリッド — 計測 累積面積 7.384% · 動いたフレーム 23/23GODRICH オリジナル · SCSS と React は zip に同梱 · saas サイト デザイン 信頼感キット、クリーンなセクション9選
このデモが属する記事
saas サイト デザイン 信頼感キット、クリーンなセクション9選saas サイト デザインの良さは、白とオフホワイトという二つの中立な背景の上に、ブランドカラーを青一色だけ重ね、影は色付きにせずグレー系で浅く、角の丸みも抑えた半径に統一するところにあります。パステルやキャンディグラデーションの明るさとは この記事のデモをすべて見る


