๐ŸŸข Radio


Basic

use NRadio to create a radio input.

AttributeDescription
v-modelReactive value of radio if checked or not.
valueThe value of the radio.
labelThe label of the radio.

To view the full list of attributes, see the Props section.

<script setup lang="ts">
const radio = ref('a')
</script>

<template>
  <div class="flex gap-4">
    <NRadio
      v-model="radio"
      value="a"
      label="Radio A"
    />

    <NRadio
      v-model="radio"
      value="b"
      label="Radio B"
    />

    <NRadio
      v-model="radio"
      value="c"
      label="Radio C"
    />
  </div>
</template>

Color

radio="{color}" - change the color of the radio.

You can use breakpoints such as sm:red, xs:green to change color based on screen size.

You can use any color provided by the Tailwind CSS color palette, the default is primary. You can also add your own colors to the palette through the Configuration section.
<script setup lang="ts">
const radio = ref('')

const colors = [
  {
    radio: 'red sm:primary',
  },
  {
    radio: 'info',
  },
  {
    radio: 'error',
  },
  {
    radio: 'warning',
  },
  {
    radio: 'success',
  },
  {
    radio: 'purple',
  },
  {
    radio: 'pink',
  },
  {
    radio: 'violet',
  },
  {
    radio: 'fuchsia',
  },
  {
    radio: 'indigo',
  },
]
</script>

<template>
  <div class="flex flex-wrap gap-4">
    <NRadio
      v-for="color in colors"
      :key="color.radio"
      v-model="radio"
      :label="color.radio"
      :radio="color.radio"
      :value="color.radio"
    />
  </div>
</template>

Disabled

disabled - disable the radio.

<script setup lang="ts">
const radio = ref()
</script>

<template>
  <div class="flex gap-4">
    <NRadio
      v-model="radio"
      value="a"
      label="Disabled"
      disabled
    />

    <NRadio
      v-model="radio"
      value="b"
      label="Not disabled"
    />
  </div>
</template>

Form Group

You can use the NFormGroup component to create a radio group for the radio,

Read more about the NFormGroup component here.

Remember to set for to false to disable for behavior on the label since we have a custom implementation for the radio input.
Please select your service provider
Please select your service provider

Service provider is required

<script setup lang="ts">
const gender = ref('digital ocean')

const options = [
  {
    label: 'Digital Ocean',
    value: 'digital ocean',
    radio: 'blue',
  },
  {
    label: 'AWS',
    value: 'aws',
    radio: 'amber',
  },
  {
    label: 'Google Cloud',
    value: 'google cloud',
    radio: 'red',
  },
  {
    label: 'Heroku',
    value: 'heroku',
    radio: 'purple',
  },
]
</script>

<template>
  <div class="flex flex-col space-y-6">
    <!-- Vertical -->
    <NFormGroup
      :for="false"
      label="Service Provider"
      description="Please select your service provider"
    >
      <NRadio v-for="option in options" :key="option.value" v-model="gender" v-bind="option" />
    </NFormGroup>

    <NSeparator />

    <!-- Horizontal -->
    <NFormGroup
      :for="false"
      label="Service Provider"
      description="Please select your service provider"
      required
      status="error"
      message="Service provider is required"
    >
      <div class="flex flex-wrap gap-8">
        <NRadio v-for="option in options" :key="option.value" v-model="gender" v-bind="option" />
      </div>
    </NFormGroup>
  </div>
</template>

Reverse

reverse - Switch the position of the radio and the label.

<script setup lang="ts">
const radio = ref('a')

const choices = [
  { label: 'Radio A', value: 'a' },
  { label: 'Radio B', value: 'b' },
  { label: 'Radio C', value: 'c' },
]
</script>

<template>
  <div class="flex flex-wrap space-x-8">
    <NRadio
      v-for="choice in choices"
      :key="choice.value"
      v-bind="choice"
      v-model="radio"
      reverse
    />
  </div>
</template>

Size

size="{size}" - change the size of the radio.

๐Ÿš€ You can freely adjust the size of the radio using any size imaginable. No limits exist, and you can use breakpoints such as sm:sm, xs:lg to change size based on screen size.

