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">2,900円</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 オリジナル · SCSS と React は zip に同梱 · CSS ホログラムカード効果9選 — ステッカー・バッジ・チケット
このデモが属する記事
CSS ホログラムカード効果9選 — ステッカー・バッジ・チケットCSS ホログラムとは、角度によって光の反射が変わる箔素材を、CSSのグラデーションとアニメーションだけで再現する技法です。ポケモンカードのような輝きは何度も画面で見かけますが、いざコードにしようとすると手が止まることが多いので、カード・ス この記事のデモをすべて見る


