Skip to content

BbButton

Build actions and button-shaped links with loading, accessible names, and a consistent visual hierarchy.

import { BbButton } from 'bitboss-ui';

On this page

Use it for

Use BbButton for actions the user performs — save, delete, submit — and for destinations that need button styling. Actions use @click or type="submit"; navigation uses href or to.

Use something else when

  • BbDropdownButton — one dominant action has close alternatives: Save / Save as draft
  • BbDropdown — no action dominates
  • BbBaseButton — the surface is clickable with no button chrome at all: a table row, a card

Also see: Design language, Icons.

Passthrough

Hover or tap a part to outline it. The toggles turn on loading, errors and warnings where the component has them; only parts get a row.

Only one button submits the form

BbButton is the library's button, and its label is slot content. type defaults to button, so inside a form the only button that submits is the one you mark type="submit".

Not published yet

The variant ladder

Variants are a hierarchy, not a palette: one primary per region of the page, then secondary, outline, ghost.

Keep destructive for what cannot be undone. link recolors the button without changing its box, so it is the wrong choice for an inline text link.

variantDefaultDisabled
primary
secondary
outline
ghost
destructive
link

Sizes and width

Six sizes, xs to 2xl. md (28px) is the default and has the height of a compact field, so leave size off. Use sm or xs on dense surfaces and lg or larger for a rare hero action. block stretches the button to its container. size also takes a map by breakpoint, such as { default: 'sm', lg: 'lg' }.

xs
sm
md
lg
xl
2xl

Name icon-only buttons

prepend:icon and append:icon put an icon beside the label. For an icon-only button use icon and keep writing the label in the slot — it is hidden visually and still announced. Use aria-label only when that accessible name must differ from the slot text. An icon-only button with neither is an error for bitboss-ui check and the ESLint rule bitboss-ui/require-accessible-name.

To change the glyph with the button's state, use the slot named after the prop: #icon, #prepend:icon or #append:icon. It replaces the prop and renders inside the icon part, so pt:icon, --icon-size and the loading spinner work as they do on the prop's glyph. Its scope is the button's states, { pressed, loading, disabled, activeLink, exactActiveLink }: the toggle below swaps its bell when pressed, and a navigation link can show a solid icon on the current page. A BbIcon in the slot takes the part's size.

Async actions

A click handler that returns a promise puts the button into its loading state until the promise settles and blocks repeat clicks. Fire-and-forget work is not trackable; return the promise or bind loading to the state that owns it.

Delete ORD-2026-0421?

The order and its invoice are removed for everyone. This cannot be undone.

Coming from v2

In v2 this was opt-in through auto-loading. Delete that prop in v3. Use disable-auto-loading only when you intentionally need the old behaviour.

A type="submit" button inside a BbForm spins by itself while a @submit that returned a promise runs. Only the button that submitted spins, and a submit button outside the form that names it with form="<id>" does too. A submit from code spins none.

disable-auto-loading turns off every spin the button starts on its own, the form's included; only loading shows the spinner then. Reach for it on a button whose click awaits a whole dialog (await show()): spinning until the dialog closes would read as busy.

Pass href or to instead of navigating from a click handler. The result is a real link with middle-click, open-in-new-tab and copy-address behaviour.

ORD-2026-0417 has shipped

DHL Express · tracking 4815 1623 42

target, download and external make a plain anchor that no router intercepts. The routing rules are on BbBaseButton.

Toggles and exclusive sets

v-model operates the button as a toggle between true-value and false-value. Seed the model — an undefined value swallows the first click.

For an exclusive set, share one model and give each button its own true-value. Set each false-value to that button's own true-value (never a sibling's) and one choice always stays selected. Set it to null instead and a second click clears the selection.

group joins adjacent buttons into one segment. A set of group toggles is also one Tab stop: the arrow keys move between the buttons, and Space or Enter presses one. Keep the buttons side by side in one parent, or the set splits.

A pressed toggle carries aria-pressed="true". Restyle it with pt:root:pressed, not a selector of your own.

Off

Register a look, or start blank

Register a name in the plugin config and it becomes a typed value of variant:

ts
// nuxt.config.ts
export default defineNuxtConfig({
    bitboss: { buttonVariants: ['upgrade'] },
});

The library ships no CSS for registered variants — you style .bb-button--upgrade yourself through the same local properties as the built-in variants. The name is also a pt state (pt:root:upgrade), so a name the button already uses as a state (loading, pressed, …) fails the build.

css
.bb-button--upgrade {
    --bg: var(--bb-primary);
    --bg-hover: var(--bb-primary-hover);
    --bg-pressed: var(--bb-primary-pressed);
    --fg: var(--bb-primary-fg);
    --ring-color: var(--bb-ring);
}

--ring-color paints the focus ring and --ring-size sets its width, drawn outside the border. --ring-size defaults to var(--bb-ring-size): leave it out unless the variant needs a heavier ring.

To start a style from scratch, pass variant="none": the button keeps its size, padding, icons and loading, drops every colour, and your classes draw the rest. You own the hover, the focus ring and the disabled look. (On Tailwind v4 your classes win over the button's own rules only with cssLayers set.)

3 files attached ·

When the same look shows up on a second screen, register it as a variant so it is typed and styled in one place. When the shape is not a button at all, such as a card or a table row, start from BbBaseButton.

Coming from v2v2 tooltip and theme props

Replace tooltip* props with v-bb-tooltip or BbTooltip. Replace theme with a built-in or registered variant; unregistered variant names are now type errors.