# `PhiaUi.Components.RadioGroup`
[🔗](https://github.com/charlenopires/PhiaUI/blob/v0.1.17/lib/phia_ui/components/inputs/radio_group.ex#L1)

Mutually-exclusive radio button group component for Phoenix LiveView.

Renders a group of radio buttons with custom circular indicators styled with
Tailwind semantic tokens. All inputs are native `<input type="radio">` elements,
so keyboard navigation (arrow keys), screen readers, and browser autofill work
out-of-the-box without any JavaScript.

## Sub-components

| Function           | Purpose                                                             |
|--------------------|---------------------------------------------------------------------|
| `radio_group/1`    | Container with `role="radiogroup"`, exposes context via `:let`      |
| `radio_group_item/1` | Individual styled radio option with a custom circular indicator   |
| `form_radio_group/1` | Full group integrated with `Phoenix.HTML.FormField` and errors   |

## Standalone usage with `:let` context

The `:let` pattern passes the group's shared `name` and current `value` down
to each item automatically, avoiding repetition:

    <.radio_group :let={group} value={@plan} name="plan" phx-change="set_plan">
      <.radio_group_item value="free"       label="Free"       {group} />
      <.radio_group_item value="pro"        label="Pro"        {group} />
      <.radio_group_item value="enterprise" label="Enterprise" {group} />
    </.radio_group>

The `{group}` spread assigns `name={group.name}` and `group_value={group.group_value}`
to each item so they know which one is currently selected.

## Horizontal layout

    <.radio_group :let={group} value={@size} name="size" orientation="horizontal">
      <.radio_group_item value="xs" label="XS" {group} />
      <.radio_group_item value="sm" label="SM" {group} />
      <.radio_group_item value="md" label="MD" {group} />
      <.radio_group_item value="lg" label="LG" {group} />
    </.radio_group>

## Form-integrated usage

`form_radio_group/1` is the simplest API for Ecto changeset forms. Pass an
`options` list and the component handles name, value, and error display:

    <.form_radio_group
      field={@form[:plan]}
      label="Subscription plan"
      options={[{"Free", "free"}, {"Pro", "pro"}, {"Team", "team"}]}
    />

    <.form_radio_group
      field={@form[:experience_level]}
      label="Experience level"
      orientation="horizontal"
      options={[{"Beginner", "beginner"}, {"Intermediate", "intermediate"}, {"Advanced", "advanced"}]}
    />

## Settings form example

    <.form for={@form} phx-submit="save_preferences">
      <.form_radio_group
        field={@form[:theme]}
        label="Color theme"
        orientation="horizontal"
        options={[{"Light", "light"}, {"Dark", "dark"}, {"System", "system"}]}
      />
      <.form_radio_group
        field={@form[:notifications]}
        label="Notification frequency"
        options={[
          {"Real-time", "realtime"},
          {"Hourly digest", "hourly"},
          {"Daily digest", "daily"},
          {"Never", "never"}
        ]}
      />
      <.button type="submit">Save preferences</.button>
    </.form>

## Accessibility

- `role="radiogroup"` on the container groups items for screen readers
- Native `<input type="radio">` with `class="sr-only"` handles keyboard
  navigation (arrow keys cycle through options) and screen reader announcements
- Labels are associated via `for`/`id` so clicking the text selects the option
- `peer-focus-visible:ring-2` on the visual indicator provides keyboard focus feedback
- Disabled items have `aria-disabled` semantics via the native `disabled` attribute

# `form_radio_group`

Renders a complete radio group integrated with `Phoenix.HTML.FormField`.

This is the simplest API for Ecto changeset forms. Provide `:field` and
`:options` and the component handles everything: deriving the `name` from
the field, pre-selecting the current value, and displaying changeset errors.

Options are provided as `{label, value}` tuples. Values are coerced to
strings via `to_string/1` before comparison, so integer and atom values
from changesets work transparently.

## Examples

    <%!-- Subscription plan selector --%>
    <.form_radio_group
      field={@form[:plan]}
      label="Subscription plan"
      options={[{"Free", "free"}, {"Pro", "pro"}, {"Team", "team"}]}
    />

    <%!-- Horizontal notification preference --%>
    <.form_radio_group
      field={@form[:notification_frequency]}
      label="Notifications"
      orientation="horizontal"
      options={[
        {"Real-time", "realtime"},
        {"Daily digest", "daily"},
        {"Never", "never"}
      ]}
    />

    <%!-- Dynamic options from the database --%>
    <.form_radio_group
      field={@form[:category_id]}
      label="Category"
      options={Enum.map(@categories, &{&1.name, to_string(&1.id)})}
    />

## Attributes

* `field` (`Phoenix.HTML.FormField`) (required) - A `Phoenix.HTML.FormField` struct obtained via `@form[:field_name]`. Provides
  the group `name`, current `value` (for pre-selection), and `errors` for
  changeset validation display.

* `options` (`:list`) - List of `{label, value}` tuples defining the available radio options.
  Each tuple becomes a `radio_group_item/1`. Values are coerced to strings
  via `to_string/1` for comparison with the field value.

  Example: `[{"Free", "free"}, {"Pro", "pro"}, {"Enterprise", "enterprise"}]`

  Defaults to `[]`.
* `label` (`:string`) - Optional group label rendered as a `<p>` above the options. Use to
  describe what the user is choosing. For accessibility, consider wrapping
  in a `<fieldset>` with `<legend>` in complex forms.

  Defaults to `nil`.
* `orientation` (`:string`) - Layout direction forwarded to the `radio_group/1` container.
  `"vertical"` (default) stacks options. `"horizontal"` places them inline.

  Defaults to `"vertical"`. Must be one of `"vertical"`, or `"horizontal"`.
* `class` (`:string`) - Additional CSS classes applied to the outer wrapper `<div>`. Defaults to `nil`.

# `radio_group`

Renders a radio button group container with `role="radiogroup"`.

Exposes group context via `:let` so items receive their shared `name` and
`group_value` (the currently selected value) automatically. Items use this
context to compute their `checked` state without needing it passed explicitly:

    <.radio_group :let={group} value={@selected} name="fruit" phx-change="select_fruit">
      <.radio_group_item value="apple"  label="Apple"  {group} />
      <.radio_group_item value="banana" label="Banana" {group} />
      <.radio_group_item value="cherry" label="Cherry" {group} />
    </.radio_group>

The `{group}` spread is equivalent to:
`name={group.name} group_value={group.group_value}`

## Examples

    <%!-- Vertical (default) --%>
    <.radio_group :let={g} value={@color} name="color" phx-change="pick_color">
      <.radio_group_item value="red"  label="Red"   {g} />
      <.radio_group_item value="blue" label="Blue"  {g} />
    </.radio_group>

    <%!-- Horizontal --%>
    <.radio_group :let={g} value={@size} name="size" orientation="horizontal">
      <.radio_group_item value="sm" label="Small"  {g} />
      <.radio_group_item value="md" label="Medium" {g} />
      <.radio_group_item value="lg" label="Large"  {g} />
    </.radio_group>

## Attributes

* `value` (`:string`) - The currently selected value. The `radio_group_item/1` whose `:value`
  matches this string will render as checked. Pass `nil` for an unselected
  group. This should mirror your LiveView assign (e.g. `@selected_plan`).

  Defaults to `nil`.
* `name` (`:string`) - The HTML `name` attribute shared by all radio inputs in this group. All
  radios with the same name are mutually exclusive. Required for form
  submission. When using `form_radio_group/1`, this is derived from the
  field struct automatically.

  Defaults to `nil`.
* `orientation` (`:string`) - Layout direction for the items. `"vertical"` (default) stacks items in a
  column with `flex-col`. `"horizontal"` places them inline with `flex-row
  flex-wrap`, which wraps on narrow viewports.

  Defaults to `"vertical"`. Must be one of `"vertical"`, or `"horizontal"`.
* `class` (`:string`) - Additional CSS classes applied to the group container `<div>`. Defaults to `nil`.
* Global attributes are accepted. HTML attributes forwarded to the group `<div>`. Typically used for
  `phx-change` to notify the LiveView when the selection changes.
 Supports all globals plus: `["phx-change", "phx-value"]`.
## Slots

* `inner_block` (required) - One or more `radio_group_item/1` components. Use `:let={group}` on the
  parent to pass context, then spread `{group}` onto each item.

# `radio_group_item`

Renders a single radio button item within a `radio_group/1`.

The visible indicator is a custom-styled circle built with CSS. The actual
`<input type="radio">` is hidden with `class="sr-only"` but remains fully
functional for keyboard navigation and form submission. The `<label>` wraps
both the hidden input and the visual indicator so clicking anywhere on the
row selects the option.

The `checked` state is computed at render time by comparing `group_value`
against this item's `value`. A filled inner circle (`<span>`) is rendered
conditionally only when checked.

## Examples

    <%!-- Typical usage inside radio_group with :let context --%>
    <.radio_group :let={g} value={@plan} name="plan">
      <.radio_group_item value="free" label="Free plan" {g} />
      <.radio_group_item value="pro"  label="Pro plan"  {g} />
    </.radio_group>

    <%!-- Manual usage without group context --%>
    <.radio_group_item
      value="monthly"
      label="Monthly billing"
      name="billing_cycle"
      group_value={@billing_cycle}
    />

    <%!-- Disabled item --%>
    <.radio_group_item value="enterprise" label="Enterprise (contact us)" {g} disabled={true} />

## Attributes

* `value` (`:string`) (required) - The value this radio button represents. Submitted to the server when this
  option is selected. Should match the expected changeset field values.

* `label` (`:string`) (required) - Visible text label rendered next to the custom circular indicator.
* `name` (`:string`) - HTML `name` attribute for this radio input. In normal usage this is
  inherited from the group context via `:let` + `{group}` spread. Only
  set this manually when composing items without the group container.

  Defaults to `nil`.
* `group_value` (`:string`) - The group's currently selected value. This item renders as checked when
  `group_value == value`. Inherited from the group context via `{group}`
  spread in typical usage. Only set manually when composing without the group.

  Defaults to `nil`.
* `disabled` (`:boolean`) - When `true`, disables this radio option. The item becomes non-interactive
  and renders at 50% opacity with `cursor-not-allowed`.

  Defaults to `false`.
* `class` (`:string`) - Additional CSS classes applied to the `<label>` wrapper element. Defaults to `nil`.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
