Skip to content

Countdown ​

Precise countdown timer calculated from absolute timestamp differences. Self-calibrates when recovering from background tab throttling to eliminate cumulative timer drift, paired with accessible screen reader announcements. To keep SSR hydration consistent, both the server and the client's initial render show placeholder text; the browser clock is read and timing starts after mount.

Preview ​

Preview
发版倒计时
-
精准计时
-

Installation ​

pnpm dlx brutx-vue@latest add countdown

Usage ​

vue
<script setup>
import { onMounted, ref } from 'vue'
import { Countdown } from 'brutx-ui-vue'

const ONE_DAY_MS = 24 * 60 * 60 * 1000
const targetTime = ref<number | null>(null)

onMounted(() => {
    targetTime.value = Date.now() + ONE_DAY_MS
})
</script>

<template>
    <!-- Basic countdown -->
    <Countdown title="Time Left" :value="targetTime" />

    <!-- Custom format and card layout -->
    <Countdown
        title="Deadline"
        :value="targetTime"
        format="DD [days] HH [hours] mm [mins] ss [secs]"
        variant="card"
    />
</template>

SSR First Frame and Custom Content ​

During SSR and hydration, the component displays placeholder (default -) first, then calculates the remaining time from value after mount. The default slot's formatted value is also placeholder text until then; remaining is 0 and isFinished is false. isReady becomes true after mount and the first calculation; remaining and isFinished then reflect the current target.

vue
<Countdown :value="targetTime">
    <template #default="{ formatted, isReady }">
        {{ isReady ? formatted : 'Preparing countdown' }}
    </template>
</Countdown>

Millisecond Refresh and Escaping ​

Including SSS in the format string automatically enables high-frequency (~30fps) refreshes. Literal letters matching format tokens (like D, H, m, s) can be escaped inside brackets [...]:

vue
<script setup>
import { onMounted, ref } from 'vue'

const ONE_MINUTE_MS = 60 * 1000
const targetTime = ref<number | null>(null)

onMounted(() => {
    targetTime.value = Date.now() + ONE_MINUTE_MS
})
</script>

<template>
    <Countdown
        title="Flash Sale"
        :value="targetTime"
        format="mm:ss.SSS"
        variant="card"
    />
</template>

Finish and Change Events ​

Listen to the @finish event when countdown hits zero, and monitor @change for remaining milliseconds:

vue
<script setup>
import { onMounted, ref } from 'vue'
import { Countdown } from 'brutx-ui-vue'

const FIVE_SECONDS_MS = 5 * 1000
const targetTime = ref<number | null>(null)

onMounted(() => {
    targetTime.value = Date.now() + FIVE_SECONDS_MS
})

function onFinish() {
    console.log('Countdown reached zero!')
}

function onChange(remainingMs: number) {
    console.log('Remaining milliseconds:', remainingMs)
}
</script>

<template>
    <Countdown
        :value="targetTime"
        @finish="onFinish"
        @change="onChange"
    />
</template>

Variants ​

VariantDescription
defaultDefault variant with lightweight inline layout and no extra border/background
cardNeo-Brutalist card with 3px solid border, hard drop shadow, padding, and rounded corners
borderedSolid bordered card container without drop shadow
subtleHighlighted subtle background card with solid border and hard drop shadow
vue
<template>
    <Countdown title="Default" :value="targetTime" variant="default" />
    <Countdown title="Card" :value="targetTime" variant="card" />
    <Countdown title="Bordered" :value="targetTime" variant="bordered" />
    <Countdown title="Subtle" :value="targetTime" variant="subtle" />
</template>

Sizes ​

SizeTitle Font SizeValue Font Size
smtext-xstext-2xl
defaulttext-smtext-3xl sm:text-4xl
lgtext-basetext-4xl sm:text-5xl
vue
<template>
    <Countdown title="Small" :value="targetTime" size="sm" variant="card" />
    <Countdown title="Default" :value="targetTime" size="default" variant="card" />
    <Countdown title="Large" :value="targetTime" size="lg" variant="card" />
</template>

Exported Types ​

The component exports variant helper functions and format parsing utilities:

ts
import type {
    CountdownProps,
    CountdownValue,
    CountdownEmits,
    CountdownVariantProps,
} from 'brutx-ui-vue'
import {
    countdownVariants,
    parseCountdownTarget,
    formatCountdown,
} from 'brutx-ui-vue'

API Reference ​

Countdown — Props
NameTypeDefaultDescription
value
Nullable
CountdownValue
undefined
The target deadline, as a millisecond timestamp, Date, numeric timestamp string, or parseable date string. Empty or invalid values show the placeholder.
Details

Declared type

CountdownValue

Literal values

  • null

Referenced types

CountdownValue
Type definition — CountdownValue
export type CountdownValue = number | string | Date | null | undefined

