Forms radiogroup

Custom Radio Group

Styled radio button collection.

Preview

Usage

Copy the full block below to use this component with its imports.

astro
---
import { CustomRadioGroup } from 'astro-component-kit';
---

<CustomRadioGroup 
  label="Select your billing cycle" 
  name="billing" 
  options={[
    { label: "Monthly", value: "monthly" },
    { label: "Yearly (Save 20%)", value: "yearly", checked: true },
    { label: "Lifetime", value: "life" }
  ]} 
/>
--- import { CustomRadioGroup } from 'astro-component-kit'; --- <CustomRadioGroup label="Select your billing cycle" name="billing" options={[ { label: "Monthly", value: "monthly" }, { label: "Yearly (Save 20%)", value: "yearly", checked: true }, { label: "Lifetime", value: "life" } ]} />

Manual Installation

If you are not using the npm package, create a new file src/components/lib/CustomRadioGroup.astro and paste the following code:

astro
---
/**
 * CustomRadioGroup — A group of styled radio buttons for vertical or horizontal options.
 * 
 * @param {string} label - Optional title for the fieldset.
 * @param {string} name - Common HTML name for the radio group.
 * @param {Array<{label: string, value: string, checked?: boolean}>} options - List of choices.
 * @param {boolean} horizontal - Layout orientation. Default is false (vertical).
 */
interface Props {
  label?: string;
  name: string;
  options: Array<{ label: string; value: string; checked?: boolean }>;
  horizontal?: boolean;
}

const { label, name, options = [], horizontal = false } = Astro.props;
---

<fieldset class:list={["radio-group", { "radio-group--horizontal": horizontal }]}>
  {label && <legend class="radio-group-legend">{label}</legend>}
  <div class="radio-items">
    {options.map((opt, i) => (
      <label class="radio-item">
        <input type="radio" name={name} value={opt.value} checked={opt.checked} />
        <span class="radio-circle" aria-hidden="true"></span>
        <span class="radio-label">{opt.label}</span>
      </label>
    ))}
  </div>
</fieldset>

<style>
  .radio-group { border: none; padding: 0; margin: 0; width: 100%; }
  .radio-group-legend { font-size: 0.85rem; font-weight: 600; color: var(--c-text-2, #94a3b8); margin-bottom: var(--sp-3, 0.75rem); margin-left: 0.5rem; }
  
  .radio-items { display: flex; flex-direction: column; gap: var(--sp-3, 0.8rem); }
  .radio-group--horizontal .radio-items { flex-direction: row; flex-wrap: wrap; gap: var(--sp-6, 1.5rem); }
  
  .radio-item { display: flex; align-items: center; gap: 0.75rem; cursor: pointer; color: var(--c-text-2, #94a3b8); transition: 0.2s; user-select: none; }
  
  .radio-item input { appearance: none; display: none; }
  
  .radio-circle { 
    width: 20px; height: 20px; 
    border: 2px solid var(--c-border, #334155); 
    border-radius: var(--r-full, 50%); 
    position: relative; cursor: pointer; transition: 0.2s;
    background: var(--c-bg-elev, rgba(255,255,255,0.02));
  }
  
  .radio-item:hover .radio-circle { border-color: var(--c-primary-light, #818cf8); }
  
  .radio-circle::after { 
    content: ''; position: absolute; inset: 4px; 
    background: var(--c-primary, #6366f1); 
    border-radius: 50%; opacity: 0; transform: scale(0.5); transition: 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275);
  }
  
  .radio-item input:checked + .radio-circle { border-color: var(--c-primary, #6366f1); box-shadow: 0 0 10px rgba(99, 102, 241, 0.2); }
  .radio-item input:checked + .radio-circle::after { opacity: 1; transform: scale(1); }
  
  .radio-label { font-size: 0.95rem; }
  .radio-item input:checked ~ .radio-label { color: var(--c-text-1, #fff); font-weight: 600; }
</style>
--- /** * CustomRadioGroup — A group of styled radio buttons for vertical or horizontal options. * * @param {string} label - Optional title for the fieldset. * @param {string} name - Common HTML name for the radio group. * @param {Array<{label: string, value: string, checked?: boolean}>} options - List of choices. * @param {boolean} horizontal - Layout orientation. Default is false (vertical). */ interface Props { label?: string; name: string; options: Array<{ label: string; value: string; checked?: boolean }>; horizontal?: boolean; } const { label, name, options = [], horizontal = false } = Astro.props; --- <fieldset class:list={["radio-group", { "radio-group--horizontal": horizontal }]}> {label && <legend class="radio-group-legend">{label}</legend>} <div class="radio-items"> {options.map((opt, i) => ( <label class="radio-item"> <input type="radio" name={name} value={opt.value} checked={opt.checked} /> <span class="radio-circle" aria-hidden="true"></span> <span class="radio-label">{opt.label}</span> </label> ))} </div> </fieldset> <style> .radio-group { border: none; padding: 0; margin: 0; width: 100%; } .radio-group-legend { font-size: 0.85rem; font-weight: 600; color: var(--c-text-2, #94a3b8); margin-bottom: var(--sp-3, 0.75rem); margin-left: 0.5rem; } .radio-items { display: flex; flex-direction: column; gap: var(--sp-3, 0.8rem); } .radio-group--horizontal .radio-items { flex-direction: row; flex-wrap: wrap; gap: var(--sp-6, 1.5rem); } .radio-item { display: flex; align-items: center; gap: 0.75rem; cursor: pointer; color: var(--c-text-2, #94a3b8); transition: 0.2s; user-select: none; } .radio-item input { appearance: none; display: none; } .radio-circle { width: 20px; height: 20px; border: 2px solid var(--c-border, #334155); border-radius: var(--r-full, 50%); position: relative; cursor: pointer; transition: 0.2s; background: var(--c-bg-elev, rgba(255,255,255,0.02)); } .radio-item:hover .radio-circle { border-color: var(--c-primary-light, #818cf8); } .radio-circle::after { content: ''; position: absolute; inset: 4px; background: var(--c-primary, #6366f1); border-radius: 50%; opacity: 0; transform: scale(0.5); transition: 0.2s cubic-bezier(0.175, 0.885, 0.32, 1.275); } .radio-item input:checked + .radio-circle { border-color: var(--c-primary, #6366f1); box-shadow: 0 0 10px rgba(99, 102, 241, 0.2); } .radio-item input:checked + .radio-circle::after { opacity: 1; transform: scale(1); } .radio-label { font-size: 0.95rem; } .radio-item input:checked ~ .radio-label { color: var(--c-text-1, #fff); font-weight: 600; } </style>

Quick Info

Category
Forms
Filename
CustomRadioGroup.astro
Dependencies
None — pure Astro + CSS
Tags
radiogroup