UIPackage
Menu

Fab

fab ui
Edit on GitHub

Floating action button — a fixed or absolute positioned circular button for the primary screen action. Supports an icon (default), an extended label variant, mini/large sizes, four color variants, six anchor positions, and a disabled state.

Also available for React ->

Installation

$ npx shadcn-vue@latest add https://uipkge.dev/r/vue/fab.json
Named registry: npx shadcn-vue@latest add @uipkge/fab Installs to: app/components/ui/fab/

Examples

Props

Name Type / Values Default Required
label

Label text — renders an extended FAB. Use the default slot for an icon.

string optional
as string 'button' optional
asChild boolean optional
variant Variant 'default' optional
size Size 'default' optional
position Position 'bottom-right' optional
absolute

Use absolute instead of fixed positioning (for contained FABs).

boolean false optional
disabled boolean optional
ariaLabel

Accessible label. Defaults to the label prop or 'Floating action'.

string optional
class HTMLAttributes['class'] optional

Used by

Files installed (3)

  • app/components/ui/fab/Fab.vue 1.8 kB
    <script setup lang="ts">
    import type { HTMLAttributes } from 'vue'
    import { Primitive } from 'reka-ui'
    import { cn } from '@/lib/utils'
    import { fabVariants } from './fab.variants'
    
    type Variant = 'default' | 'secondary' | 'destructive' | 'outline'
    type Size = 'mini' | 'default' | 'large' | 'extended'
    type Position = 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'bottom-center' | 'inline'
    
    interface Props {
      /** Label text — renders an extended FAB. Use the default slot for an icon. */
      label?: string
      as?: string
      asChild?: boolean
      variant?: Variant
      size?: Size
      position?: Position
      /** Use absolute instead of fixed positioning (for contained FABs). */
      absolute?: boolean
      disabled?: boolean
      /** Accessible label. Defaults to the label prop or 'Floating action'. */
      ariaLabel?: string
      class?: HTMLAttributes['class']
    }
    
    const props = withDefaults(defineProps<Props>(), {
      as: 'button',
      variant: 'default',
      size: 'default',
      position: 'bottom-right',
      absolute: false,
    })
    
    const emit = defineEmits<{ (e: 'click', event: MouseEvent): void }>()
    
    function onClick(e: MouseEvent) {
      if (props.disabled) return
      emit('click', e)
    }
    
    const resolvedSize = () => (props.label ? 'extended' : props.size)
    </script>
    
    <template>
      <Primitive
        data-uipkge
        data-slot="fab"
        :data-variant="variant"
        :data-size="resolvedSize()"
        :data-position="position"
        :as="as"
        :as-child="asChild"
        :disabled="disabled"
        :aria-label="ariaLabel || label || 'Floating action'"
        :class="
          cn(
            fabVariants({ variant, size: resolvedSize(), position }),
            absolute && position !== 'inline' && 'absolute',
            props.class,
          )
        "
        @click="onClick"
      >
        <slot />
        <span v-if="label" class="pr-1">{{ label }}</span>
      </Primitive>
    </template>
  • app/components/ui/fab/fab.variants.ts 1.6 kB
    import type { VariantProps } from 'class-variance-authority'
    import { cva } from 'class-variance-authority'
    
    export const fabVariants = cva(
      "inline-flex items-center justify-center gap-2 rounded-full font-medium shadow-lg transition-[color,background-color,box-shadow,transform] duration-200 outline-none focus-visible:ring-ring/50 focus-visible:ring-[3px] active:scale-95 touch-manipulation [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-6 disabled:pointer-events-none disabled:opacity-50",
      {
        variants: {
          variant: {
            default: 'bg-primary text-primary-foreground hover:bg-primary/90',
            secondary: 'bg-secondary text-secondary-foreground hover:bg-secondary/80',
            destructive: 'bg-destructive text-white hover:bg-destructive/90',
            outline:
              'border bg-background text-foreground hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50',
          },
          size: {
            mini: "size-10 [&_svg:not([class*='size-'])]:size-4",
            default: 'size-14',
            large: "size-16 [&_svg:not([class*='size-'])]:size-7",
            extended: 'h-14 px-5',
          },
          position: {
            'bottom-right': 'fixed bottom-6 right-6',
            'bottom-left': 'fixed bottom-6 left-6',
            'top-right': 'fixed top-6 right-6',
            'top-left': 'fixed top-6 left-6',
            'bottom-center': 'fixed bottom-6 left-1/2 -translate-x-1/2',
            inline: '',
          },
        },
        defaultVariants: {
          variant: 'default',
          size: 'default',
          position: 'bottom-right',
        },
      },
    )
    
    export type FabVariants = VariantProps<typeof fabVariants>
  • app/components/ui/fab/index.ts 0.1 kB
    export { default as Fab } from './Fab.vue'
    
    export { fabVariants, type FabVariants } from './fab.variants'

Raw manifest: https://uipkge.dev/r/vue/fab.json