Cooldown Bar
A slim progress bar with a label and monospace countdown for command cooldowns.
A slim progress bar with a label and monospace countdown for command cooldowns. Compose it from the exported registry parts so local content and behaviour stay in the application while the visual contract stays token-driven.
newt-cooldownimport {
CooldownBar,
CooldownBarLabel,
CooldownBarTrack,
CooldownBarValue,
} from "@/registry/bases/newt/ui/cooldown-bar"
export default function CooldownBarDemo() {
return (
<CooldownBar className="w-full">
<CooldownBarLabel>
<span>Command cooldown</span>
<CooldownBarValue>3.2s</CooldownBarValue>
</CooldownBarLabel>
<CooldownBarTrack value={40} aria-label="Command cooldown" />
</CooldownBar>
)
}<script setup lang="ts">
import {
CooldownBar,
CooldownBarLabel,
CooldownBarTrack,
CooldownBarValue,
} from "~~/registry/bases/newt/ui/cooldown-bar"
</script>
<template>
<CooldownBar class="w-full">
<CooldownBarLabel>
<span>Command cooldown</span>
<CooldownBarValue>3.2s</CooldownBarValue>
</CooldownBarLabel>
<CooldownBarTrack :value="40" aria-label="Command cooldown" />
</CooldownBar>
</template>Installation
pnpm dlx newtui@latest add cooldown-barnpx newtui@latest add cooldown-baryarn dlx newtui@latest add cooldown-barbunx newtui@latest add cooldown-barCopy the source into your project
import * as React from "react"
import { cn } from "@/lib/utils"
const CooldownBar = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex max-w-[260px] flex-col gap-1.5", className)}
{...props}
/>
))
CooldownBar.displayName = "CooldownBar"
const CooldownBarLabel = React.forwardRef<
HTMLDivElement,
React.HTMLAttributes<HTMLDivElement>
>(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"flex justify-between text-xs text-newt-text-muted",
className
)}
{...props}
/>
))
CooldownBarLabel.displayName = "CooldownBarLabel"
const CooldownBarValue = React.forwardRef<
HTMLElement,
React.HTMLAttributes<HTMLElement>
>(({ className, ...props }, ref) => (
<strong
ref={ref}
className={cn(
"font-mono font-semibold text-newt-text-secondary",
className
)}
{...props}
/>
))
CooldownBarValue.displayName = "CooldownBarValue"
export interface CooldownBarTrackProps extends React.HTMLAttributes<HTMLDivElement> {
/** Fill percentage, 0-100. */
value: number
/** Switch the fill to the "ready" (online green) colour. */
ready?: boolean
}
const CooldownBarTrack = React.forwardRef<
HTMLDivElement,
CooldownBarTrackProps
>(({ className, value, ready = false, ...props }, ref) => {
const clamped = Math.min(100, Math.max(0, value))
return (
<div
ref={ref}
role="progressbar"
aria-valuemin={0}
aria-valuemax={100}
aria-valuenow={clamped}
data-state={ready ? "ready" : "cooling"}
className={cn(
"h-1.5 overflow-hidden rounded-full bg-newt-bg-base",
className
)}
{...props}
>
<div
className={cn(
"h-full rounded-full",
ready ? "bg-newt-online" : "bg-newt-idle"
)}
style={{ width: `${clamped}%` }}
/>
</div>
)
})
CooldownBarTrack.displayName = "CooldownBarTrack"
export { CooldownBar, CooldownBarLabel, CooldownBarValue, CooldownBarTrack }<script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>
<template>
<div :class="cn('flex max-w-[260px] flex-col gap-1.5', props.class)">
<slot />
</div>
</template><script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>
<template>
<div
:class="
cn('flex justify-between text-xs text-newt-text-muted', props.class)
"
>
<slot />
</div>
</template><script setup lang="ts">
import type { HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{ class?: HTMLAttributes["class"] }>()
</script>
<template>
<strong
:class="cn('font-mono font-semibold text-newt-text-secondary', props.class)"
>
<slot />
</strong>
</template><script setup lang="ts">
import { computed, type HTMLAttributes } from "vue"
import { cn } from "@/lib/utils"
const props = defineProps<{
class?: HTMLAttributes["class"]
/** Fill percentage, 0-100. */
value: number
/** Switch the fill to the "ready" (online green) colour. */
ready?: boolean
}>()
const clamped = computed(() => Math.min(100, Math.max(0, props.value)))
</script>
<template>
<div
role="progressbar"
:aria-valuemin="0"
:aria-valuemax="100"
:aria-valuenow="clamped"
:data-state="props.ready ? 'ready' : 'cooling'"
:class="
cn('h-1.5 overflow-hidden rounded-full bg-newt-bg-base', props.class)
"
>
<div
:class="
cn(
'h-full rounded-full',
props.ready ? 'bg-newt-online' : 'bg-newt-idle'
)
"
:style="{ width: `${clamped}%` }"
/>
</div>
</template>export { default as CooldownBar } from "./CooldownBar.vue"
export { default as CooldownBarLabel } from "./CooldownBarLabel.vue"
export { default as CooldownBarValue } from "./CooldownBarValue.vue"
export { default as CooldownBarTrack } from "./CooldownBarTrack.vue"Update the import paths
The files import @/lib/utils and sibling components through the aliases in your components.json. Point them at wherever those live in your project.
Usage
import { CooldownBar } from "@/components/ui/cooldown-bar"
export function Example() {
return <CooldownBar />
}<script setup lang="ts">
import { CooldownBar, CooldownBarLabel, CooldownBarValue, CooldownBarTrack } from "@/components/ui/cooldown-bar"
</script>
<template>
<CooldownBar />
</template>API reference
CooldownBarTrack
value?number
Fill percentage, 0-100.
numberready?boolean | undefined
Switch the fill to the "ready" (online green) colour.
boolean | undefinedThe registry exports CooldownBar, CooldownBarLabel, CooldownBarValue, CooldownBarTrack. Parts without a table accept the native attributes for the element they render; React uses className, while Vue uses class and fallthrough attributes.
Accessibility
Preserve the native semantics of the rendered elements and pass through ARIA attributes when the surrounding context needs a label or description. Any interactive child needs a visible focus indicator, and meaning must not rely on colour alone.
Tokens
Cooldown Bar reads its colours, radii and motion from the --newt-* tokens installed with the library. Override a token and every component that reads it follows, including this one — see the token layer. In the plain HTML and CSS distribution the same component is newt-cooldown.