Use it for
Use BbSlider when position within a known interval carries meaning.
Use something else when
BbNumberInput— exact entry is the taskBbRating— the scale is a few discrete judgements
Pass Through
Hover or tap a part to outline it. Toggles flip loading, errors and warnings when the component has them — only parts highlight.
Set an exact spending limit
When the amount has consequences, bind the slider and number field to the same ref.
<template>
<div class="flex max-w-sm flex-col gap-3">
<!-- One ref, two controls: the slider for the feel, the field for the figure. -->
<BbSlider
id="spend-limit-slider"
v-model="limit"
compact
description="API calls pause once the cap is reached."
label="Monthly spend limit"
:max="500"
:min="0"
name="spend-limit"
:step="25"
/>
<BbNumberInput
id="spend-limit-exact"
v-model="limit"
compact
label="Exact amount"
:max="500"
:max-precision="0"
:min="0"
name="spend-limit-exact"
:step="25"
>
<template #prefix>€</template>
<template #suffix>/ month</template>
</BbNumberInput>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { BbNumberInput, BbSlider } from 'bitboss-ui';
// No sync code: both controls write to this ref, and the slider clamps and
// snaps whatever the field emits.
const limit = ref<number | null>(200);
</script>
min, max, and step are hard invariants. Invalid bounds or a non-positive step throw at mount. A value from an API must be converted to a number before binding.
Filter a range
Set range and bind a two-element array; the component keeps it sorted.
17 of 26 products in this range
<template>
<div class="flex max-w-sm flex-col gap-2">
<div class="flex items-baseline justify-between gap-2">
<span class="text-sm font-medium">Price</span>
<span class="text-sm tabular-nums opacity-70">
€{{ price[0] }} – €{{ price[1] }}
</span>
</div>
<BbSlider
id="filter-price"
v-model="price"
hide-label
label="Price range"
:max="1000"
:min="0"
name="price"
range
:step="50"
/>
<p class="text-sm opacity-70">
{{ matching.length }} of {{ products.length }} products in this range
</p>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { BbSlider } from 'bitboss-ui';
import { products } from '~/demo-data';
// `range` needs an array. A two-element tuple says so in the type as well, and
// keeps `price[0]` a number rather than `number | undefined`.
const price = ref<[number, number]>([100, 500]);
const matching = computed(() =>
products.filter(
(product) =>
product.price >= price.value[0] && product.price <= price.value[1]
)
);
</script>
range with a scalar throws. The reverse mismatch is silent: an array without range renders one thumb and ignores later entries.
Readout, ticks, and keyboard
The component has no built-in numeric readout. Show the model beside the track or through the thumb slot.
<template>
<!-- Ticks render below the track, so the field needs room underneath. -->
<div class="max-w-sm pb-6">
<BbSlider
id="ticks-priority"
v-model="priority"
label="Priority"
:max="5"
:min="1"
name="priority"
:step="1"
ticks
>
<!-- `ticks` positions one slot per step; the mark itself is yours. -->
<template #tick="{ value, active }">
<span
class="absolute top-1 left-1/2 -translate-x-1/2 text-xs tabular-nums"
:class="active ? 'font-medium' : 'opacity-50'"
>{{ value }}</span
>
</template>
</BbSlider>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { BbSlider } from 'bitboss-ui';
const priority = ref<number | null>(3);
</script>
ticks positions the tick slot and draws no marks by itself. Raise max-precision when step is finer than 0.01.
60% — the buttons and the arrow keys move the same step.
<template>
<div class="flex max-w-sm flex-col gap-2">
<BbSlider
id="steppers-brightness"
v-model="brightness"
label="Brightness"
:max="100"
:min="0"
name="brightness"
:step="10"
>
<!-- Icon-only buttons, so each one carries its own accessible name. -->
<template #prepend="{ decrease }">
<BbButton
aria-label="Dimmer"
icon="lucide:minus"
size="xs"
variant="ghost"
@click="decrease"
/>
</template>
<template #append="{ increase }">
<BbButton
aria-label="Brighter"
icon="lucide:plus"
size="xs"
variant="ghost"
@click="increase"
/>
</template>
</BbSlider>
<p class="text-sm opacity-70">
{{ brightness }}% — the buttons and the arrow keys move the same step.
</p>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import { BbButton, BbSlider } from 'bitboss-ui';
const brightness = ref<number | null>(60);
</script>
Arrow keys move one step, Page keys ten, and Home/End reach the bounds. Stepper callbacks work only in single-value mode. Save on inactive when persistence should wait for release.
Coming from v2thumb-translate → disable-thumb-translate
:thumb-translate="false" became disable-thumb-translate with inverted polarity.
Invalid and locked configurations
floating labels are unsupported: an explicit value warns and falls back, while the global floating default degrades silently to outside.
<template>
<div class="flex max-w-sm flex-col gap-5">
<BbSlider
id="chrome-quality"
v-model="quality"
description="Higher quality means larger exports."
hint="Anything above 80 is rarely worth the file size."
label="Export quality"
:max="100"
:min="10"
name="quality"
:step="10"
/>
<!-- A non-empty `errors` implies the state; `has-errors` is for when the
message lives somewhere else. -->
<BbSlider
id="chrome-replicas"
v-model="replicas"
:errors="replicaErrors"
label="Replicas"
:max="8"
:min="0"
name="replicas"
:step="1"
/>
<!-- readonly, not disabled: still focusable, so the value is reachable. -->
<BbSlider
id="chrome-quota"
hint="Set by your plan. Upgrade to raise it."
label="Included seats"
label-mode="inside"
:max="100"
:min="0"
:model-value="25"
name="quota"
persistent-hint
readonly
/>
</div>
</template>
<script setup lang="ts">
import { computed, ref } from 'vue';
import { BbSlider } from 'bitboss-ui';
const quality = ref<number | null>(70);
const replicas = ref<number | null>(0);
const replicaErrors = computed(() =>
(replicas.value ?? 0) < 2 ? ['Production services need at least two.'] : []
);
</script>
Prefer readonly for a locked value that people still need to inspect. disabled removes the thumb from the tab order. The v2 color prop moved to local track custom properties.