๐ข Button
Basic
NButton
- use to trigger an action or event, such as submitting a form, opening a dialog, canceling an action, or performing a delete operation.
You can use
label prop
ordefault slot
to set the button text.
Variants
btn="{variant}"
- change the variant of the button.
Variant | Description |
---|---|
solid | The default variant. |
outline | The outline variant. |
ghost | The ghost variant. |
link | The link variant. |
soft | The soft variant. |
text | The text variant. |
~ | The unstyle or base variant |
Color
btn="{variant}-{color}"
- change the color of the button.
primary
. You can also add your own colors to the palette through the Configuration section.Color with states:
Custom colors using utilities:
Static colors:
Size
size="{size}"
- change the size of the button.
๐ You can freely adjust the size of the button using any size imaginable. No limits exist, and you can use
breakpoints
such assm:sm, xs:lg
to change size based on screen size orstates
such ashover:lg, focus:3xl
to change size based on input state and more.
padding
and font-size
of the button scale depends on the size
. If you want to change the font-size
and padding
simultaneously, you can always customize it using utility classes.Padding & Shape
btn="square"
- to force the padding to have the same size, usefull for icon buttons.
rectangle
, you can change it to square
. You can always add and customize it using utility classes such as adding rounded-full
to make it circle.Icon
icon
- change label text to icon.
leading="{icon}"
- add a leading icon to the button.
trailing="{icon}"
- add a trailing icon to the button.
heroicons
and tabler
for the icons, you can use any icon provided by Iconify
through icones, refer to configuration for more information.Icon with states
Leading icon with label
Trailing icon with label
Link
to
- add a link to the button.
NuxtLink
for the link, you can use any NuxtLink
props such as prefetch
, target
, activeClass
, etc. Refer to NuxtLink for more information.Block
btn="block"
- add block style to the button.
Disabled
disabled
- add a disabled state to the button.
Loading
By default we trigger the disabled state when the button is loading.
loading
- add a loading state to the button.
loading-placement
- change the loading icon placement, default is leading
. options are leading
, trailing
and label
.
loading
with icon
and label
at the same time.Slots
Default
#default
- set the button label, refer to label for the example.
Leading
#leading
- add a leading icon to the button.
Trailing
#tailing
- add a trailing icon to the button.
Loading
#loading
- add a loading icon to the button.
Props
export interface NButtonProps {
/**
* Change the button type.
*
* @default 'button'
*/
type?: 'button' | 'submit' | 'reset'
/**
* Change the loading placement of the button.
*
* @default 'leading'
*/
loadingPlacement?: 'leading' | 'trailing' | 'label'
/**
* Convert `label` prop to icon component.
*
* @default false
* @example
* icon
* label="i-heroicons-information-circle"
*/
icon?: boolean
/**
* Disable the button.
*
* @default false
*/
disabled?: boolean
/**
* Swap the position of the leading and trailing icons.
*
* @default false
*/
reverse?: boolean
/**
* Show loading state on button
* @default false
*/
loading?: boolean
/**
* Change the button tag to `NuxtLink` component,
* This allows you to use `NuxtLink` available props.
*
* @see https://nuxt.com/docs/api/components/nuxt-link#props
* @example
* to="/"
*/
to?: string
/**
* Add a label to the button.
*
* @example
* label="Click me"
*/
label?: string
/**
* Allows you to add `UnaUI` button preset properties,
* Think of it as a shortcut for adding options or variants to the preset if available.
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/button.ts
* @example
* btn="solid-green block square"
*/
btn?: string
/**
* Add leading icon the button,
* This also allows you to add utility classes to the icon.
*
* @example
* leading="i-heroicons-information-circle text-green-500 dark:text-green-400 text-2xl"
*/
leading?: string
/**
* Add trailing icon the button.
* This also allows you to add utility classes to the icon.
*
* @example
* trailing="i-heroicons-information-circle text-green-500 dark:text-green-400 text-2xl"
*/
trailing?: string
/**
* `UnaUI` preset configuration
*
* @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/button.ts
*/
una?: {
// base
btnDefaultVariant?: string
btn?: string
btnLabel?: string
btnIconLabel?: string
btnLoading?: string
// icons
btnTrailing?: string
btnLeading?: string
btnLoadingIcon?: string
}
}
Presets
type BtnPrefix = 'btn'
export const staticBtn: Record<`${BtnPrefix}-${string}` | BtnPrefix, string> = {
// config
'btn-default-variant': 'btn-solid',
'btn-loading-icon': 'i-loading',
// base
'btn': 'bg-transparent text-0.875em leading-5 gap-0.42857142857142855em btn-rectangle rounded-md inline-flex justify-center items-center btn-disabled font-semibold cursor-pointer',
'btn-disabled': 'disabled:n-disabled',
'btn-label': '',
'btn-icon-label': 'text-1.191em',
'btn-leading': '-ml-0.14285714285714285em text-1.191em',
'btn-trailing': '-mr-0.14285714285714285em text-1.191em',
'btn-loading': 'animate-spin text-1.191em',
// options
'btn-block': 'w-full',
'btn-reverse': 'flex-row-reverse',
'btn-rectangle': 'px-0.7142857142857143em py-0.42857142857142855em',
'btn-square': 'p-0.42857142857142855em',
// variants
'btn-solid-white': 'bg-base text-base ring-1 ring-base ring-inset shadow-sm btn-focus hover:bg-muted',
'btn-ghost-white': 'text-base btn-focus hover:bg-$c-gray-50',
'btn-solid-gray': 'bg-$c-gray-50 text-$c-gray-800 ring-1 ring-base ring-inset shadow-sm btn-focus hover:bg-$c-gray-100',
'btn-ghost-gray': 'text-$c-gray-600 btn-focus hover:bg-$c-gray-100',
'btn-soft-gray': 'text-$c-gray-600 bg-$c-gray-50 btn-focus hover:bg-$c-gray-100',
'btn-link-gray': 'text-$c-gray-500 btn-focus hover:text-$c-gray-950 hover:underline underline-offset-4',
'btn-solid-black': 'bg-$c-gray-950 text-inverted shadow-sm btn-focus hover:bg-$c-gray-900',
'btn-link-black': 'text-$c-gray-950 btn-focus hover:underline underline-offset-4',
'btn-text-black': 'text-$c-gray-950 btn-focus hover:text-$c-gray-900',
'btn-text-gray': 'text-$c-gray-600 btn-focus hover:text-$c-gray-900',
}
export const dynamicBtn: [RegExp, (params: RegExpExecArray) => string][] = [
// base
[/^btn-focus(-(\S+))?$/, ([, , c = 'primary']) => `focus-visible:outline-${c}-600 dark:focus-visible:outline-${c}-500 focus-visible:outline-2 focus-visible:outline-offset-2`],
// variants
[/^btn-solid(-(\S+))?$/, ([, , c = 'primary']) => `btn-focus-${c} text-inverted shadow-sm bg-${c}-600 hover:bg-${c}-500 dark:bg-${c}-500 dark:hover:bg-${c}-400`],
[/^btn-text(-(\S+))?$/, ([, , c = 'primary']) => `btn-focus-${c} text-${c}-600 dark:text-${c}-500 hover:text-${c}-500 dark:hover:text-${c}-400`],
[/^btn-outline(-(\S+))?$/, ([, , c = 'primary']) => `bg-transparent btn-focus-${c} text-${c}-500 dark:text-${c}-400 ring-1 ring-inset ring-${c}-500 dark:ring-${c}-400 hover:bg-${c}-50 dark:hover:bg-${c}-950`],
[/^btn-soft(-(\S+))?$/, ([, , c = 'primary']) => `btn-focus-${c} text-${c}-600 dark:text-${c}-400 bg-${c}-50 dark:bg-${c}-950 hover:bg-${c}-100 dark:hover:bg-${c}-900`],
[/^btn-ghost(-(\S+))?$/, ([, , c = 'primary']) => `btn-focus-${c} text-${c}-600 dark:text-${c}-400 hover:bg-${c}-100 dark:hover:bg-${c}-900`],
[/^btn-link(-(\S+))?$/, ([, , c = 'primary']) => `btn-focus-${c} text-${c}-500 dark:text-${c}-400 hover:underline underline-offset-4`],
]
export const btn = [
...dynamicBtn,
staticBtn,
]
Component
<script setup lang="ts">
import { computed } from 'vue'
import { createReusableTemplate } from '@vueuse/core'
import NIcon from '../elements/Icon.vue'
import type { NButtonProps } from '../../types'
import NLink from '../elements/Link.vue'
defineOptions({
inheritAttrs: false,
})
const props = withDefaults(defineProps<NButtonProps>(), {
type: 'button',
loadingPlacement: 'leading',
una: () => ({
btnDefaultVariant: 'btn-default-variant',
}),
})
const btnVariants = ['solid', 'outline', 'soft', 'ghost', 'link', 'text'] as const
const hasVariant = computed(() => btnVariants.some(btnVariants => props.btn?.includes(btnVariants)))
const isBaseVariant = computed(() => props.btn?.includes('~'))
const [DefineTemplate, ReuseTemplate] = createReusableTemplate()
</script>
<template>
<Component
:is="to ? NLink : 'button'"
:to="to"
:type="to ? null : type"
class="btn"
:class="[
!hasVariant && !isBaseVariant ? una?.btnDefaultVariant : null,
{ 'btn-reverse': reverse },
una?.btn,
]"
:disabled="to ? null : disabled || loading"
:btn="btn"
:aria-label="icon ? label : null"
v-bind="$attrs"
>
<DefineTemplate v-if="loading">
<slot name="loading">
<NIcon
:name="una?.btnLoadingIcon ?? 'btn-loading-icon'"
:class="una?.btnLoading"
btn="loading"
/>
</slot>
</DefineTemplate>
<ReuseTemplate v-if="loading && loadingPlacement === 'leading'" />
<slot
v-else
name="leading"
>
<NIcon
v-if="leading"
:name="leading"
:class="una?.btnLeading"
btn="leading"
/>
</slot>
<ReuseTemplate v-if="loading && loadingPlacement === 'label'" />
<slot v-else>
<NIcon
v-if="label && icon"
:name="label"
btn="icon-label"
:class="una?.btnIconLabel"
/>
<span
v-if="!icon"
btn="label"
:class="una?.btnLabel"
>
{{ label }}
</span>
</slot>
<ReuseTemplate v-if="loading && loadingPlacement === 'trailing'" />
<slot
v-else
name="trailing"
>
<NIcon
v-if="trailing"
:name="trailing"
:class="una?.btnTrailing"
btn="trailing"
/>
</slot>
</Component>
</template>