07찢은 가장자리 CTA 밴드
위아래가 지그재그로 찢긴 산호색 띠 안에서 흰 버튼이 8px 떠올랐다가 바닥에 붙고, 그림자 두께가 같이 줄어듭니다.
쓰임페이지 하단 전환 유도 띠
conic-gradient 마스크그림자=두께radius 12

<main class="stage">
<section class="tornband">
<div class="tornband__in">
<p class="tornband__t">오늘 오려 붙이세요</p>
<button class="tornband__btn" type="button">
<span>부품 받기</span>
<span class="tornband__ic" 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 128l-72 72V56Z" opacity=".2"/><path d="m221.66 122.34l-72-72A8 8 0 0 0 136 56v64H40a8 8 0 0 0 0 16h96v64a8 8 0 0 0 13.66 5.66l72-72a8 8 0 0 0 0-11.32M152 180.69V75.31L204.69 128Z"/></g></svg>
</span>
</button>
</div>
</section>
</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-coral; // 띠의 색종이 색. 흰 글자 대비 4.93:1
@include stage($paper-base);
.tornband {
box-sizing: border-box;
width: clamp(252px, 84vw, 372px);
padding: $sp-6 $sp-5;
background: $color;
// 위아래를 지그재그로 찢어낸 가장자리 — 마스크 세 겹을 겹쳐 만든다 (애니메이션 대상 아님)
-webkit-mask:
conic-gradient(from -45deg at bottom, #0000, #000 1deg 89deg, #0000 90deg) bottom / 14px 7px repeat-x,
conic-gradient(from 135deg at top, #0000, #000 1deg 89deg, #0000 90deg) top / 14px 7px repeat-x,
linear-gradient(#000 0 0) center / 100% calc(100% - 14px) no-repeat;
mask:
conic-gradient(from -45deg at bottom, #0000, #000 1deg 89deg, #0000 90deg) bottom / 14px 7px repeat-x,
conic-gradient(from 135deg at top, #0000, #000 1deg 89deg, #0000 90deg) top / 14px 7px repeat-x,
linear-gradient(#000 0 0) center / 100% calc(100% - 14px) no-repeat;
}
.tornband__in {
display: flex;
flex-direction: column;
align-items: center;
gap: $sp-4;
}
.tornband__t {
margin: 0;
color: $paper-sheet;
font-weight: 800; font-size: clamp(15px, 3.6vw, 19px); line-height: 1.25;
font-family: inherit;
text-align: center;
}
// 종이 버튼 — 그림자가 두께다. 눌리면 두께가 사라진다
.tornband__btn {
display: inline-flex;
align-items: center;
gap: $sp-2;
border: 0;
border-radius: $r-control;
background: $paper-sheet;
color: $paper-ink;
padding: $sp-3 $sp-6;
font-weight: 800; font-size: 14px; line-height: 1;
font-family: inherit;
box-shadow: 0 6px 0 rgba($paper-ink, .3);
animation: paperPress $duration $easing infinite;
}
.tornband__ic { font-size: 16px; line-height: 1; color: $color; }
.tornband__btn:focus-visible { outline: 3px solid $paper-sheet; outline-offset: 3px; }
// 데모: 버튼이 8px 떠올랐다가 바닥에 붙는다. 그림자 두께가 6px→0 으로 같이 줄어든다
@keyframes paperPress {
0%, 100% { transform: translateY(-8px); box-shadow: 0 14px 0 rgba($paper-ink, .3); }
50% { transform: translateY(0); box-shadow: 0 2px 0 rgba($paper-ink, .3); }
}
@media (max-width: 340px), (max-height: 220px) {
.tornband { width: 268px; padding: $sp-5 $sp-3; }
.tornband__in { gap: $sp-3; }
.tornband__t { font-size: 13px; }
.tornband__btn { padding: $sp-2 $sp-4; font-size: 12px; }
.tornband__ic { font-size: 13px; }
}
@media (prefers-reduced-motion: reduce) {
.tornband__btn { 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: #fbf6ec;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.tornband {
box-sizing: border-box;
width: clamp(252px, 84vw, 372px);
padding: 24px 20px;
background: #b97b71;
-webkit-mask: conic-gradient(from -45deg at bottom, rgba(0, 0, 0, 0), #000 1deg 89deg, rgba(0, 0, 0, 0) 90deg) bottom/14px 7px repeat-x, conic-gradient(from 135deg at top, rgba(0, 0, 0, 0), #000 1deg 89deg, rgba(0, 0, 0, 0) 90deg) top/14px 7px repeat-x, linear-gradient(#000 0 0) center/100% calc(100% - 14px) no-repeat;
mask: conic-gradient(from -45deg at bottom, rgba(0, 0, 0, 0), #000 1deg 89deg, rgba(0, 0, 0, 0) 90deg) bottom/14px 7px repeat-x, conic-gradient(from 135deg at top, rgba(0, 0, 0, 0), #000 1deg 89deg, rgba(0, 0, 0, 0) 90deg) top/14px 7px repeat-x, linear-gradient(#000 0 0) center/100% calc(100% - 14px) no-repeat;
}
.tornband__in {
display: flex;
flex-direction: column;
align-items: center;
gap: 16px;
}
.tornband__t {
margin: 0;
color: #ffffff;
font-weight: 800;
font-size: clamp(15px, 3.6vw, 19px);
line-height: 1.25;
font-family: inherit;
text-align: center;
}
.tornband__btn {
display: inline-flex;
align-items: center;
gap: 8px;
border: 0;
border-radius: 12px;
background: #ffffff;
color: #2a2320;
padding: 12px 24px;
font-weight: 800;
font-size: 14px;
line-height: 1;
font-family: inherit;
box-shadow: 0 6px 0 rgba(42, 35, 32, 0.3);
animation: paperPress 2s cubic-bezier(0.2, 0.8, 0.2, 1) infinite;
}
.tornband__ic {
font-size: 16px;
line-height: 1;
color: #b97b71;
}
.tornband__btn:focus-visible {
outline: 3px solid #ffffff;
outline-offset: 3px;
}
@keyframes paperPress {
0%, 100% {
transform: translateY(-8px);
box-shadow: 0 14px 0 rgba(42, 35, 32, 0.3);
}
50% {
transform: translateY(0);
box-shadow: 0 2px 0 rgba(42, 35, 32, 0.3);
}
}
@media (max-width: 340px), (max-height: 220px) {
.tornband {
width: 268px;
padding: 20px 12px;
}
.tornband__in {
gap: 12px;
}
.tornband__t {
font-size: 13px;
}
.tornband__btn {
padding: 8px 16px;
font-size: 12px;
}
.tornband__ic {
font-size: 13px;
}
}
@media (prefers-reduced-motion: reduce) {
.tornband__btn {
animation: none !important;
transform: none;
}
}import type { CSSProperties } from "react";
import styles from "./TornBand.module.scss";
export type TornBandProps = {
title?: string;
cta?: string;
/** 한 주기(기본 2000ms) */
duration?: number;
easing?: string;
/** 띠의 색종이 색. 흰 글자와 4.5:1 이상이어야 한다 */
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
const ARROW = (
<svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" viewBox="0 0 256 256"><g fill="currentColor"><path d="m216 128l-72 72V56Z" opacity=".2"/><path d="m221.66 122.34l-72-72A8 8 0 0 0 136 56v64H40a8 8 0 0 0 0 16h96v64a8 8 0 0 0 13.66 5.66l72-72a8 8 0 0 0 0-11.32M152 180.69V75.31L204.69 128Z"/></g></svg>
);
export default function TornBand({
title = "오늘 오려 붙이세요",
cta = "부품 받기",
duration = 2000,
easing = "cubic-bezier(.2,.8,.2,1)",
color = "#cc3d26",
}: TornBandProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<section className={styles.tornband} style={vars}>
<div className={styles.in}>
<p className={styles.t}>{title}</p>
<button className={styles.btn} type="button">
<span>{cta}</span>
<span className={styles.ic} aria-hidden="true">{ARROW}</span>
</button>
</div>
</section>
);
}07 찢은 가장자리 CTA 밴드 — 측정 누적면적 2.563% · 움직인 프레임 21/23GODRICH 원작 · zip 에 SCSS·React 포함 · 종이 질감 디자인 랜딩 키트, 오려 붙인 섹션 9개
이 데모가 속한 글
종이 질감 디자인 랜딩 키트, 오려 붙인 섹션 9개종이 질감 디자인은 색종이를 가위로 오려 붙인 것처럼 면마다 흐림 없는 그림자로 두께를 주고, 색은 다섯 가지 안에서만 쓰는 스타일입니다. 종이 사진을 깔지 않고 그림자 값 하나만으로 종이처럼 보이게 만드는 것이 전 이 글의 데모 전부 보기