<script setup lang="ts">
const radio = ref()

const items = [
  {
    label: '0.8cm',
    size: '0.8cm',
  },
  {
    label: 'xs md:2xl',
    size: 'xs md:2xl',
  },
  {
    label: 'sm',
    size: 'sm',
  },
]
</script>

<template>
  <div class="flex gap-4">
    <NRadio
      v-for="item in items"
      :key="item.size"
      v-model="radio"
      :value="item.size"
      v-bind="item"
    />
  </div>
</template>

Rules

You can use the following rules to customize the radio if it is checked.

You can also globally customize the radio preset if you want to have a different default style. See Configuration section for more details.
Rule NameDescriptionExample
n-checkedOnly apply the class if the radio is checked.n-checked:bg-red
<script setup lang="ts">
const radio = ref('a')

const options = [
  {
    value: 'vue',
    radio: 'green',
    class: 'n-checked:bg-green-500/30 n-checked:border-green-500',
  },
  {
    value: 'react',
    radio: 'blue',
    class: 'n-checked:bg-blue-500/30 n-checked:border-blue-500',
  },
  {
    value: 'angular',
    radio: 'red',
    class: 'n-checked:bg-red-500/30 n-checked:border-red-500',
  },
]
</script>

<template>
  <div class="flex flex-wrap gap-4">
    <NRadio
      v-for="option in options"
      v-bind="option"
      :key="option.value"
      v-model="radio"
      :label="option.value"
    />
  </div>
</template>

Customization

You can customize the radio using the una prop and utility classes.

Value:
<script setup lang="ts">
const radio = ref()

const options = [
  {
    value: 'vue',
    class: 'n-checked:bg-green-500/30 n-checked:border-green-500',
    radio: 'green',
    una: {
      radioIcon: 'i-logos-vue n-checked:scale-200 transition-transform duration-1000',
    },
  },
  {
    value: 'react',
    class: 'n-checked:bg-blue-500/30 n-checked:border-blue-500',
    radio: 'blue',
    una: {
      radioIcon: 'i-logos-react  n-checked:rotate-360 transition-transform duration-1000',
    },
  },
  {
    value: 'angular',
    class: 'n-checked:bg-red-500/30 n-checked:border-red-500',
    radio: 'red',
    una: {
      radioIcon: 'i-logos-angular-icon n-checked:scale-150 transition-transform duration-1000',
    },
  },
]
</script>

<template>
  <div class="flex flex-col space-y-6">
    <span>
      Value: {{ radio }}
    </span>

    <div class="flex flex-wrap gap-4">
      <NRadio
        v-for="option in options"
        :key="option.value"
        v-model="radio"
        :value="option.value"
        :radio="option.radio"
        :label="option.value"
        :class="option.class"
        size="15"
        class="rounded-md"
        :una="{
          radioLabel: 'capitalize',
          ...option.una,
        }"
      />
    </div>
  </div>
</template>

Slots

NameDescription
labelUse this slot to customize the label of the radio.
iconUse this slot to customize the icon of the radio when it is checked

Props

export interface NRadioProps {
  /**
   * Disable the radio.
   *
   * @default false
   */
  disabled?: boolean
  /**
   * Switch the position of label and radio.
   *
   * @default false
   */
  reverse?: boolean

  /**
   * Allows you to add `UnaUI` radio 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/radio.ts
   * @example
   * radio="green"
   */
  radio?: string
  /**
   * v-model binding value if the radio is checked.
   *
   * @default null
   */
  modelValue?: string | number | boolean | Record<string, any>
  /**
   * Add name attribute to the radio.
   *
   * @default null
   */
  name?: string
  /**
   * Manually set the id attribute.
   *
   * By default, the id attribute is generated randomly for accessibility reasons.
   *
   * @default randomId
   * @example
   * id="options"
   */
  id?: string
  /**
   * Manually set the for attribute.
   *
   * By default, the for attribute is synced with the id attribute for accessibility reasons.
   *
   * @default randomId
   * @example
   * for="options"
   */
  for?: string
  /**
   * Value of the radio.
   *
   * @example
   * value="1"
   */
  value?: string
  /**
   * Display label of the radio.
   *
   * @default null
   */
  label?: string
  /**
   * Allows you to change the size of the radio.
   *
   * @default size="sm"
   *
   * @example
   * size="sm" | size="2cm" | size="2rem" | size="2px"
   */
  size?: string

