patterns
Count Up
実績/統計の数値をスクロール時にカウントアップ。
numbersstatsjs
Customize
data-mk-to / data-mk-from / data-mk-duration など。
A11y
装飾なら aria-hidden。意味があるなら周辺文脈で補強。
Install
“壊れにくさ”優先のため、依存は増やさずに ファイルをコピーして使う 形式です。
Files
まずは下記ファイルをプロジェクトに配置してください。
required-files.md
- tailwind-motion-kit/js/count-up.js (js)Option A
まずは Bundle を読み込んで、動作を確認するのがおすすめです。
globals.css
/* Bundle: まずはこれが一番簡単 */
@import "./tailwind-motion-kit/css/99_bundle.css";Option B
必要な CSS だけ読み込みたい場合はこちら。
globals.css
/* このコンポーネントは CSS ファイルを持ちません */JavaScript
動きやUI制御が必要な場合のみ、初期化を追加します。
init.js
import { initCountUp } from "./tailwind-motion-kit/js/count-up.js";
initCountUp();Snippets
Copy & paste
usage.html
<strong data-mk-countup data-mk-to="12800" data-mk-duration="1200"></strong>usage.js
import { initCountUp } from "./tailwind-motion-kit/js/count-up.js";
initCountUp();Files
この部品が参照する実ファイルです。
tailwind-motion-kit/js/count-up.jsjs
tailwind-motion-kit/js/count-up.js
// Tailwind Motion Kit (mk) - Count up
// ----------------------------------
// data-mk-countup 要素の数値を、inview でカウントアップさせます。
// Nyano の統計などに便利です。
// 例:
// <span data-mk-countup data-mk-to="1234" data-mk-duration="1200"></span>
function prefersReducedMotion() {
return window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
}
function toMs(value, fallbackMs) {
if (value == null || value === "") return fallbackMs;
const v = String(value).trim();
if (v.endsWith("ms")) return Number(v.replace("ms", "")) || fallbackMs;
if (v.endsWith("s")) return (Number(v.replace("s", "")) * 1000) || fallbackMs;
const n = Number(v);
return Number.isFinite(n) ? n : fallbackMs;
}
function formatNumber(n, { decimals = 0, sep = "," } = {}) {
const fixed = n.toFixed(decimals);
const [a, b] = fixed.split(".");
const withSep = a.replace(/\B(?=(\d{3})+(?!\d))/g, sep);
return b ? `${withSep}.${b}` : withSep;
}
export function initCountUp({
selector = "[data-mk-countup]",
threshold = 0.2,
rootMargin = "0px 0px -10% 0px",
once = true,
} = {}) {
const reduce = prefersReducedMotion();
const els = Array.from(document.querySelectorAll(selector));
const play = (el) => {
if (el.getAttribute("data-mk-countup-played") === "true") return;
const to = Number(el.getAttribute("data-mk-to") ?? el.textContent ?? 0);
const from = Number(el.getAttribute("data-mk-from") ?? 0);
const duration = toMs(el.getAttribute("data-mk-duration"), 1200);
const decimals = Number(el.getAttribute("data-mk-decimals") ?? 0);
const sep = el.getAttribute("data-mk-sep") ?? ",";
const prefix = el.getAttribute("data-mk-prefix") ?? "";
const suffix = el.getAttribute("data-mk-suffix") ?? "";
if (reduce || duration <= 0) {
el.textContent = `${prefix}${formatNumber(to, { decimals, sep })}${suffix}`;
el.setAttribute("data-mk-countup-played", "true");
return;
}
const start = performance.now();
const delta = to - from;
const tick = (now) => {
const t = Math.min(1, (now - start) / duration);
// ease-out
const eased = 1 - Math.pow(1 - t, 3);
const value = from + delta * eased;
el.textContent = `${prefix}${formatNumber(value, { decimals, sep })}${suffix}`;
if (t < 1) {
requestAnimationFrame(tick);
} else {
el.textContent = `${prefix}${formatNumber(to, { decimals, sep })}${suffix}`;
el.setAttribute("data-mk-countup-played", "true");
}
};
requestAnimationFrame(tick);
};
if (reduce) {
els.forEach(play);
return { destroy() {}, refresh() { els.forEach(play); } };
}
const io = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (!entry.isIntersecting) return;
play(entry.target);
if (once) io.unobserve(entry.target);
});
}, { threshold, rootMargin });
els.forEach((el) => io.observe(el));
return {
destroy() { io.disconnect(); },
refresh() {
document.querySelectorAll(selector).forEach((el) => io.observe(el));
},
};
}
Preview
Count up (static preview)
12,800+
実運用では initCountUp() でスクロール連動のカウントアップが可能です。
Tips
- • デザイン側は CSS 変数(tokens)で一括調整できます。
- • 動きは “短く・小さく・同じ癖” を優先すると品位が保てます。
- • prefers-reduced-motion を必ず考慮してください。
