# Radio Indicator

## Usage

Indicators are the selection pictures shared by a checkbox, a radio, a selector, and a Markdown task list. They are decorative. The owning control keeps the input, the role, the accessible name, the focus, and the keyboard. The indicator only turns state into a picture. That split is what makes them themeable: restyle one through its class, or replace the component by name.

```vue
<script setup lang="ts">
import { RadioIndicator } from '@astryx-vue/core/Indicator'
</script>

<template>
  <RadioIndicator state="checked" />
</template>
```

The circle draws in both states. Unchecked is an empty circle, which is what lets it stand in for a check in a selection slot. There is no mixed state. Inside a radio that has no size, the circle stays 18px and the dot stays 8px. On its own, sm is 20px and md is 24px.

The box is [Checkbox Indicator](/en/components/checkbox-indicator). The mark you replace to get this circle everywhere is [Check Indicator](/en/components/check-indicator).

## Anatomy

| Element | Description |
| --- | --- |
| Chrome (checkbox, radio) | Required. The persistent circle drawn in every state. The check indicator has no chrome of its own. |
| State mark | The filled inner dot, shown only when the circle is chosen. |

## Best practices

| Guidance | Practices |
| --- | --- |
| Do | Restyle the stable class first. Replacing the component is the heavier path, for when the shape itself is wrong. |
| Do | Decide whether slot content replaces the mark with isRenderable, not a null check. An empty slot, including a condition that is false, keeps the circle and its dot. |
| Do | A replacement must set aria-hidden. The owning control provides the role and the accessible name. A visible indicator would be announced twice. |
| Do | Use theme tokens for every color, radius, and border width in a replacement. |
| Do | Render a single root element, and let it keep the border radius the focus ring should follow. The radio paints that ring onto the circle when the real input is focused. Do not draw a second ring. |
| Don't | Pass hover or pressed state in as props. Hover reaches the circle through the ancestor class astryx-indicator-scope. |
| Don't | Assume the graphic is mounted only when it is selected. The host renders the empty circle too. That is what lets it replace a check, which draws nothing when unchosen. |

## Examples

### States

Empty and filled circles at sm and md, including disabled. Unchecked stays an empty circle.

```vue
<script setup lang="ts">
import { RadioIndicator } from '@astryx-vue/core/Indicator'
</script>

<template>
  <div class="indicator-board">
    <div v-for="size in (['sm', 'md'] as const)" :key="size" class="indicator-board-row">
      <span class="checkbox-indicator-preview-size">{{ size }}</span>
      <span class="indicator-board-item">
        <RadioIndicator state="unchecked" :size="size" />
        <span>Unchecked</span>
      </span>
      <span class="indicator-board-item">
        <RadioIndicator state="checked" :size="size" />
        <span>Checked</span>
      </span>
      <span class="indicator-board-item">
        <RadioIndicator state="unchecked" :size="size" is-disabled />
        <span>Disabled</span>
      </span>
      <span class="indicator-board-item">
        <RadioIndicator state="checked" :size="size" is-disabled />
        <span>Disabled checked</span>
      </span>
    </div>
  </div>
</template>
```

## Props

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `state` | `'unchecked' \| 'checked'` | — | Required. Single selection has no mixed state. Unchecked draws an empty circle. |
| `size` | `'sm' \| 'md'` | `'md'` | 20px or 24px. The inner dot is 8px or 10px. Omit it inside a radio that has no size, and the circle stays 18px. |
| `isDisabled` | `boolean` | false | Visual only. The circle fades and an empty one uses the muted background. The owning control keeps the disabled semantics. |

## Slots

- The default slot replaces the inner dot and is drawn inside the circle.
- An empty slot, including a condition that is false, keeps the circle and the dot.
- Slot content is decorative. Do not put a focusable control in it.

## Accessibility

- The root always has `aria-hidden="true"`.
- The component sets no interactive role and provides no click or keyboard behavior.
- A passed role, tab order, content editable flag, or attribute whose name starts with aria is dropped.
- The exposed element is the circle.

## Theme

- `.astryx-radio-indicator`: the circle.
- `.astryx-radio-indicator-dot`: the inner dot when chosen.
- `data-state`: unchecked or checked.
- `data-size`: sm or md. Omitted when no size is passed.
- `data-disabled`: disabled styling.

A chosen circle uses the input status color first, so a field error or warning still tints it, then the accent color. Hover comes from an ancestor `.astryx-indicator-scope`, and only for a pointer that can hover. Reduced motion turns the transition off. In forced colors, an empty circle uses the canvas color and a chosen circle uses the highlight.

The focus ring is painted by the radio on `.astryx-radio-native:focus-visible + .astryx-radio-indicator`. The circle's radius is what that outline follows.
