06테이프로 붙인 후기
메모지 위 두 귀퉁이를 마스킹 테이프로 눌러 붙이고, 종이 바깥의 축을 중심으로 좌우 1.6도씩 천천히 흔들립니다.
쓰임후기·추천사 카드, 공지 메모
transform-origin 50% -12px테이프 두 장radius 8

<main class="stage">
<figure class="tapenote">
<span class="tapenote__tape tapenote__tape--l" aria-hidden="true"></span>
<span class="tapenote__tape tapenote__tape--r" aria-hidden="true"></span>
<span class="tapenote__q" 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="M108 72v72H40a8 8 0 0 1-8-8V72a8 8 0 0 1 8-8h60a8 8 0 0 1 8 8m108-8h-60a8 8 0 0 0-8 8v64a8 8 0 0 0 8 8h68V72a8 8 0 0 0-8-8" opacity=".2"/><path d="M100 56H40a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80H40V72h60Zm116-80h-60a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80h-60V72h60Z"/></g></svg>
</span>
<blockquote class="tapenote__body">색종이 세 장으로 첫 화면이 끝났습니다.</blockquote>
<figcaption class="tapenote__by">
<span class="tapenote__ava" aria-hidden="true">ㅂ</span>
<span class="tapenote__who">바이브코더 · 1인 개발</span>
</figcaption>
</figure>
</main>@use "../../../../site/static/demo-tokens" as *;
// 페이퍼컷 팔레트
$paper-base: #fbf6ec;
$paper-sheet: #ffffff;
$paper-ink: #2a2320;
$paper-stone: #5c5048;
$paper-coral: #b97b71;
$paper-sky: #6a8db5;
$paper-leaf: #2b7d5a;
$paper-butter: #d6c9ab;
$paper-edge: #e7dccb;
$duration: $dur-loop; // 메모가 좌우로 한 번 흔들리는 주기
$easing: $ease-spring; // 테이프에 매달린 종이의 감쇠
$color: $paper-butter; // 마스킹 테이프 색
@include stage($paper-sheet);
.tapenote {
box-sizing: border-box;
position: relative;
margin: 0;
width: clamp(224px, 74vw, 308px);
padding: $sp-6 $sp-5 $sp-5;
border-radius: $r-sm;
background: $paper-base;
box-shadow: 0 8px 16px rgba($paper-ink, .14);
transform-origin: 50% -12px; // 규칙8: 종이 밖 축 흔들림(테이프)
animation: noteSway $duration $easing infinite;
}
// 위쪽 두 귀퉁이를 눌러 붙인 마스킹 테이프
.tapenote__tape {
position: absolute;
top: -10px;
width: 56px;
height: $sp-5;
background: $color;
opacity: .92;
border-radius: $r-xs;
}
.tapenote__tape--l { left: $sp-4; transform: rotate(-6deg); }
.tapenote__tape--r { right: $sp-4; transform: rotate(5deg); }
.tapenote__q {
display: block;
color: $paper-coral;
font-size: 22px;
line-height: 1;
margin-bottom: $sp-2;
}
.tapenote__body {
margin: 0 0 $sp-4;
color: $paper-ink;
font-weight: 600; font-size: 15px; line-height: 1.45;
font-family: inherit;
}
.tapenote__by {
display: flex;
align-items: center;
gap: $sp-2;
}
.tapenote__ava {
width: 26px;
height: 26px;
display: grid;
place-items: center;
border-radius: 50%;
background: $paper-leaf;
color: $paper-sheet;
font-weight: 700; font-size: 12px; line-height: 1;
font-family: inherit;
}
.tapenote__who {
color: $paper-stone;
font-weight: 500; font-size: 11px; line-height: 1;
font-family: inherit;
}
// 데모: 테이프 위쪽을 축으로 좌우로 흔들린다. 축이 종이 밖(-12px)이라 아래쪽이 크게 움직인다
@keyframes noteSway {
0%, 100% { transform: rotate(-1.6deg); }
50% { transform: rotate(1.6deg); }
}
@media (max-width: 340px), (max-height: 220px) {
.tapenote { width: 232px; padding: $sp-5 $sp-3 $sp-3; }
.tapenote__tape { width: 40px; height: $sp-4; top: -8px; }
.tapenote__q { font-size: 16px; margin-bottom: $sp-1; }
.tapenote__body { font-size: 12px; margin-bottom: $sp-2; }
.tapenote__ava { width: 20px; height: 20px; font-size: 10px; }
.tapenote__who { font-size: 10px; }
}
@media (prefers-reduced-motion: reduce) {
.tapenote { 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: #ffffff;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.tapenote {
box-sizing: border-box;
position: relative;
margin: 0;
width: clamp(224px, 74vw, 308px);
padding: 24px 20px 20px;
border-radius: 8px;
background: #fbf6ec;
box-shadow: 0 8px 16px rgba(42, 35, 32, 0.14);
transform-origin: 50% -12px;
animation: noteSway 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.tapenote__tape {
position: absolute;
top: -10px;
width: 56px;
height: 20px;
background: #d6c9ab;
opacity: 0.92;
border-radius: 4px;
}
.tapenote__tape--l {
left: 16px;
transform: rotate(-6deg);
}
.tapenote__tape--r {
right: 16px;
transform: rotate(5deg);
}
.tapenote__q {
display: block;
color: #b97b71;
font-size: 22px;
line-height: 1;
margin-bottom: 8px;
}
.tapenote__body {
margin: 0 0 16px;
color: #2a2320;
font-weight: 600;
font-size: 15px;
line-height: 1.45;
font-family: inherit;
}
.tapenote__by {
display: flex;
align-items: center;
gap: 8px;
}
.tapenote__ava {
width: 26px;
height: 26px;
display: grid;
place-items: center;
border-radius: 50%;
background: #2b7d5a;
color: #ffffff;
font-weight: 700;
font-size: 12px;
line-height: 1;
font-family: inherit;
}
.tapenote__who {
color: #5c5048;
font-weight: 500;
font-size: 11px;
line-height: 1;
font-family: inherit;
}
@keyframes noteSway {
0%, 100% {
transform: rotate(-1.6deg);
}
50% {
transform: rotate(1.6deg);
}
}
@media (max-width: 340px), (max-height: 220px) {
.tapenote {
width: 232px;
padding: 20px 12px 12px;
}
.tapenote__tape {
width: 40px;
height: 16px;
top: -8px;
}
.tapenote__q {
font-size: 16px;
margin-bottom: 4px;
}
.tapenote__body {
font-size: 12px;
margin-bottom: 8px;
}
.tapenote__ava {
width: 20px;
height: 20px;
font-size: 10px;
}
.tapenote__who {
font-size: 10px;
}
}
@media (prefers-reduced-motion: reduce) {
.tapenote {
animation: none !important;
transform: none;
}
}import type { CSSProperties } from "react";
import styles from "./TapeNote.module.scss";
export type TapeNoteProps = {
quote?: string;
initial?: string;
who?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
/** 마스킹 테이프 색 */
color?: string;
/** 좌우로 기우는 각도(deg) */
tilt?: number;
};
type Vars = CSSProperties & {
"--duration"?: string; "--easing"?: string; "--color"?: string; "--tilt"?: string;
};
const QUOTES = (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="M108 72v72H40a8 8 0 0 1-8-8V72a8 8 0 0 1 8-8h60a8 8 0 0 1 8 8m108-8h-60a8 8 0 0 0-8 8v64a8 8 0 0 0 8 8h68V72a8 8 0 0 0-8-8" opacity=".2"/><path d="M100 56H40a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80H40V72h60Zm116-80h-60a16 16 0 0 0-16 16v64a16 16 0 0 0 16 16h60v8a32 32 0 0 1-32 32a8 8 0 0 0 0 16a48.05 48.05 0 0 0 48-48V72a16 16 0 0 0-16-16m0 80h-60V72h60Z"/></g></svg>
);
export default function TapeNote({
quote = "색종이 세 장으로 첫 화면이 끝났습니다.",
initial = "ㅂ",
who = "바이브코더 · 1인 개발",
duration = 2000,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#f6c453",
tilt = 1.6,
}: TapeNoteProps) {
const vars: Vars = {
"--duration": `${duration}ms`, "--easing": easing, "--color": color, "--tilt": `${tilt}deg`,
};
return (
<figure className={styles.tapenote} style={vars}>
<span className={`${styles.tape} ${styles.tapeL}`} aria-hidden="true" />
<span className={`${styles.tape} ${styles.tapeR}`} aria-hidden="true" />
<span className={styles.q} aria-hidden="true">{QUOTES}</span>
<blockquote className={styles.body}>{quote}</blockquote>
<figcaption className={styles.by}>
<span className={styles.ava} aria-hidden="true">{initial}</span>
<span className={styles.who}>{who}</span>
</figcaption>
</figure>
);
}06 테이프로 붙인 후기 — 측정 누적면적 6.52% · 움직인 프레임 18/23GODRICH 원작 · zip 에 SCSS·React 포함 · 종이 질감 디자인 랜딩 키트, 오려 붙인 섹션 9개
이 데모가 속한 글
종이 질감 디자인 랜딩 키트, 오려 붙인 섹션 9개종이 질감 디자인은 색종이를 가위로 오려 붙인 것처럼 면마다 흐림 없는 그림자로 두께를 주고, 색은 다섯 가지 안에서만 쓰는 스타일입니다. 종이 사진을 깔지 않고 그림자 값 하나만으로 종이처럼 보이게 만드는 것이 전 이 글의 데모 전부 보기


