03小数点フォーマットのカウントアップ
整数部と小数部をそれぞれ別の@propertyとして同時に数え、小数部はpadされたcounter-styleで2桁に揃えて小数点の後に付ける。
使いどころ評価点、平均星評価、温度や単位の数値
value×100pad:2소수점 둘째 자리

<main class="stage">
<div class="counter is-demo">
<span class="counter__num" aria-hidden="true"></span>
<span class="counter__sr">4.98</span>
<span class="counter__label">平均評価</span>
</div>
</main>@use "../../../../site/static/demo-tokens" as *;
// ── 변수 3개 ──────────────────────────────────────────
$duration: $dur-loop;
$easing: $ease-out;
$color: $subject-cream;
@include stage($stage-orange);
// 정수부(whole)와 소수부(frac)를 각각 다른 정수 커스텀 속성으로 함께 센다
@property --whole {
syntax: '<integer>';
initial-value: 0;
inherits: false;
}
@property --frac {
syntax: '<integer>';
initial-value: 0;
inherits: false;
}
// 소수부는 항상 두 자리 — 8이 ".8" 이 아니라 ".08" 로 보이게
@counter-style padded-2 {
system: numeric;
symbols: '0' '1' '2' '3' '4' '5' '6' '7' '8' '9';
suffix: '';
pad: 2 '0';
}
.counter {
text-align: center;
&__num {
--whole: 0;
--frac: 0;
counter-reset: whole var(--whole) frac var(--frac);
display: block;
font-weight: 800; font-size: clamp(36px, 11vw, 60px); line-height: 1; font-family: inherit;
font-variant-numeric: tabular-nums;
color: $color;
&::before { content: counter(whole) "." counter(frac, padded-2); }
}
&__sr {
position: absolute;
width: 1px; height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
&__label {
display: block;
margin-top: clamp(6px, 2vw, 10px);
font-weight: 600; font-size: clamp(12px, 3.4vw, 15px); line-height: 1; font-family: inherit;
color: $subject-ink; // 주황 면 위 크림 글자는 3.11:1 — 잉크로 5.50:1
}
}
.counter.is-demo .counter__num {
animation: decimal-up $duration $easing infinite;
}
@keyframes decimal-up {
0% { --whole: 0; --frac: 0; }
65% { --whole: 4; --frac: 98; }
100% { --whole: 4; --frac: 98; }
}
@media (prefers-reduced-motion: reduce) {
.counter.is-demo .counter__num { animation: none; --whole: 4; --frac: 98; }
}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);
}
@property --whole {
syntax: "<integer>";
initial-value: 0;
inherits: false;
}
@property --frac {
syntax: "<integer>";
initial-value: 0;
inherits: false;
}
@counter-style padded-2 {
system: numeric;
symbols: "0" "1" "2" "3" "4" "5" "6" "7" "8" "9";
suffix: "";
pad: 2 "0";
}
.counter {
text-align: center;
}
.counter__num {
--whole: 0;
--frac: 0;
counter-reset: whole var(--whole) frac var(--frac);
display: block;
font-weight: 800;
font-size: clamp(36px, 11vw, 60px);
line-height: 1;
font-family: inherit;
font-variant-numeric: tabular-nums;
color: #fff7e6;
}
.counter__num::before {
content: counter(whole) "." counter(frac, padded-2);
}
.counter__sr {
position: absolute;
width: 1px;
height: 1px;
overflow: hidden;
clip-path: inset(50%);
white-space: nowrap;
}
.counter__label {
display: block;
margin-top: clamp(6px, 2vw, 10px);
font-weight: 600;
font-size: clamp(12px, 3.4vw, 15px);
line-height: 1;
font-family: inherit;
color: #17141a;
}
.counter.is-demo .counter__num {
animation: decimal-up 2s ease-out infinite;
}
@keyframes decimal-up {
0% {
--whole: 0;
--frac: 0;
}
65% {
--whole: 4;
--frac: 98;
}
100% {
--whole: 4;
--frac: 98;
}
}
@media (prefers-reduced-motion: reduce) {
.counter.is-demo .counter__num {
animation: none;
--whole: 4;
--frac: 98;
}
}import type { CSSProperties } from "react";
import styles from "./DecimalFormatCounter.module.scss";
export type DecimalFormatCounterProps = {
/** 4.98 처럼 소수 둘째 자리까지 */
value: number;
label: string;
duration?: number;
easing?: string;
color?: string;
};
type Vars = CSSProperties & {
"--duration"?: string; "--easing"?: string; "--color"?: string;
"--whole-target"?: number; "--frac-target"?: number;
};
export default function DecimalFormatCounter({
value, label, duration = 2000, easing = "ease-out", color = "#fff7e6",
}: DecimalFormatCounterProps) {
const whole = Math.floor(value);
const frac = Math.round((value - whole) * 100);
const vars: Vars = {
"--duration": `${duration}ms`,
"--easing": easing,
"--color": color,
"--whole-target": whole,
"--frac-target": frac,
};
return (
<div className={styles.counter} style={vars}>
<span className={styles.num} aria-hidden="true" />
<span className={styles.sr}>{value.toFixed(2)}</span>
<span className={styles.label}>{label}</span>
</div>
);
}03 小数点フォーマットのカウントアップ — 計測 累積面積 2.37% · 動いたフレーム 15/23GODRICH オリジナル · SCSS と React は zip に同梱 · CSS 数字 カウントアップ アニメーション9選 — 一行でコピペOK
このデモが属する記事
CSS 数字 カウントアップ アニメーション9選 — 一行でコピペOKcss 数字 カウントアップは、0や開始値から目標の数字まで上がっていくテキストを、JavaScriptなしでCSSのカスタムプロパティだけで数える手法です。9つのうち1つは逆に0へ向かって減っていき、別の1つはスクロールして画面に入るまで この記事のデモをすべて見る


