03Paper grain feature card
Three grained paper sheets sit stacked, and the top one slides 48px up to expose the grain of the sheet beneath before easing back.
Best forStacked feature cards, reorderable card decks
1px 결 겹침translateY 시트radius 16

<main class="stage">
<section class="paperstack" aria-label="Three grained paper cards">
<article class="paperstack__sheet paperstack__sheet--3" aria-hidden="true">
<span class="paperstack__line"></span><span class="paperstack__line"></span>
</article>
<article class="paperstack__sheet paperstack__sheet--2" aria-hidden="true">
<span class="paperstack__tag">Second</span>
<span class="paperstack__line"></span>
</article>
<article class="paperstack__sheet paperstack__sheet--1">
<p class="paperstack__eyebrow">Feature card</p>
<h2 class="paperstack__title">Fast checkout</h2>
<p class="paperstack__desc">A paper card with visible grain</p>
</article>
</section>
</main>@use "../../../../site/static/demo-tokens" as *;
// 스큐어모픽 종이 — base64 노이즈 없이 repeating-linear-gradient 세 겹으로 미세한 결을 만든다.
// 결 방향을 섞는다(0deg·90deg·48deg) — 한 방향만 쓰면 줄무늬로 보인다
$grain-a: rgba(122, 105, 78, .06); // 어두운 결 — 세로
$grain-b: rgba(122, 105, 78, .05); // 어두운 결 — 가로
$grain-c: rgba(255, 255, 255, .5); // 밝은 결 — 비스듬한 실
$paper-face: $subject-cream; // 종이 면 (토큰)
$paper-ink: #574a38; // 종이 위 글자 — cream 대비 7.1:1
$duration: $dur-loop; // 맨 위 시트가 올라갔다 돌아오는 주기
$easing: $ease-spring; // 종이가 살짝 튀어 오른다
$color: $subject-cream;
@include stage($stage-orange);
.paperstack {
position: relative;
width: clamp(200px, 62vw, 280px);
}
// 시트 공통 — 결 그라디언트 + 아래 두 장은 살짝 비틀려 겹침을 보여준다
.paperstack__sheet {
position: absolute;
top: 0; left: 0;
box-sizing: border-box;
width: 100%;
padding: $sp-4;
border-radius: $r-card;
background:
repeating-linear-gradient(0deg, $grain-a 0 1px, transparent 1px 3px),
repeating-linear-gradient(90deg, $grain-b 0 1px, transparent 1px 4px),
repeating-linear-gradient(48deg, $grain-c 0 1px, transparent 1px 7px),
$paper-face;
box-shadow: 0 6px 10px rgba(23, 20, 26, .3);
}
.paperstack__sheet--2 {
top: 10px; left: 8px;
transform: rotate(1.6deg);
height: 152px;
}
.paperstack__sheet--3 {
top: 20px; left: -6px;
transform: rotate(-2.2deg);
height: 148px;
}
// 맨 위 시트 — 48px 위로 미끄러져 아래 시트의 결을 드러낸다
.paperstack__sheet--1 {
position: relative;
height: 156px;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: $sp-1;
z-index: 1;
animation: sheetSlide $duration $easing infinite;
}
.paperstack__eyebrow {
margin: 0;
color: #8a6d3f;
font-weight: 700; font-size: clamp(9px, 2.4vw, 11px); line-height: 1;
font-family: inherit;
letter-spacing: .12em;
text-transform: uppercase;
}
.paperstack__title {
margin: 0;
color: $paper-ink;
font-weight: 800; font-size: clamp(16px, 4.2vw, 22px); line-height: 1.2;
font-family: inherit;
}
.paperstack__desc {
margin: 0;
color: $paper-ink;
font-weight: 500; font-size: clamp(11px, 2.8vw, 13px); line-height: 1.4;
font-family: inherit;
}
.paperstack__tag {
margin: $sp-2 0 0;
border-radius: $r-xs;
padding: $sp-1 $sp-2;
background: #e9dcc2;
color: #574a38;
font-weight: 700; font-size: 10px; line-height: 1;
font-family: inherit;
}
.paperstack__line {
display: block;
height: 8px;
margin: $sp-2 0 0;
border-radius: $r-pill;
background: rgba(87, 74, 56, .16);
}
// 0%·12% 정지 → 46%까지 48px 상승 → 잠시 멈춤 → 90%까지 복귀. 텔레포트 없다
@keyframes sheetSlide {
0%, 10% { transform: translateY(0); }
46%, 58% { transform: translateY(-48px); }
88%, 100% { transform: translateY(0); }
}
@media (max-width: 340px), (max-height: 220px) {
.paperstack { width: 208px; }
.paperstack__sheet--1 { height: 128px; padding: $sp-3; }
.paperstack__sheet--2, .paperstack__sheet--3 { height: 124px; }
.paperstack__sheet--2 { top: 8px; }
.paperstack__sheet--3 { top: 16px; }
.paperstack__desc { display: none; }
@keyframes sheetSlide {
0%, 10% { transform: translateY(0); }
46%, 58% { transform: translateY(-20px); }
88%, 100% { transform: translateY(0); }
}
}
@media (prefers-reduced-motion: reduce) {
.paperstack__sheet--1 { 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: #ff4d1f;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.paperstack {
position: relative;
width: clamp(200px, 62vw, 280px);
}
.paperstack__sheet {
position: absolute;
top: 0;
left: 0;
box-sizing: border-box;
width: 100%;
padding: 16px;
border-radius: 16px;
background: repeating-linear-gradient(0deg, rgba(122, 105, 78, 0.06) 0 1px, transparent 1px 3px), repeating-linear-gradient(90deg, rgba(122, 105, 78, 0.05) 0 1px, transparent 1px 4px), repeating-linear-gradient(48deg, rgba(255, 255, 255, 0.5) 0 1px, transparent 1px 7px), #fff7e6;
box-shadow: 0 6px 10px rgba(23, 20, 26, 0.3);
}
.paperstack__sheet--2 {
top: 10px;
left: 8px;
transform: rotate(1.6deg);
height: 152px;
}
.paperstack__sheet--3 {
top: 20px;
left: -6px;
transform: rotate(-2.2deg);
height: 148px;
}
.paperstack__sheet--1 {
position: relative;
height: 156px;
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 4px;
z-index: 1;
animation: sheetSlide 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.paperstack__eyebrow {
margin: 0;
color: #8a6d3f;
font-weight: 700;
font-size: clamp(9px, 2.4vw, 11px);
line-height: 1;
font-family: inherit;
letter-spacing: 0.12em;
text-transform: uppercase;
}
.paperstack__title {
margin: 0;
color: #574a38;
font-weight: 800;
font-size: clamp(16px, 4.2vw, 22px);
line-height: 1.2;
font-family: inherit;
}
.paperstack__desc {
margin: 0;
color: #574a38;
font-weight: 500;
font-size: clamp(11px, 2.8vw, 13px);
line-height: 1.4;
font-family: inherit;
}
.paperstack__tag {
margin: 8px 0 0;
border-radius: 4px;
padding: 4px 8px;
background: #e9dcc2;
color: #574a38;
font-weight: 700;
font-size: 10px;
line-height: 1;
font-family: inherit;
}
.paperstack__line {
display: block;
height: 8px;
margin: 8px 0 0;
border-radius: 999px;
background: rgba(87, 74, 56, 0.16);
}
@keyframes sheetSlide {
0%, 10% {
transform: translateY(0);
}
46%, 58% {
transform: translateY(-48px);
}
88%, 100% {
transform: translateY(0);
}
}
@media (max-width: 340px), (max-height: 220px) {
.paperstack {
width: 208px;
}
.paperstack__sheet--1 {
height: 128px;
padding: 12px;
}
.paperstack__sheet--2, .paperstack__sheet--3 {
height: 124px;
}
.paperstack__sheet--2 {
top: 8px;
}
.paperstack__sheet--3 {
top: 16px;
}
.paperstack__desc {
display: none;
}
@keyframes sheetSlide {
0%, 10% {
transform: translateY(0);
}
46%, 58% {
transform: translateY(-20px);
}
88%, 100% {
transform: translateY(0);
}
}
}
@media (prefers-reduced-motion: reduce) {
.paperstack__sheet--1 {
animation: none !important;
transform: none;
}
}import type { CSSProperties } from "react";
import styles from "./PaperGrainCard.module.scss";
export type PaperGrainCardProps = {
eyebrow?: string;
title?: string;
desc?: string;
tag?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function PaperGrainCard({
eyebrow = "기능 카드",
title = "빠른 결제",
desc = "결이 보이는 종이 카드",
tag = "두 번째",
duration = 2000,
easing = "cubic-bezier(.2, .8, .2, 1)",
color = "#fff7e6",
}: PaperGrainCardProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.paperstack} style={vars} aria-label="종이 결 카드 세 장">
<article className={`${styles.sheet} ${styles.sheet3}`} aria-hidden="true">
<span className={styles.line}></span><span className={styles.line}></span>
</article>
<article className={`${styles.sheet} ${styles.sheet2}`} aria-hidden="true">
<span className={styles.tag}>{tag}</span>
<span className={styles.line}></span>
</article>
<article className={`${styles.sheet} ${styles.sheet1}`}>
<p className={styles.eyebrow}>{eyebrow}</p>
<h2 className={styles.title}>{title}</h2>
<p className={styles.desc}>{desc}</p>
</article>
</section>
);
}03 Paper grain feature card — Measured area 34.744% · moving frames 17/23Original by GODRICH · SCSS and React ship in the zip · Skeuomorphism Design Kit: 9 Tactile Landing Parts
The post this demo lives in
Skeuomorphism Design Kit: 9 Tactile Landing PartsSkeuomorphism design brings the feel of real materials — leather, metal, wood, glass — onto the screen. This kit rebuild See all demos in this post


