Radio
Native radio input styled with custom dot. Supports inline groups and card-style options.
Overview
The Radio component is a core building block of the Byteion design system, providing a robust, accessibility-compliant interface for its specific UI capabilities.
Why this exists
Radio buttons communicate mutually exclusive choices. Unlike select dropdowns, all options are visible at once, enabling faster comparison.
Mental model
Only one radio button can be selected within a group. Use them when the user must choose exactly one option from a visible list.
When to use
When not to use
Basic example
The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.
Default
Card style
Stacked
Disabled
import { useState } from 'react';
import { Radio, RadioGroup } from '@byteion/react';
export default function RadioDemo() {
const [plan, setPlan] = useState('pro');
return (
<RadioGroup label="Choose a plan" value={plan} onChange={setPlan} className="space-y-2 p-6">
<Radio value="free" label="Free — for hobby projects" />
<Radio value="pro" label="Pro — for teams up to 10" />
<Radio value="enterprise" label="Enterprise — SSO, audit logs, SLA" />
</RadioGroup>
);
}
<script setup lang="ts">
import { ref } from 'vue';
import { Radio, RadioGroup } from '@byteion/vue';
const plan = ref('pro');
</script>
<template>
<RadioGroup v-model="plan" label="Choose a plan" class="space-y-2 p-6">
<Radio value="free" label="Free — for hobby projects" />
<Radio value="pro" label="Pro — for teams up to 10" />
<Radio value="enterprise" label="Enterprise — SSO, audit logs, SLA" />
</RadioGroup>
</template>
Variants
Each variant communicates a different meaning. Choose based on priority, not aesthetics.
Single default variant.
Sizes
Size should match the importance and context of the component.
- md — md size.
States
This component reacts seamlessly to the following interaction and data states:
- default — Resting appearance before interaction.
- hover — Pointer is over the element — visual feedback via color or shadow.
- focus-visible — focus-visible state.
- checked — Checkbox or radio is selected.
- disabled — Non-interactive — grayed out, cursor not-allowed.
- focus — Keyboard or tab focus — shows ring indicator for accessibility.
- active — Currently selected or in use.
Anatomy
name attributeUsage guidelines
- Always preselect one option when there is a sensible default.
- Keep options under 5.
- Use vertical layout for readability.
Do and Don't
Do
- Preselect a sensible default.
- Use vertical stacking.
- Keep labels concise.
Don't
- Do not leave all options unselected if there is a default.
- Do not use for multi-select.
- Do not use for more than 5 options.
Accessibility
Arrow keys navigate between radio buttons within a group. Enter/Space selects. Group via shared name attribute.
Role:radio
ARIA Attributes
aria-checked
Keyboard Navigation
- ArrowKeys: Navigates between items sequentially
- Space: Toggles selection or activates the element
Customization
Dot color follows --primary.
Compose with React cva()
Use the exportedcva() +cn() utilities to compose your own variant on top of the Byteion tokens.
import { cva, cn, type VariantProps } from '@byteion/react';
const myRadio = cva('base classes…', {
variants: {
accent: { soft:'border-primary-200', loud:'border-primary-500' },
},
defaultVariants: { accent:'soft' },
});
type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof myRadio>;
Compose with Vue cva()
Use the exportedcva() +cn() utilities inside standard Vue setup blocks.
<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cva, type VariantProps } from '@byteion/vue';
interface Props { accent?:'soft' |'loud' }
const props = withDefaults(defineProps<Props>(), { accent:'soft' });
const attrs = useAttrs();
const myRadio = cva('base classes…', {
variants: { accent: { soft:'border-primary-200', loud:'border-primary-500' } },
});
const cls = computed(() => myRadio({ accent: props.accent, class: attrs.class as string | undefined }));
</script>
Runtime events
Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.
HTML / JS events
No HTML runtime events are declared by this adapter.
React callbacks
onValueChange(value: string) => void
Vue emits
update:modelValue[value: string]; change: [event: Event]
Remote / AJAX
This component does not implement Byteion JS remote loading. Use normal HTML form submission, framework state, or your application data layer around it.
API Reference
Native radio element with shared name attribute.
HTML Data Attributes
No custom data attributes required.
React Props · RadioProps
Extracted directly fromreact/src/components/Radio.tsx.
export interface RadioProps
extends Omit<InputHTMLAttributes<HTMLInputElement>, 'type' | 'size' | 'value'>,
VariantProps<typeof radioInput> {
value: string;
label?: ReactNode;
description?: ReactNode;
size?: keyof typeof SIZE_BOX;
}
React Variant Schema
Internal variant composition defined viacva().
cva(
'peer m-0 h-full w-full cursor-pointer appearance-none rounded-full border bg-card shadow-inner-sm transition-all duration-150 ease-out-soft hover:border-primary-400 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-500/30 checked:bg-card checked:border-[6px] dark:bg-muted dark:checked:bg-background',
{
variants: {
tone: {
primary: 'border-border checked:border-primary-500 dark:border-border dark:checked:border-primary-500',
success: 'border-border checked:border-success-500 dark:border-border dark:checked:border-success-500',
danger: 'border-border checked:border-danger-500 dark:border-border dark:checked:border-danger-500',
neutral: 'border-border checked:border-muted-foreground dark:border-border dark:checked:border-muted-foreground',
},
},
defaultVariants: { tone: 'primary' },
},
)
Vue Props · RadioProps
Extracted directly fromvue/src/components/Radio.vue.
export interface RadioProps {
value: string;
tone?: 'primary' | 'success' | 'danger' | 'neutral';
size?: 'sm' | 'md' | 'lg';
label?: string;
description?: string;
disabled?: boolean;
id?: string;
name?: string;
/** Use when rendered outside <RadioGroup>; otherwise use the group's modelValue. */
modelValue?: string;
}
Developer tips
Common mistakes
Production usage tips
data-bi-* attributes instead of styling hooks.
Premium examples
Advanced patterns available in Byteion Pro:
Related components
Unlock Premium Patterns
Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.
Explore Pro Components