04Đung đưa nhẹ (yurayura)
`.o-yura` ghim transform-origin lên đỉnh và đưa rotate qua lại thật chậm, nên tag sale treo lơ lửng trông như đang đung đưa trong gió.
Dùng choTag sale treo, icon chuông trang trí, điểm nhấn banner sự kiện
rotatetransform-origin: top2.4s

<main class="stage">
<div class="tagwrap">
<span class="tagwrap__string" aria-hidden="true"></span>
<span class="tagwrap__tag o-yura">Giảm giá</span>
</div>
<span class="tag">Đung đưa</span>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: 2.4s;
$easing: ease-in-out;
$color: $subject-blue;
// ── 데모 무대 ───────────────────────────────
@include stage($stage-paper);
.stage { position: relative; }
.tagwrap {
display: flex;
flex-direction: column;
align-items: center;
}
.tagwrap__string {
width: 2px;
height: 28px;
background: $stage-ink;
opacity: .4;
}
.tagwrap__tag {
padding: $sp-3 $sp-6;
border-radius: $r-sm;
background: $stage-ink;
color: $subject-cream;
font-weight: 700;
font-size: clamp(16px, 4.5vw, 20px);
line-height: 1;
font-family: inherit;
box-shadow: $shadow-raised;
transform-origin: top center; // 규칙8: 줄 매달림 top 피벗
}
.tag {
position: absolute;
top: $sp-4;
right: $sp-4;
padding: $sp-1 $sp-3;
border-radius: $r-sm;
background: rgba(23, 20, 26, .08);
color: $stage-ink;
font-weight: 700;
font-size: 12px;
line-height: 1;
font-family: inherit;
letter-spacing: .04em;
}
// ── transform-origin 을 위쪽에 고정하고 rotate 만 아주 천천히 오간다 ─────
.o-yura {
animation: o-yura $duration $easing infinite;
color: $color;
}
@keyframes o-yura {
0%, 100% { transform: rotate(-6deg); }
50% { transform: rotate(6deg); }
}
// ── 접근성 ──────────────────────────────────────────
@media (prefers-reduced-motion: reduce) {
.o-yura { animation: 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);
}
.stage {
position: relative;
}
.tagwrap {
display: flex;
flex-direction: column;
align-items: center;
}
.tagwrap__string {
width: 2px;
height: 28px;
background: #17141a;
opacity: 0.4;
}
.tagwrap__tag {
padding: 12px 24px;
border-radius: 8px;
background: #17141a;
color: #fff7e6;
font-weight: 700;
font-size: clamp(16px, 4.5vw, 20px);
line-height: 1;
font-family: inherit;
box-shadow: 0 14px 24px rgba(23, 20, 26, 0.32);
transform-origin: top center;
}
.tag {
position: absolute;
top: 16px;
right: 16px;
padding: 4px 12px;
border-radius: 8px;
background: rgba(23, 20, 26, 0.08);
color: #17141a;
font-weight: 700;
font-size: 12px;
line-height: 1;
font-family: inherit;
letter-spacing: 0.04em;
}
.o-yura {
animation: o-yura 2.4s ease-in-out infinite;
color: #2f6df6;
}
@keyframes o-yura {
0%, 100% {
transform: rotate(-6deg);
}
50% {
transform: rotate(6deg);
}
}
@media (prefers-reduced-motion: reduce) {
.o-yura {
animation: none;
}
}import type { CSSProperties, ReactNode } from "react";
import styles from "./SwayTag.module.scss";
export type SwayTagProps = {
children: ReactNode;
/** 한 번 오가는 데 걸리는 시간 (기본 2400ms) */
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & {
"--duration"?: string;
"--easing"?: string;
"--color"?: string;
};
export default function SwayTag({
children,
duration = 2400,
easing = "ease-in-out",
color = "#2f6df6",
}: SwayTagProps) {
const vars: Vars = {
"--duration": `${duration}ms`,
"--easing": easing,
"--color": color,
};
return (
<div className={styles.wrap}>
<span className={styles.string} aria-hidden="true" />
<span className={styles.tag} style={vars}>
{children}
</span>
</div>
);
}04 Đung đưa nhẹ (yurayura) — Diện tích đo được 1.628% · khung hình chuyển động 22/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 css bounce animation — dán một dòng là chạy
Bài viết chứa demo này
9 css bounce animation — dán một dòng là chạycss bounce animation là chuyển động cho phần tử nổi lên, nảy, rung và đung đưa như đang sống. Chín mục gọi theo từ tượng Xem tất cả demo trong bài này


