01Bồng bềnh lên xuống
Chỉ cần gắn `.a-float`, một vòng keyframes di chuyển translateY lên xuống chậm rãi chạy ngay, khiến phần tử nào cũng trông như đang lơ lửng.
Dùng choHuy hiệu minh họa, icon trang trí, ảnh mockup ở phần hero của trang landing
translateYease-in-out3초

<main class="stage">
<div class="badge a-float">
<span class="badge__dot" aria-hidden="true"></span>
Hàng mới
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개. 변형은 이것만 바꾼다 ──────────────────────
$duration: 3s; // 한 번 오가는 데 걸리는 시간
$easing: ease-in-out; // 뜨고 가라앉는 리듬이라 스프링보다 사인 곡선에 가까운 이 값을 쓴다
$color: $subject-blue; // 점 색
// ── 데모 무대 ───────────────────────────────
@include stage($stage-ink);
// ── 배지: .a-float 하나만 붙이면 어떤 요소든 떠오른다 ─────
.badge {
display: inline-flex;
align-items: center;
gap: clamp(8px, 3vw, 12px);
padding: clamp(14px, 4vw, 18px) clamp(24px, 6vw, 32px);
border-radius: $r-pill;
background: $subject-cream;
color: $stage-ink;
font-weight: 700; font-size: clamp(16px, 4.5vw, 20px); line-height: 1; font-family: inherit;
box-shadow: $shadow-raised;
}
.badge__dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: $color;
flex: none;
}
.a-float {
animation: float $duration $easing infinite;
}
@keyframes float {
0%, 100% { transform: translateY(0); }
50% { transform: translateY(-16px); }
}
// ── 접근성: 움직임 줄이기 설정이면 뜨지 않는다 ──────────────
@media (prefers-reduced-motion: reduce) {
.a-float { animation: none; }
}html, body {
margin: 0;
overflow: hidden;
}
.stage {
box-sizing: border-box;
min-height: 100vh;
display: grid;
place-items: center;
margin: 0;
background: #17141a;
font-family: -apple-system, "Segoe UI", "Noto Sans KR", sans-serif;
padding: clamp(8px, 4vw, 24px);
}
.badge {
display: inline-flex;
align-items: center;
gap: clamp(8px, 3vw, 12px);
padding: clamp(14px, 4vw, 18px) clamp(24px, 6vw, 32px);
border-radius: 999px;
background: #fff7e6;
color: #17141a;
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);
}
.badge__dot {
width: 10px;
height: 10px;
border-radius: 50%;
background: #2f6df6;
flex: none;
}
.a-float {
animation: float 3s ease-in-out infinite;
}
@keyframes float {
0%, 100% {
transform: translateY(0);
}
50% {
transform: translateY(-16px);
}
}
@media (prefers-reduced-motion: reduce) {
.a-float {
animation: none;
}
}import type { CSSProperties, ReactNode } from "react";
import styles from "./FloatBadge.module.scss";
export type FloatBadgeProps = {
children: ReactNode;
/** 한 번 오가는 데 걸리는 시간 (기본 3000ms) */
duration?: number;
/** CSS easing (기본 ease-in-out) */
easing?: string;
/** 점 색 */
color?: string;
};
type Vars = CSSProperties & {
"--duration"?: string;
"--easing"?: string;
"--color"?: string;
};
export default function FloatBadge({
children,
duration = 3000,
easing = "ease-in-out",
color = "#2f6df6",
}: FloatBadgeProps) {
const vars: Vars = {
"--duration": `${duration}ms`,
"--easing": easing,
"--color": color,
};
return (
<div className={styles.badge} style={vars}>
<span className={styles.dot} aria-hidden="true" />
{children}
</div>
);
}01 Bồng bềnh lên xuống — Diện tích đo được 4.311% · khung hình chuyển động 23/23Nguyên tác GODRICH · SCSS và React nằm trong tệp zip · 9 CSS Keyframe Animations — dán 1 dòng là chạy
Bài viết chứa demo này
9 CSS Keyframe Animations — dán 1 dòng là chạyCSS keyframe animation là chuyển động lặp được định nghĩa bằng @keyframes rồi gộp vào một class duy nhất, dán vào phần t Xem tất cả demo trong bài này

