07가격표 리본 홀로그램
가격표 모서리에 걸린 리본이 홀로그램 재질로 빛나고, 호버하면 살짝 흔들리며 빛이 리본 위를 스쳐 지나갑니다.
쓰임할인 가격표, 신상품 리본 태그, 프로모션 배지
rotate(35deg) 리본@keyframes swaybackground-position 스윕

<main class="stage">
<div class="tag is-demo" tabindex="0">
<span class="tag__ribbon" aria-hidden="true">NEW</span>
<div class="tag__body">
<span class="tag__price">29,000원</span>
</div>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
$duration: $dur-base; // 리본이 흔들리는 시간
$easing: $ease-out;
$color: $subject-blue; // 사용 안 함(참고용)
@include stage($stage-orange);
.tag {
position: relative;
width: min(140px, 46vw);
aspect-ratio: 4 / 3;
border-radius: $r-card;
background: $stage-paper;
box-shadow: $shadow-press;
overflow: hidden;
display: grid;
place-items: center;
outline: none;
cursor: pointer;
}
.tag__ribbon {
position: absolute; top: 14px; right: -34px;
width: 120px; padding: 4px 0;
text-align: center;
transform: rotate(35deg);
background: repeating-linear-gradient(115deg, #dcb9d1 0 10%, #b9d9dc 10% 20%, #c8dcb9 20% 30%, #dcdab9 30% 40%);
background-size: 220% 220%;
background-position: 0 0;
font-size: 10px; font-weight: 800; letter-spacing: .05em; color: $stage-ink; font-family: inherit;
box-shadow: 0 2px 6px rgba($stage-ink, .25);
transition: transform $duration $easing, background-position $duration $easing;
}
.tag__price { font-weight: 700; font-size: 15px; color: $stage-ink; font-family: inherit; }
.tag:hover .tag__ribbon,
.tag:focus-visible .tag__ribbon { transform: rotate(30deg) translateY(-2px); background-position: 100% 40%; }
.tag.is-demo .tag__ribbon { animation: ribbon-sway $dur-loop $easing infinite; }
@keyframes ribbon-sway {
0%, 15% { transform: rotate(35deg) translateY(0); background-position: 0 0; }
40% { transform: rotate(28deg) translateY(-3px); background-position: 100% 40%; }
65% { transform: rotate(38deg) translateY(2px); background-position: 0 100%; }
90%, 100% { transform: rotate(35deg) translateY(0); background-position: 0 0; }
}
@media (prefers-reduced-motion: reduce) {
.tag__ribbon { transition: none; }
.tag.is-demo .tag__ribbon { animation: 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);
}
.tag {
position: relative;
width: min(140px, 46vw);
aspect-ratio: 4/3;
border-radius: 16px;
background: #ffffff;
box-shadow: 0 6px 12px rgba(23, 20, 26, 0.32);
overflow: hidden;
display: grid;
place-items: center;
outline: none;
cursor: pointer;
}
.tag__ribbon {
position: absolute;
top: 14px;
right: -34px;
width: 120px;
padding: 4px 0;
text-align: center;
transform: rotate(35deg);
background: repeating-linear-gradient(115deg, #dcb9d1 0 10%, #b9d9dc 10% 20%, #c8dcb9 20% 30%, #dcdab9 30% 40%);
background-size: 220% 220%;
background-position: 0 0;
font-size: 10px;
font-weight: 800;
letter-spacing: 0.05em;
color: #17141a;
font-family: inherit;
box-shadow: 0 2px 6px rgba(23, 20, 26, 0.25);
transition: transform 300ms ease-out, background-position 300ms ease-out;
}
.tag__price {
font-weight: 700;
font-size: 15px;
color: #17141a;
font-family: inherit;
}
.tag:hover .tag__ribbon,
.tag:focus-visible .tag__ribbon {
transform: rotate(30deg) translateY(-2px);
background-position: 100% 40%;
}
.tag.is-demo .tag__ribbon {
animation: ribbon-sway 2s ease-out infinite;
}
@keyframes ribbon-sway {
0%, 15% {
transform: rotate(35deg) translateY(0);
background-position: 0 0;
}
40% {
transform: rotate(28deg) translateY(-3px);
background-position: 100% 40%;
}
65% {
transform: rotate(38deg) translateY(2px);
background-position: 0 100%;
}
90%, 100% {
transform: rotate(35deg) translateY(0);
background-position: 0 0;
}
}
@media (prefers-reduced-motion: reduce) {
.tag__ribbon {
transition: none;
}
.tag.is-demo .tag__ribbon {
animation: none;
}
}import type { CSSProperties } from "react";
import styles from "./PriceTagRibbon.module.scss";
export type PriceTagRibbonProps = {
ribbon: string;
price: string;
/** 리본이 흔들리는 시간 (기본 300ms) */
duration?: number;
easing?: string;
/** 사용 안 함(참고용) */
color?: string;
};
type Vars = CSSProperties & { "--duration"?: string; "--easing"?: string; "--color"?: string };
export default function PriceTagRibbon({
ribbon,
price,
duration = 300,
easing = "ease-out",
color = "#2f6df6",
}: PriceTagRibbonProps) {
const vars: Vars = { "--duration": `${duration}ms`, "--easing": easing, "--color": color };
return (
<div className={styles.tag} style={vars} tabIndex={0}>
<span className={styles.ribbon} aria-hidden="true">{ribbon}</span>
<div className={styles.body}>
<span className={styles.price}>{price}</span>
</div>
</div>
);
}07 가격표 리본 홀로그램 — 측정 누적면적 1.608% · 움직인 프레임 19/23GODRICH 원작 · zip 에 SCSS·React 포함 · CSS 홀로그램 카드 효과 9가지 — 스티커·뱃지·티켓
이 데모가 속한 글
CSS 홀로그램 카드 효과 9가지 — 스티커·뱃지·티켓CSS 홀로그램 효과는 각도에 따라 빛이 다르게 반사되는 홀로그램 재질을, CSS 그라디언트와 애니메이션만으로 흉내 내는 기법입니다. 포켓몬 카드처럼 빛나는 표면을 눈으로는 자주 보지만 막상 코드로 옮기려면 막막한 이 글의 데모 전부 보기