Default value details

Resolved value
undefined

Source

types.ts:10

packages/ui/src/components/countdown/types.ts

format
string | undefined
"HH:mm:ss"
The remaining-time format, supporting DD, D, HH, H, mm, m, ss, s, SSS, SS, S, and literal text in square brackets. Formats containing S use millisecond-level updates.
Details

Declared type

string | undefined

Default value details

Resolved value
"HH:mm:ss"

Source

types.ts:11

packages/ui/src/components/countdown/types.ts

title
string | undefined
undefined
The title above the countdown value; the title slot can replace its content.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined

Source

types.ts:12

packages/ui/src/components/countdown/types.ts

prefix
string | undefined
undefined
Text before the countdown value; the prefix slot can replace its content.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined

Source

types.ts:13

packages/ui/src/components/countdown/types.ts

suffix
string | undefined
undefined
Text after the countdown value; the suffix slot can replace its content.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined

Source

types.ts:14

packages/ui/src/components/countdown/types.ts

placeholder
string | undefined
undefined

Runtime fallback:Uses a hyphen when omitted.

The placeholder displayed before mount or when the target time is empty or invalid.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined
Runtime fallback
Uses a hyphen when omitted.

Applied by the component under the documented runtime conditions.

Source

types.ts:15

packages/ui/src/components/countdown/types.ts

variant
"bordered" | "card" | "default" | "subtle" | undefined
"default"
The background and border variant of the countdown container.
Details

Declared type

NonNullable<"default" | "card" | "bordered" | "subtle" | null | undefined> | undefined

Literal values

  • "bordered"
  • "card"
  • "default"
  • "subtle"

Default value details

Resolved value
"default"

Source

types.ts:16

packages/ui/src/components/countdown/types.ts

size
"default" | "lg" | "sm" | undefined
"default"
The size passed to the inner Statistic, controlling the value and accompanying text.
Details

Declared type

NonNullable<"default" | "lg" | "sm" | null | undefined> | undefined

Literal values

  • "default"
  • "lg"
  • "sm"

Default value details

Resolved value
"default"

Source

types.ts:17

packages/ui/src/components/countdown/types.ts

class
string | undefined
undefined
Custom CSS classes merged onto the countdown container.
Details

Declared type

string | undefined

Default value details

Resolved value
undefined

Source

types.ts:18

packages/ui/src/components/countdown/types.ts

Countdown — Events
NameSignature or typeDescription
@change
[remaining: number]
Emits the remaining milliseconds, clamped at zero, when the timer updates or the target is recalculated.
Details

Declared type

[remaining: number]

Source

Countdown.vue

packages/ui/src/components/countdown/Countdown.vue

@finish
[]
Emitted when a valid countdown enters its finished state, including an already-expired target at mount. A new future target restarts the countdown and allows it to finish again.
Details

Declared type

[]

Source

Countdown.vue

packages/ui/src/components/countdown/Countdown.vue

Countdown — Slots
NameSignature or typeDescription
#title
{ title?: string | undefined; }
Customizes the title area with the title prop value, which is rendered by default.
Details

Declared type

{ title?: string | undefined; }

Source

Countdown.vue:26

packages/ui/src/components/countdown/Countdown.vue

#prefix
{ prefix?: string | undefined; }
Customizes the value prefix area with the prefix prop value.
Details

Declared type

{ prefix?: string | undefined; }

Source

Countdown.vue:27

packages/ui/src/components/countdown/Countdown.vue

#suffix
{ suffix?: string | undefined; }
Customizes the value suffix area with the suffix prop value.
Details

Declared type

{ suffix?: string | undefined; }

Source

Countdown.vue:28

packages/ui/src/components/countdown/Countdown.vue

#default
{ remaining: number; formatted: string; isFinished: boolean; isReady: boolean; }
Customizes the value area with the remaining milliseconds, formatted string, finished state, and mounted isReady state.
Details

Declared type

{ remaining: number; formatted: string; isFinished: boolean; isReady: boolean; }

Source

Countdown.vue:29

packages/ui/src/components/countdown/Countdown.vue

Accessibility ​

  • Keyboard Interaction: Pure temporal display component that does not trap keyboard input; embedded interactive controls follow standard focus flow.
  • ARIA Attributes: Keeps aria-live quiet during high-frequency ticks to avoid screen reader spam; triggers a single aria-live="polite" speech announcement when countdown reaches zero.
  • Focus Management: Non-modal inline layout without focus traps, respecting assistive cursor navigation.
  • Number Layout: Uses monospace numerals (font-mono) to reduce width changes during updates. Remaining time is calculated from the current clock and timer scheduling begins after mount. Custom default slots can use isReady to distinguish the placeholder first frame from calculated values.

Brute force builds.