Components

Checkbox

A control that allows the user to toggle between checked and not checked.

Save my login details for next time.

Source Code

Copy the following code into your project.

<template>
  <div :class="cn('flex gap-3', !hasDescription && 'items-center')">
    <input
      :id="inputId"
      :class="cn(variants({ size, type }))"
      type="checkbox"
      :name="name"
      :value="value"
      v-model="isChecked"
      :checked="checked"
      v-bind="$attrs"
    />
    <div v-if="hasLabel || hasDescription" class="flex flex-col gap-px">
      <slot name="label">
        <label
          :class="cn('cursor-pointer font-medium leading-none', size === 'sm' && 'text-sm')"
          :for="inputId"
          v-if="label"
          >{{ label }}</label
        >
      </slot>
      <slot name="description">
        <p class="text-sm text-muted-foreground" v-if="description">{{ description }}</p>
      </slot>
    </div>
  </div>
</template>

<script setup lang="ts">
  import { cva, type VariantProps } from "class-variance-authority";

  const variants = cva(
    "form-checkbox cursor-pointer rounded-md bg-transparent transition focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-offset-background",
    {
      variants: {
        type: {
          muted: "border-input text-gray-800 focus:ring-muted",
          primary: "border-primary/50 text-primary dark:text-border focus:ring-primary/30",
          success: "border-success/50 text-success focus:ring-success/30",
          error: "border-destructive/50 text-destructive focus:ring-destructive/30",
          warning: "border-warning/50 text-warning focus:ring-warning/30",
          info: "border-info/50 text-info focus:ring-info/30",
        },
        size: {
          sm: "h-4 w-4 rounded",
          md: "h-[20px] w-[20px]",
          lg: "h-[26px] w-[26px]",
        },
      },
      defaultVariants: {
        size: "md",
        type: "muted",
      },
    }
  );

  type Props = VariantProps<typeof variants>;

  defineOptions({ inheritAttrs: false });

  const props = withDefaults(
    defineProps<{
      /**
       * The value of the checkbox
       */
      value?: any;
      /**
       * v-model binding
       */
      modelValue?: any;
      /**
       * The name of the checkbox
       */
      name?: string;
      /**
       * The label of the checkbox
       */
      label?: string;
      /**
       * The description of the checkbox
       */
      description?: string;
      /**
       * The size of the checkbox
       */
      size?: Props["size"];
      /**
       * The type of the checkbox
       */
      type?: Props["type"];
      /**
       * The id of the checkbox
       */
      id?: any;
      /**
       * Checked state of the checkbox
       */
      checked?: boolean;
    }>(),
    { size: "md", type: "muted" }
  );

  const slots = useSlots();

  // Get the ID for the input element
  const inputId = computed(
    () => props.id || `checkbox-${Math.random().toString(36).substring(2, 9)}`
  );

  // check if a label or description is provided
  const hasLabel = computed(() => props.label || slots.label);
  const hasDescription = computed(() => props.description || slots.description);

  const emit = defineEmits<{
    "update:modelValue": [any];
  }>();

  const isChecked = computed({
    get() {
      return props.modelValue;
    },
    set(value) {
      emit("update:modelValue", value);
    },
  });
</script>

Usage

Sizes

Types