Skip to content

CBtn

Button. Two render variants, coloring through the platform utility classes, a loading state with a loader slot, and full preset system support.

Basic usage

A regular button emits click, disabled disables it natively, block stretches it to the container width.

Clicks: 0
Show code
vue
<template>
  <c-btn @click="count++">Click me</c-btn>
  <c-btn disabled>Disabled</c-btn>
  <c-btn block @click="count++">Block button</c-btn>
</template>

Variants

variant switches the rendering: flat (default) is a filled button, outlined is transparent with a border. The outlined border is drawn with currentColor, so it always matches the text color.

Show code
html
<c-btn>Flat</c-btn> <c-btn variant="outlined">Outlined</c-btn>

Color

color accepts any platform color — there is no predefined set:

  • a palette tokenred-darken-1, teal, deep-purple-lighten-1 — becomes a static utility (bg-teal);
  • a raw CSS value#7C4DFF, rgb(0,150,136), var(--my-color) — becomes an arbitrary class (bg-[#7C4DFF]) generated by utils-jit.

Where the color lands depends on the variant: flat colors the background, outlined colors the text and the border (via currentColor).

Show code
html
<c-btn color="red-darken-1">red-darken-1</c-btn>
<c-btn color="teal">teal</c-btn>
<c-btn color="#7C4DFF">#7C4DFF</c-btn>
<c-btn color="rgb(0,150,136)">rgb(0,150,136)</c-btn>

<c-btn variant="outlined" color="red-darken-1">outlined red</c-btn>
<c-btn variant="outlined" color="#7C4DFF">outlined #7C4DFF</c-btn>

A raw color must be a literal

Arbitrary classes are generated by a static source scan: color="#7C4DFF" and :color="'#7C4DFF'" work, while :color="someVar" with a raw value does not. Palette tokens are unaffected — their classes are always present in the CSS. See Custom attrs for details.

The text of a filled button stays on-primary (white). When the background needs dark text, add a text-* class:

html
<c-btn color="amber-lighten-3" class="text-black">Light button</c-btn>

Loading

loading shows a loader instead of the label (the button keeps its size), sets aria-busy, and suppresses click. The default loader is CProgressCircular; the loader slot replaces it with anything.

Show code
vue
<template>
  <c-btn :loading="loading" @click="submit">Submit</c-btn>

  <c-btn :loading="loading" color="teal" @click="submit">
    Custom loader
    <template #loader>
      <span>Saving…</span>
    </template>
  </c-btn>
</template>

Presets

CBtn supports the preset system. Zones: root (the button), label, loader (the loader container). States: disabled, loading, active, focused. At any moment exactly one state preset is applied (when defined) — its zones override base per zone.

Show code
ts
// main.ts — the preset is registered once
createVuelandUI({
  presets: {
    button: {
      save: {
        base: { root: ['bg-indigo', 'hover:bg-indigo-darken-1', 'text-white', 'elevation-2'] },
        active: { root: ['bg-indigo-darken-2', 'text-white', 'elevation-0'] },
        loading: { root: ['bg-indigo-lighten-2', 'text-white', 'elevation-0'] },
        disabled: { root: ['bg-grey-lighten-1', 'text-grey-darken-1'] },
      },
    },
  },
})
vue
<template>
  <c-btn preset="button.save" :loading="loading" :disabled="disabled" @click="save"> Save </c-btn>
</template>

Preset and color together

Both mechanisms do the same thing — they add utility classes to the button zones. They are additive and combine well as long as they own different aspects: in the demo below the preset defines the shape, paddings, and shadow, while each button's color comes from the color prop.

Show code
ts
createVuelandUI({
  presets: {
    button: {
      pill: {
        base: { root: ['radius-16', 'px-6', 'text-uppercase', 'elevation-3'] },
      },
    },
  },
})
html
<c-btn preset="button.pill" color="teal">teal</c-btn>
<c-btn preset="button.pill" color="red-darken-1">red</c-btn>
<c-btn preset="button.pill" color="#7C4DFF">#7C4DFF</c-btn>

Don't define the same aspect twice

When both the preset and color paint the same property of the same zone (for example both put bg-* on the root), the winner is decided not by the template order but by the rule order in the bundled CSS: all utilities have equal specificity and both use !important. In practice arbitrary classes (bg-[#7C4DFF]) are usually imported after the static utilities and override them, but that is an import-order detail, not a contract.

The rule is simple: the color lives either in the preset (a systemic decision with states) or in color (a one-off accent for a specific button). The preset then owns the shape, shadow, typography, and state behavior.

Accessibility

Renders a native <button>: keyboard and focus work out of the box. In the loading state the button gets aria-busy="true" and click is not emitted. disabled uses the native attribute.

The button renders with type="button" by default — it does not trigger form submission. For a submit button pass the attribute explicitly:

html
<c-form @submit="onSubmit">
  <c-btn type="submit">Send</c-btn>
</c-form>

API

Props

Prop
Type
Default
Description
variant
'flat' | 'outlined'
'flat'
Render variant
color
string
Palette token (red-darken-1) or raw color (#7C4DFF, rgb(...), var(...))
block
boolean
false
Full-width button
disabled
boolean
false
Disables the button (native disabled)
loading
boolean
false
Shows the loader, suppresses click, sets aria-busy
preset
string
Preset name (dot-separated path) from the registry

Slots

Slot
Description
default
Button content (label)
loader
Replaces the default loader (CProgressCircular) when loading

Events

Event
Arguments
Description
click
(e: MouseEvent)
Button click; not emitted while loading

CSS variables

Variable
Default
--c-btn-bg-color
var(--c-sys-color-primary)
--c-btn-text-color
var(--c-sys-color-on-primary)
--c-btn-loader-color
var(--c-sys-color-on-primary)
--c-btn-container-color
var(--c-sys-color-primary-container)
--c-btn-on-container-color
var(--c-sys-color-on-primary-container)
--c-btn-border-color
currentColor
--c-btn-border-width
var(--c-sys-border-width-thin)
--c-btn-border-radius
var(--c-sys-shape-md)
--c-btn-hover-bg-color
currentColor
--c-btn-focus-bg-color
var(--c-sys-state-focus-color)
--c-btn-pressed-bg-color
currentColor
--c-btn-transition-duration
.3s
--c-btn-disabled-text-color
var(--c-sys-color-disabled)
--c-btn-disabled-bg-color
var(--c-sys-color-disabled-container)
--c-btn-disabled-opacity
var(--c-sys-state-disabled-opacity)
--c-btn-paddings
var(--c-sys-space-2) var(--c-sys-space-3)
--c-btn-min-width
80px
--c-btn-min-height
var(--c-sys-control-height-sm)
--c-btn-font-size
var(--c-sys-typography-body-size)

The hover/pressed overlays are drawn by a pseudo-element on top of the background: currentColor at a low opacity, so the state highlight automatically adapts to the button color.