Radio Group
A set of checkable buttons where no more than one of the buttons can be checked at a time.
Loading...
<twig:RadioGroup class="w-fit">
<div class="flex items-center gap-3">
<twig:RadioGroup:Item id="r1" name="spacing" value="default" />
<twig:Label for="r1">Default</twig:Label>
</div>
<div class="flex items-center gap-3">
<twig:RadioGroup:Item id="r2" name="spacing" value="comfortable" checked />
<twig:Label for="r2">Comfortable</twig:Label>
</div>
<div class="flex items-center gap-3">
<twig:RadioGroup:Item id="r3" name="spacing" value="compact" />
<twig:Label for="r3">Compact</twig:Label>
</div>
</twig:RadioGroup>
Installation
bin/console ux:install radio-group --kit shadcn
That's it!
Install the following Composer dependencies:
composer require tales-from-a-dev/twig-tailwind-extra:^1.0.0
Copy the following file(s) into your Symfony app:
templates/components/RadioGroup.html.twig
{# @block content The radio items, typically multiple `RadioGroup:Item` components with `Label` #}
<div
role="radiogroup"
class="{{ ('grid w-full gap-2 ' ~ attributes.render('class'))|tailwind_merge }}"
{{ attributes.defaults({
'data-slot': 'radio-group',
}) }}
>
{%- block content %}{% endblock -%}
</div>
templates/components/RadioGroup/Item.html.twig
{# @prop name string The name shared by all radio inputs in the same group #}
{# @prop value string The value submitted when this item is selected #}
{%- props name, value -%}
<label class="{{ ('relative inline-flex size-4 shrink-0 items-center justify-center cursor-pointer group-data-[disabled=true]/field:cursor-not-allowed group-data-[disabled=true]/field:opacity-50 ' ~ attributes.render('class'))|tailwind_merge }}">
<input
type="radio"
name="{{ name }}"
value="{{ value }}"
class="peer sr-only"
{{ attributes.defaults({
'data-slot': 'radio-group-item',
}) }}
>
<span class="flex size-4 cursor-pointer items-center justify-center rounded-full border border-input outline-none peer-focus-visible:border-ring peer-focus-visible:ring-[3px] peer-focus-visible:ring-ring/50 peer-disabled:cursor-not-allowed peer-aria-invalid:border-destructive peer-aria-invalid:ring-[3px] peer-aria-invalid:ring-destructive/20 dark:bg-input/30 dark:peer-aria-invalid:border-destructive/50 dark:peer-aria-invalid:ring-destructive/40 peer-checked:border-primary peer-checked:bg-primary dark:peer-checked:bg-primary"></span>
<span class="pointer-events-none absolute top-1/2 left-1/2 size-2 -translate-x-1/2 -translate-y-1/2 scale-0 rounded-full bg-primary-foreground transition-transform peer-checked:scale-100"></span>
</label>
Happy coding!
Usage
<twig:RadioGroup class="w-fit">
<div class="flex items-center gap-3">
<twig:RadioGroup:Item id="option-a" name="option" value="a" checked />
<twig:Label for="option-a">Option A</twig:Label>
</div>
<div class="flex items-center gap-3">
<twig:RadioGroup:Item id="option-b" name="option" value="b" />
<twig:Label for="option-b">Option B</twig:Label>
</div>
</twig:RadioGroup>
Examples
Description
Loading...
<twig:RadioGroup class="w-fit">
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="desc-r1" name="density" value="default" />
<twig:Field:Content>
<twig:Field:Label for="desc-r1">Default</twig:Field:Label>
<twig:Field:Description>Standard spacing for most use cases.</twig:Field:Description>
</twig:Field:Content>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="desc-r2" name="density" value="comfortable" checked />
<twig:Field:Content>
<twig:Field:Label for="desc-r2">Comfortable</twig:Field:Label>
<twig:Field:Description>More space between elements.</twig:Field:Description>
</twig:Field:Content>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="desc-r3" name="density" value="compact" />
<twig:Field:Content>
<twig:Field:Label for="desc-r3">Compact</twig:Field:Label>
<twig:Field:Description>Minimal spacing for dense layouts.</twig:Field:Description>
</twig:Field:Content>
</twig:Field>
</twig:RadioGroup>
Choice Card
Loading...
<twig:RadioGroup class="max-w-sm">
<twig:Field:Label for="plus-plan" class="border border-input rounded-md has-[:checked]:bg-primary/5 has-[:checked]:border-primary/20 dark:has-[:checked]:bg-primary/10">
<twig:Field orientation="horizontal" class="p-3">
<twig:Field:Content>
<twig:Field:Title>Plus</twig:Field:Title>
<twig:Field:Description>For individuals and small teams.</twig:Field:Description>
</twig:Field:Content>
<twig:RadioGroup:Item id="plus-plan" name="plan" value="plus" checked />
</twig:Field>
</twig:Field:Label>
<twig:Field:Label for="pro-plan" class="border border-input rounded-md has-[:checked]:bg-primary/5 has-[:checked]:border-primary/20 dark:has-[:checked]:bg-primary/10">
<twig:Field orientation="horizontal" class="p-3">
<twig:Field:Content>
<twig:Field:Title>Pro</twig:Field:Title>
<twig:Field:Description>For growing businesses.</twig:Field:Description>
</twig:Field:Content>
<twig:RadioGroup:Item id="pro-plan" name="plan" value="pro" />
</twig:Field>
</twig:Field:Label>
<twig:Field:Label for="enterprise-plan" class="border border-input rounded-md has-[:checked]:bg-primary/5 has-[:checked]:border-primary/20 dark:has-[:checked]:bg-primary/10">
<twig:Field orientation="horizontal" class="p-3">
<twig:Field:Content>
<twig:Field:Title>Enterprise</twig:Field:Title>
<twig:Field:Description>For large teams and enterprises.</twig:Field:Description>
</twig:Field:Content>
<twig:RadioGroup:Item id="enterprise-plan" name="plan" value="enterprise" />
</twig:Field>
</twig:Field:Label>
</twig:RadioGroup>
Fieldset
Loading...
<twig:Field:Set class="w-full max-w-xs">
<twig:Field:Legend variant="label">Subscription Plan</twig:Field:Legend>
<twig:Field:Description>Yearly and lifetime plans offer significant savings.</twig:Field:Description>
<twig:RadioGroup class="gap-3">
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="plan-monthly" name="subscription" value="monthly" checked />
<twig:Field:Label for="plan-monthly" class="font-normal">Monthly ($9.99/month)</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="plan-yearly" name="subscription" value="yearly" />
<twig:Field:Label for="plan-yearly" class="font-normal">Yearly ($99.99/year)</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="plan-lifetime" name="subscription" value="lifetime" />
<twig:Field:Label for="plan-lifetime" class="font-normal">Lifetime ($299.99)</twig:Field:Label>
</twig:Field>
</twig:RadioGroup>
</twig:Field:Set>
Disabled
Loading...
<twig:RadioGroup class="w-fit">
<twig:Field orientation="horizontal" data-disabled="true" class="opacity-50">
<twig:RadioGroup:Item id="disabled-1" name="disabled-example" value="option1" disabled />
<twig:Field:Label for="disabled-1" class="font-normal">Disabled</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="disabled-2" name="disabled-example" value="option2" checked />
<twig:Field:Label for="disabled-2" class="font-normal">Option 2</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="disabled-3" name="disabled-example" value="option3" />
<twig:Field:Label for="disabled-3" class="font-normal">Option 3</twig:Field:Label>
</twig:Field>
</twig:RadioGroup>
Invalid
Loading...
<twig:Field:Set class="w-full max-w-xs">
<twig:Field:Legend variant="label">Notification Preferences</twig:Field:Legend>
<twig:Field:Description>Choose how you want to receive notifications.</twig:Field:Description>
<twig:RadioGroup>
<twig:Field orientation="horizontal" data-invalid="true">
<twig:RadioGroup:Item id="invalid-email" name="notifications" value="email" checked aria-invalid="true" />
<twig:Field:Label for="invalid-email" class="font-normal">Email only</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal" data-invalid="true">
<twig:RadioGroup:Item id="invalid-sms" name="notifications" value="sms" aria-invalid="true" />
<twig:Field:Label for="invalid-sms" class="font-normal">SMS only</twig:Field:Label>
</twig:Field>
<twig:Field orientation="horizontal" data-invalid="true">
<twig:RadioGroup:Item id="invalid-both" name="notifications" value="both" aria-invalid="true" />
<twig:Field:Label for="invalid-both" class="font-normal">Both Email & SMS</twig:Field:Label>
</twig:Field>
</twig:RadioGroup>
</twig:Field:Set>
RTL
Loading...
<twig:RadioGroup class="w-fit" dir="rtl">
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="r1-rtl" name="density-rtl" value="default" dir="rtl" />
<twig:Field:Content>
<twig:Field:Label for="r1-rtl" dir="rtl">افتراضي</twig:Field:Label>
<twig:Field:Description dir="rtl">تباعد قياسي لمعظم حالات الاستخدام.</twig:Field:Description>
</twig:Field:Content>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="r2-rtl" name="density-rtl" value="comfortable" checked dir="rtl" />
<twig:Field:Content>
<twig:Field:Label for="r2-rtl" dir="rtl">مريح</twig:Field:Label>
<twig:Field:Description dir="rtl">مساحة أكبر بين العناصر.</twig:Field:Description>
</twig:Field:Content>
</twig:Field>
<twig:Field orientation="horizontal">
<twig:RadioGroup:Item id="r3-rtl" name="density-rtl" value="compact" dir="rtl" />
<twig:Field:Content>
<twig:Field:Label for="r3-rtl" dir="rtl">مضغوط</twig:Field:Label>
<twig:Field:Description dir="rtl">تباعد أدنى للتخطيطات الكثيفة.</twig:Field:Description>
</twig:Field:Content>
</twig:Field>
</twig:RadioGroup>
API Reference
RadioGroup
| Block | Description |
|---|---|
content |
The radio items, typically multiple RadioGroup:Item components with Label |
RadioGroup:Item
| Prop | Type | Description |
|---|---|---|
name |
string |
The name shared by all radio inputs in the same group |
value |
string |
The value submitted when this item is selected |