QuesoRadio
A radio button group component that supports single selection from multiple choices, two-way data binding with v-model
, and provides a consistent interface for form inputs. All form field components share the same base structure for consistent styling and behavior across your application.
Basic Usage
vue
<template>
<queso-radio
name="gender"
label="Gender"
:choices="genderChoices"
v-model="selectedGender"
/>
</template>
<script setup>
import { ref } from "vue";
import { QuesoRadio } from "@allomambo/queso";
const selectedGender = ref("");
const genderChoices = [
{ label: "Male", value: "male" },
{ label: "Female", value: "female" },
{ label: "Other", value: "other" },
];
</script>
Props
name
- Type:
string
- Required:
true
- Description: The name attribute for the radio group field.
label
- Type:
string
- Required:
false
- Description: The label text displayed above the radio group.
id
- Type:
string
- Required:
false
- Description: The ID attribute for the radio group field (defaults to
name
if not provided).
choices
- Type:
QuesoRadioChoice[]
- Required:
true
- Description: Array of choices for the radio group. Each choice must have a
label
andvalue
.
isRequired
- Type:
boolean
- Default:
false
- Description: Whether the field is required.
isDisabled
- Type:
boolean
- Default:
false
- Description: Whether the field is disabled.
isReadOnly
- Type:
boolean
- Default:
false
- Description: Whether the field is read-only.
isError
- Type:
boolean
- Default:
false
- Description: Whether the field has an error state.
extraAttributes
- Type:
Record<string, string>
- Required:
false
- Description: Additional HTML attributes to apply to each radio input element.
Slots
beforeLabel
- Props:
ExposedData
- Description: Content displayed before the label text.
label
- Props:
ExposedData
- Description: The main label content. Defaults to the
label
prop if not provided.
required
- Props:
ExposedData
- Description: Content displayed for the required indicator. Defaults to
*
if not provided.
afterLabel
- Props:
ExposedData
- Description: Content displayed after the label text.
beforeInput
- Props:
ExposedData
- Description: Content displayed before the radio group field.
radioBox
- Props:
{}
- Description: Custom radio box element for each choice. Defaults to a styled radio box.
radioBoxSymbol
- Props:
{}
- Description: Symbol displayed inside the radio box when selected. Defaults to "✔︎".
radioLabel
- Props:
{}
- Description: Custom label element for each choice. Defaults to the choice label.
afterInput
- Props:
ExposedData
- Description: Content displayed after the radio group field.
error
- Props:
ExposedData
- Description: Content displayed when the field has an error state.
Examples
Basic TypeScript Support
vue
<template>
<queso-radio
name="preference"
label="Preferred Contact Method"
:choices="contactChoices"
v-model="selectedContact"
/>
</template>
<script setup lang="ts">
import { ref } from "vue";
import { QuesoRadio } from "@allomambo/queso";
import type { QuesoRadioModel, QuesoRadioChoices } from "@allomambo/queso";
const selectedContact = ref<QuesoRadioModel>("");
const contactChoices: QuesoRadioChoices = [
{ label: "Email", value: "email" },
{ label: "Phone", value: "phone" },
{ label: "SMS", value: "sms" },
];
</script>
html
<div class="queso-field">
<div class="queso-field__label">
<span class="queso-field__label__text">Preferred Contact Method</span>
</div>
<div class="queso-field__input">
<label class="queso-radio" for="preference-email">
<span class="queso-radio__box">
<span class="queso-radio__box__symbol">✔︎</span>
</span>
<span class="queso-radio__label">
<span class="queso-radio__label__text">Email</span>
</span>
<input
class="queso-radio__native"
type="radio"
id="preference-email"
name="preference"
value="email"
/>
</label>
<label class="queso-radio" for="preference-phone">
<span class="queso-radio__box">
<span class="queso-radio__box__symbol">✔︎</span>
</span>
<span class="queso-radio__label">
<span class="queso-radio__label__text">Phone</span>
</span>
<input
class="queso-radio__native"
type="radio"
id="preference-phone"
name="preference"
value="phone"
/>
</label>
<label class="queso-radio" for="preference-sms">
<span class="queso-radio__box">
<span class="queso-radio__box__symbol">✔︎</span>
</span>
<span class="queso-radio__label">
<span class="queso-radio__label__text">SMS</span>
</span>
<input
class="queso-radio__native"
type="radio"
id="preference-sms"
name="preference"
value="sms"
/>
</label>
</div>
</div>
Custom Radio Box
vue
<template>
<queso-radio
name="theme"
label="Theme Preference"
:choices="themeChoices"
v-model="selectedTheme"
>
<template #radioBox>
<div class="custom-radio-box">
<div class="custom-radio-circle"></div>
</div>
</template>
<template #radioBoxSymbol>
<div class="custom-radio-dot"></div>
</template>
</queso-radio>
</template>
<script setup>
import { ref } from "vue";
import { QuesoRadio } from "@allomambo/queso";
const selectedTheme = ref("");
const themeChoices = [
{ label: "Light", value: "light" },
{ label: "Dark", value: "dark" },
{ label: "Auto", value: "auto" },
];
</script>
html
<div class="queso-field">
<div class="queso-field__label">
<span class="queso-field__label__text">Theme Preference</span>
</div>
<div class="queso-field__input">
<label class="queso-radio" for="theme-light">
<div class="custom-radio-box">
<div class="custom-radio-circle"></div>
</div>
<span class="queso-radio__label">
<span class="queso-radio__label__text">Light</span>
</span>
<input
class="queso-radio__native"
type="radio"
id="theme-light"
name="theme"
value="light"
/>
</label>
<label class="queso-radio" for="theme-dark">
<div class="custom-radio-box">
<div class="custom-radio-circle"></div>
</div>
<span class="queso-radio__label">
<span class="queso-radio__label__text">Dark</span>
</span>
<input
class="queso-radio__native"
type="radio"
id="theme-dark"
name="theme"
value="dark"
/>
</label>
<label class="queso-radio" for="theme-auto">
<div class="custom-radio-box">
<div class="custom-radio-circle"></div>
</div>
<span class="queso-radio__label">
<span class="queso-radio__label__text">Auto</span>
</span>
<input
class="queso-radio__native"
type="radio"
id="theme-auto"
name="theme"
value="auto"
/>
</label>
</div>
</div>
With Extra Attributes
vue
<template>
<queso-radio
name="notification"
label="Notification Settings"
:choices="notificationChoices"
v-model="selectedNotification"
:extra-attributes="{
'data-testid': 'notification-radio',
'aria-describedby': 'notification-help',
}"
/>
<div id="notification-help" class="help-text">
Choose how you want to receive notifications
</div>
</template>
<script setup>
import { ref } from "vue";
import { QuesoRadio } from "@allomambo/queso";
const selectedNotification = ref("");
const notificationChoices = [
{ label: "All notifications", value: "all" },
{ label: "Important only", value: "important" },
{ label: "None", value: "none" },
];
</script>
html
<div class="queso-field">
<div class="queso-field__label">
<span class="queso-field__label__text">Notification Settings</span>
</div>
<div class="queso-field__input">
<label class="queso-radio" for="notification-all">
<span class="queso-radio__box">
<span class="queso-radio__box__symbol">✔︎</span>
</span>
<span class="queso-radio__label">
<span class="queso-radio__label__text">All notifications</span>
</span>
<input
class="queso-radio__native"
type="radio"
id="notification-all"
name="notification"
value="all"
data-testid="notification-radio"
aria-describedby="notification-help"
/>
</label>
<label class="queso-radio" for="notification-important">
<span class="queso-radio__box">
<span class="queso-radio__box__symbol">✔︎</span>
</span>
<span class="queso-radio__label">
<span class="queso-radio__label__text">Important only</span>
</span>
<input
class="queso-radio__native"
type="radio"
id="notification-important"
name="notification"
value="important"
data-testid="notification-radio"
aria-describedby="notification-help"
/>
</label>
<label class="queso-radio" for="notification-none">
<span class="queso-radio__box">
<span class="queso-radio__box__symbol">✔︎</span>
</span>
<span class="queso-radio__label">
<span class="queso-radio__label__text">None</span>
</span>
<input
class="queso-radio__native"
type="radio"
id="notification-none"
name="notification"
value="none"
data-testid="notification-radio"
aria-describedby="notification-help"
/>
</label>
</div>
</div>
<div id="notification-help" class="help-text">
Choose how you want to receive notifications
</div>
Read-only Field
vue
<template>
<queso-radio
name="status"
label="Account Status"
:choices="statusChoices"
is-read-only
v-model="accountStatus"
/>
</template>
<script setup>
import { ref } from "vue";
import { QuesoRadio } from "@allomambo/queso";
const accountStatus = ref("active");
const statusChoices = [
{ label: "Active", value: "active" },
{ label: "Inactive", value: "inactive" },
{ label: "Suspended", value: "suspended" },
];
</script>
html
<div class="queso-field is-read-only">
<div class="queso-field__label">
<span class="queso-field__label__text">Account Status</span>
</div>
<div class="queso-field__input">
<div class="queso-radio is-selected">
<span class="queso-radio__box">
<span class="queso-radio__box__symbol">✔︎</span>
</span>
<span class="queso-radio__label">
<span class="queso-radio__label__text">Active</span>
</span>
</div>
<div class="queso-radio">
<span class="queso-radio__box">
<span class="queso-radio__box__symbol">✔︎</span>
</span>
<span class="queso-radio__label">
<span class="queso-radio__label__text">Inactive</span>
</span>
</div>
<div class="queso-radio">
<span class="queso-radio__box">
<span class="queso-radio__box__symbol">✔︎</span>
</span>
<span class="queso-radio__label">
<span class="queso-radio__label__text">Suspended</span>
</span>
</div>
</div>
</div>
Behavior
- Two-way Data Binding: Uses Vue 3's
defineModel
for seamlessv-model
integration - Form Integration: Designed to work seamlessly with HTML forms
- Single Selection: Only one choice can be selected at a time
- Read-only Mode: When
is-read-only
is true, displays the choices as non-interactive elements - Customizable Appearance: Slots for customizing radio box and label appearance
Accessibility
- Label Association: Uses proper
label
element withfor
attribute - Required Indicator: Displays visual indicator for required fields
- Error States: Provides clear error messaging and styling
- Keyboard Navigation: Full keyboard accessibility support
- Screen Reader Support: Proper ARIA attributes and semantic structure
- State Management: Tracks hover and focus states for accessibility
- Radio Group: Proper radio group semantics with shared
name
attribute
CSS Classes
Field Container
.queso-field
- Main field container.queso-field.is-disabled
- Disabled state.queso-field.is-error
- Error state.queso-field.is-active
- Focused state.queso-field.is-hover
- Hover state.queso-field.is-read-only
- Read-only state
Label
.queso-field__label
- Label container.queso-field__label__text
- Label text.queso-field__label__required
- Required indicator
Radio Items
.queso-radio
- Individual radio item container.queso-radio.is-selected
- Selected state.queso-radio__box
- Radio box container.queso-radio__box__symbol
- Radio box symbol (checkmark).queso-radio__label
- Radio label container.queso-radio__label__text
- Radio label text
Error
.queso-field__error
- Error message container
Type Declaration
typescript
export interface ExposedData {
fieldID: string;
fieldName: string;
isRequired: boolean;
isDisabled: boolean;
isReadOnly: boolean;
isError: boolean;
isActive: boolean;
isHover: boolean;
}
export interface QuesoRadioChoice {
label: string;
value: string;
}
export type QuesoRadioChoices = QuesoRadioChoice[];
export type QuesoRadioModel = QuesoRadioChoice["value"];
export interface QuesoRadioProps {
name: string;
label?: string;
id?: string;
choices: QuesoRadioChoices;
isRequired?: boolean;
isDisabled?: boolean;
isReadOnly?: boolean;
isError?: boolean;
extraAttributes?: Record<string, string>;
}