  /**
   * `UnaUI` preset configuration
   *
   * @see https://github.com/una-ui/una-ui/blob/main/packages/preset/src/_shortcuts/radio.ts
   */
  una?: {
    radio?: string
    radioWrapper?: string
    radioLabel?: string
    radioIconBase?: string
    radioIcon?: string
  }
}

Presets

import type { RuleContext } from '@unocss/core'
import type { Theme } from '@unocss/preset-uno'
import { parseColor } from '@unocss/preset-mini/utils'

type RadioPrefix = 'radio'

export const staticRadio: Record<`${RadioPrefix}-${string}` | RadioPrefix, string> = {
  // base
  'radio': 'radio-primary flex items-center transition-base border border-$c-ring rounded-full p-0.12em h-1em w-1em n-checked:border-brand n-checked:bg-brand',
  'radio-disabled': 'n-disabled',
  'radio-label': 'block text-sm font-medium leading-6',
  'radio-input': 'absolute w-full opacity-0',
  'radio-reverse': 'flex-row-reverse',
  'radio-peer-focus': 'peer-focus-(ring-2 ring-brand ring-offset-2 ring-offset-base)',

  // wrappers
  'radio-wrapper': 'gap-x-3 relative inline-flex items-center hover:cursor-pointer',

  // icon
  'radio-icon-base': 'm-auto opacity-0 text-inverted w-full h-full transition-base n-checked:opacity-100',
  'radio-icon': 'i-dot', // refer to general.ts
}

export const dynamicRadio = [
  [/^radio-(.*)$/, ([, body]: string[], { theme }: RuleContext<Theme>) => {
    const color = parseColor(body, theme)
    if ((color?.cssColor?.type === 'rgb' || color?.cssColor?.type === 'rgba') && color.cssColor.components)
      return `n-${body}-600 dark:n-${body}-500`
  }],
]

export const radio = [
  ...dynamicRadio,
  staticRadio,
]

Component

<script setup lang="ts">
import type { NRadioProps } from '../../types'
import { useVModel } from '@vueuse/core'
import { computed } from 'vue'
import { randomId } from '../../utils'
import NIcon from '../elements/Icon.vue'

defineOptions({
  inheritAttrs: false,
})

const props = withDefaults(
  defineProps<NRadioProps>(),
  {
    modelValue: '',
    disabled: false,
    una: () => ({
      radioIcon: 'radio-icon',
    }),
  },
)

const emit = defineEmits<{ (...args: any): void }>()

const slots = defineSlots<{
  default?: void
  icon?: any
}>()

const id = computed(() => props.id ?? randomId('radio'))

const model = useVModel(props, 'modelValue', emit, { passive: true })
</script>

<template>
  <label
    radio="wrapper"
    :for="props.for ?? id"
    :class="[
      una?.radioWrapper,
      {
        'radio-reverse': reverse,
        'radio-disabled': disabled,
      },
    ]"
    :checked="model === value || null"
    :disabled="disabled || null"
  >
    <input
      :id="id"
      v-model="model"
      type="radio"
      class="peer"
      radio="input"
      :disabled="disabled"
      :name="name"
      :value="value"
      @keypress.enter="model = value!"
    >
    <span
      :radio="radio"
      :size="size"
      class="radio radio-peer-focus"
      v-bind="$attrs"
    >
      <slot name="icon">
        <NIcon
          :class="[
            una.radioIconBase,
          ]"
          radio="icon-base icon-checked"
          :name="una.radioIcon!"
        />
      </slot>
    </span>
    <div
      v-if="slots.default || label"
      radio="label"
      :class="una?.radioLabel"
    >
      <slot>
        {{ label }}
      </slot>
    </div>
  </label>
</template>