# `hb-input-radio`

**Category:** inputs · **Tags:** inputs

`hb-input-radio` is a Bulma-styled **radio group** web component. It reads its configuration from a single **`schemaentry`** payload (JSON in HTML, or an object when set from JavaScript), builds one native `<input type="radio">` per option under a shared **`name`** equal to the field **`id`**, and reports the current choice with a **`setVal`** custom event.

The group’s accessible name comes from **`label`**, then **`id`**, then the fallback `"Options"` (`aria-label` on the `radiogroup`).

---

## When the component renders

- If **`schemaentry`** is missing, not parseable as JSON, or not an object, **nothing** is rendered.
- If **`params.options`** is missing or empty, the **field shell** still renders but **no radios** appear until you supply at least one option.

---

## Custom element

| Name | Tag |
| --- | --- |
| `hb-input-radio` | `<hb-input-radio …></hb-input-radio>` |

---

## Attributes (snake_case)

Web component attributes are **strings**. For booleans exposed as attributes, use **`yes`** or **`no`** (see `show_validation`).

| Attribute | Required | Description |
|-----------|----------|-------------|
| `schemaentry` | Yes* | Serialized field definition (JSON string from HTML). Must include a stable **`id`** and, for choices, **`params.options`**. Can be set to `undefined` / omitted in typed wrappers; the host then renders nothing. |
| `show_validation` | No | `"yes"` or `"no"` (default **`no`**). When **`yes`**, a required group with no selection shows **`validationTip`** in a Bulma danger help line (if `validationTip` is set). |
| `id` | No | Optional id on the host element. |
| `style` | No | Optional inline styles on the host element. |

\*Required for a usable field in markup; the TypeScript `Component` type allows `undefined` for programmatic cases.

### Passing `schemaentry` from HTML

Use a **single-quoted** attribute value so the JSON can use double quotes:

```html
<hb-input-radio
  schemaentry='{"id":"plan","required":true,"label":"Plan","validationTip":"Select a plan.","params":{"options":[{"label":"Starter","value":"starter"},{"label":"Pro","value":"pro"}]}}'
  show_validation="yes"
></hb-input-radio>
```

From JavaScript you may assign **`element.schemaentry`** as either a **JSON string** or a **plain object**; the internal parser accepts both without mutating the prop.

---

## `schemaentry` shape

The host expects a **`FormSchemaEntry`** compatible object (see `types/webcomponent.type.d.ts` and shared `FormSchemaEntryShared` from `hb-form`). Fields that matter for this component:

| Field | Description |
|-------|-------------|
| `id` | **Required.** Becomes the native `name` for all radios in the group and is echoed on events as `detail.id`. |
| `label` | Optional visible context; used for `aria-label` on the radiogroup. |
| `required` | If true, **`valid`** is false until an option is selected. |
| `value` | Optional initial selection; coerced with `String(...)` when the schema sync runs. |
| `validationTip` | Message shown when **`show_validation="yes"`**, the field is invalid, and this string is set. |
| `disabled` | If true, all radios are disabled. |
| `readonly` | If true, all radios are disabled (same effect as `disabled` in the template). |
| `placeholder` | Allowed by the shared schema type; **not** rendered as a separate control by this component. |
| `params` | **`InputRadioParams`** — see below. |

### `params` (`InputRadioParams`)

| Key | Type | Description |
|-----|------|-------------|
| `options` | `InputRadioOption[]` | List of choices. Each item needs a **`value`** (string). **`label`** is optional; the UI falls back to **`value`**. |

---

## Validation behavior

- **Required (`required: true`)**: `valid` is **false** until the user selects an option (`value` is a non-empty choice).
- **Optional (`required: false` or omitted)**: `valid` is **true** even when nothing is selected (`value` may be `undefined`).
- **Inline message**: Shown only when **`show_validation`** is **`yes`**, **`validationTip`** is set, and the current state is invalid.

---

## Events

| Event | `detail` | When |
|-------|----------|------|
| `setVal` | `{ value: string \| undefined; valid: boolean; id: string }` | Whenever **`value`**, **`valid`**, or **`id`** changes in a way that affects the payload. Duplicate payloads are suppressed. |

Listen in plain JavaScript:

```js
const el = document.querySelector("hb-input-radio");
el.addEventListener("setVal", (e) => {
  const { value, valid, id } = e.detail;
  console.log(id, value, valid);
});
```

---

## Styling

- Bulma **form** patterns: `field`, `control`, `radios`, `radio`, and `help is-danger` for invalid feedback.
- Theme the host with Bulma **CSS variables** on `:host` — see [Bulma CSS variables](https://bulma.io/documentation/features/css-variables/). Public tokens used by this bundle are listed in **`extra/docs.ts`** (`styleSetup.vars`).

### CSS custom properties (high level)

| Variable | Role |
|----------|------|
| `--bulma-text` | Labels and legend-related text. |
| `--bulma-border` | Radio outline; group border when invalid. |
| `--bulma-link` | Checked accent where the browser supports tinting. |
| `--bulma-danger` | Invalid outline and danger help. |
| `--bulma-scheme-main` | Background inside the shadow root. |

### `::part` selectors

| Part | Targets |
|------|---------|
| `input` | Each native `<input type="radio">` (same part name repeated per option). |
| `invalid-feedback` | The `p.help.is-danger` node when validation messaging is visible. |

---

## TypeScript

Authoring types for this package live in **`types/webcomponent.type.d.ts`**:

- **`InputRadioOption`**, **`InputRadioParams`**
- **`FormSchemaEntry`** (radio-specific entry)
- **`Component`** (host props)
- **`Events`** (`setVal` detail)

After a full web component build, generated element maps may also include this tag under **`types/html-elements.d.ts`** / **`types/svelte-elements.d.ts`**.

---

## Examples

### Required group with validation

```html
<hb-input-radio
  schemaentry='{"id":"tier","required":true,"label":"Tier","validationTip":"Choose a tier.","params":{"options":[{"label":"Free","value":"free"},{"label":"Paid","value":"paid"}]}}'
  show_validation="yes"
></hb-input-radio>
```

### Preselected value

```html
<hb-input-radio
  schemaentry='{"id":"region","required":true,"label":"Region","value":"eu","params":{"options":[{"label":"US","value":"us"},{"label":"EU","value":"eu"}]}}'
></hb-input-radio>
```

### Disabled group

```html
<hb-input-radio
  schemaentry='{"id":"locked","label":"Plan","value":"pro","disabled":true,"params":{"options":[{"label":"Starter","value":"starter"},{"label":"Pro","value":"pro"}]}}'
></hb-input-radio>
```

### Updating options from JavaScript

```js
const radio = document.querySelector("hb-input-radio");
radio.schemaentry = JSON.stringify({
  id: "gift",
  required: false,
  label: "Gift wrap",
  params: {
    options: [
      { label: "No", value: "no" },
      { label: "Yes", value: "yes" },
    ],
  },
});
```

---

## Related metadata

Storybook knobs, CSS variables, `::part` names, and packaged examples are defined in **`extra/docs.ts`** (`componentSetup`, `styleSetup`, `examples`).
