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.
Show code
<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
<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 token —
red-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
<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:
<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
<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
// 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'] },
},
},
},
})<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
createVuelandUI({
presets: {
button: {
pill: {
base: { root: ['radius-16', 'px-6', 'text-uppercase', 'elevation-3'] },
},
},
},
})<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:
<c-form @submit="onSubmit">
<c-btn type="submit">Send</c-btn>
</c-form>API
Props
variant'flat' | 'outlined''flat'colorstringred-darken-1) or raw color (#7C4DFF, rgb(...), var(...))blockbooleanfalsedisabledbooleanfalsedisabled)loadingbooleanfalseclick, sets aria-busypresetstringSlots
defaultloaderCProgressCircular) when loadingEvents
click(e: MouseEvent)loadingCSS variables
--c-btn-bg-colorvar(--c-sys-color-primary)--c-btn-text-colorvar(--c-sys-color-on-primary)--c-btn-loader-colorvar(--c-sys-color-on-primary)--c-btn-container-colorvar(--c-sys-color-primary-container)--c-btn-on-container-colorvar(--c-sys-color-on-primary-container)--c-btn-border-colorcurrentColor--c-btn-border-widthvar(--c-sys-border-width-thin)--c-btn-border-radiusvar(--c-sys-shape-md)--c-btn-hover-bg-colorcurrentColor--c-btn-focus-bg-colorvar(--c-sys-state-focus-color)--c-btn-pressed-bg-colorcurrentColor--c-btn-transition-duration.3s--c-btn-disabled-text-colorvar(--c-sys-color-disabled)--c-btn-disabled-bg-colorvar(--c-sys-color-disabled-container)--c-btn-disabled-opacityvar(--c-sys-state-disabled-opacity)--c-btn-paddingsvar(--c-sys-space-2) var(--c-sys-space-3)--c-btn-min-width80px--c-btn-min-heightvar(--c-sys-control-height-sm)--c-btn-font-sizevar(--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.
