Sliding Number
The SlidingNumber component creates animated numbers that slide in with smooth animation effects. Each digit animates independently, creating a visually appealing counting experience.
Usage
Performance Metrics
Real-time statistics with smooth sliding animations
Response Time
Milliseconds
Uptime
Service availability
Monthly actives
Current online
<template>
<div class="p-8 max-w-lg mx-auto">
<div class="text-center mb-12">
<h3 class="text-3xl font-bold mb-3 text-highlighted">
Performance Metrics
</h3>
<p class="text-muted text-lg">
Real-time statistics with smooth sliding animations
</p>
</div>
<div class="space-y-8">
<div class="bg-default rounded-xl shadow-xl p-6 border border-default">
<div class="flex justify-between items-center mb-4">
<div>
<h4 class="font-bold text-highlighted">
Response Time
</h4>
<p class="text-sm text-muted">
Milliseconds
</p>
</div>
<UBadge
color="primary"
variant="soft"
>
ms
</UBadge>
</div>
<div class="text-5xl font-bold text-highlighted">
<MSlidingNumber :value="responseTime" />
</div>
<div class="mt-2 h-2 bg-accented rounded-full overflow-hidden">
<div
class="h-full bg-primary rounded-full"
:style="{ width: `${Math.min(100, responseTime / 5)}%` }"
/>
</div>
</div>
<div class="bg-default rounded-xl shadow-xl p-6 border border-default">
<div class="flex justify-between items-center mb-4">
<div>
<h4 class="font-bold text-highlighted">
Uptime
</h4>
<p class="text-sm text-muted">
Service availability
</p>
</div>
<UBadge
color="green"
variant="soft"
>
%
</UBadge>
</div>
<div class="text-5xl font-bold text-highlighted">
<MSlidingNumber
:value="uptime"
:precision="1"
/>
</div>
<div class="mt-2 h-2 bg-accented rounded-full overflow-hidden">
<div
class="h-full bg-green-500 rounded-full"
:style="{ width: `${uptime}%` }"
/>
</div>
</div>
<div class="bg-default rounded-xl shadow-xl p-6 border border-default">
<div class="flex justify-between items-center mb-4">
<div>
<h4 class="font-bold text-highlighted">
Monthly actives
</h4>
<p class="text-sm text-muted">
Current online
</p>
</div>
</div>
<div class="text-5xl font-bold text-highlighted">
<MSlidingNumber :value="activeUsers" />
</div>
<div class="mt-2 h-2 bg-accented rounded-full overflow-hidden">
<div
class="h-full bg-primary/70 rounded-full"
:style="{ width: `${Math.min(100, activeUsers / 2000 * 100)}%` }"
/>
</div>
</div>
</div>
<div class="mt-10 flex flex-col sm:flex-row gap-4">
<UButton
color="primary"
variant="solid"
size="lg"
block
@click="updateMetrics"
>
<UIcon
name="i-lucide-refresh-cw"
class="w-5 h-5 mr-2"
/>
Refresh Data
</UButton>
<UButton
color="neutral"
variant="outline"
size="lg"
block
@click="resetMetrics"
>
<UIcon
name="i-lucide-rotate-ccw"
class="w-5 h-5 mr-2"
/>
Reset
</UButton>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
const responseTime = ref(124)
const uptime = ref(99.9)
const activeUsers = ref(1248)
const updateMetrics = () => {
responseTime.value = Math.floor(Math.random() * 200) + 50
uptime.value = 99.5 + (Math.random() * 0.4)
activeUsers.value = Math.floor(Math.random() * 2000) + 500
}
const resetMetrics = () => {
responseTime.value = 124
uptime.value = 99.9
activeUsers.value = 1248
}
</script>
Examples
Interactive counter
<template>
<div class="p-4 space-y-4 flex flex-col items-center">
<MSlidingNumber :value="currentValue" />
<button
class="px-4 py-2 bg-primary/70 text-white rounded hover:bg-primary transition-colors"
@click="changeNumber"
>
Change Number
</button>
</div>
</template>
<script setup>
import { ref } from 'vue'
const currentValue = ref(1234)
const changeNumber = () => {
currentValue.value = Math.floor(Math.random() * 10000)
}
</script>
Customize
Customize
Props
| Prop | Type | Default | Description |
|---|---|---|---|
valuerequired | number | - | Value to display. |
padStart | boolean | false | Pad single digits with leading zero. |
decimalSeparator | string | '.' | Character to use as decimal separator. |
precision | number | null | null | Number of decimal places to show. null renders the value as-is. |
class | string | undefined | CSS classes to apply to the component. |
ui | object | undefined | Per-slot classes merged over the theme defaults. See UI. |
UI
This component's theme exposes the slots below. Override any of them per instance with the ui prop, or app-wide under ui.slidingNumber in app.config.ts.
| Prop | Type | Default | Description |
|---|---|---|---|
ui | object | - | Per-slot classes merged over the theme defaults. |
| Slot | Props | Description |
|---|---|---|
root | - | flex items-baseline |
separator | - | mx-0.5 |
digit | - | relative inline-block w-[1ch] overflow-hidden leading-none tabular-nums align-baseline |
digitTrack | - | absolute inset-0 |
digitGhost | - | opacity-0 block |
digitGlyph | - | absolute inset-0 flex items-center justify-center |
Accessibility
Each digit slot renders all ten glyphs (0–9) plus an invisible ghost 0, so the text content of the element is a stack like 0123456789 per place rather than the number itself. A screen reader will not read a meaningful value from it. Give the component an aria-label with the real number, or hide the visual with aria-hidden="true" and render an sr-only copy of the value alongside it.
The root is a plain <div> with no role or tabindex; the component is not interactive. There is no prefers-reduced-motion handling — the digits always slide — so consumers that must honour it should render the value as static text.
Performance
- One spring per digit place drives the slide (
useSpringplus auseTransformper glyph inDigitGlyph), so cost scales with the number of visible digits, not the magnitude of the value — going from9to1,000,000adds springs only for the new places. - Each place measures its height once on mount via
offsetHeight(one forced reflow per digit); the animated stack renders only after the height is known, which avoids a first-frame layout blob. - Only ten glyphs per place are translated on the
yaxis, and the transform is compositor-friendly, so a value change animates transform only rather than relaying out text. - A negative value renders an extra
-span; there is no per-frame JavaScript beyond the springs.