# `Plushie.Type.A11y`
[🔗](https://github.com/plushie-ui/plushie-elixir/blob/v0.7.2/lib/plushie/type/a11y.ex#L1)

Accessibility annotation type for widget nodes.

Every widget declares default a11y values (role, label source) in its
widget definition. When the user sets `a11y` on a widget, the user's
values are merged with the defaults (user wins per field). The resolved
a11y flows through the tree to the renderer, selectors, assertions,
and assistive technology.

Most widgets need no explicit `a11y` annotation because the defaults
provide the correct role and derive the label from the widget's props.

## Construction

Builder chain (like Border, Shadow, Font):

    A11y.new()
    |> A11y.role(:heading)
    |> A11y.level(1)
    |> A11y.label("Page title")

Bare map via `cast/1` (convenience for inline props):

    Button.new("btn", "Go", a11y: %{label: "Go forward"})

DSL do-block via `from_opts/1`:

    button "btn", "Go" do
      a11y do
        label "Go forward"
      end
    end

## Override semantics

Most fields are optional overrides. When nil (the default), the
renderer uses its auto-inferred value. When set, the SDK value wins.

Some widgets auto-manage certain fields based on their interaction
state. For example, sliders set `busy: true` during drag so that
assistive technology suppresses rapid value announcements and
announces only the final value on release. Setting `busy` explicitly
from the SDK overrides this auto-detected state.

## Busy and live regions

`busy` maps to WAI-ARIA `aria-busy`. When true on a node (or a
parent of a live region), assistive technology suppresses
announcements until busy clears, then announces the final state.

Widgets that own continuous interactions (sliders) set this
automatically. For app-managed live regions that reflect another
widget's state (e.g. a text display showing a color value during
canvas drag), set `busy` explicitly based on the interaction state:

    text("hex", hex_value,
      a11y: %{live: :polite, busy: model.drag != :none}
    )

## Fields

- `role` - overrides the inferred accesskit role (e.g. `:heading`, `:alert`)
- `label` - accessible name announced by screen readers
- `description` - longer description (maps to accesskit description)
- `live` - live region semantics: `:polite` or `:assertive`
- `hidden` - if true, node is excluded from the accessibility tree
- `expanded` - expanded/collapsed state for disclosure widgets
- `required` - marks a form field as required
- `level` - heading level (1-6)
- `busy` - suppresses AT announcements until cleared (auto-managed
  by sliders during drag; set explicitly for custom continuous
  interactions)
- `invalid` - form validation failure
- `modal` - dialog is modal
- `read_only` - can be read but not edited
- `mnemonic` - Alt+letter keyboard shortcut (single character)
- `toggled` - toggled/checked state (for custom toggle widgets)
- `selected` - selected state (for custom selectable widgets)
- `value` - current value as a string (for custom value-displaying widgets)
- `orientation` - `:horizontal` or `:vertical` (for custom oriented widgets)
- `labelled_by` - ID of the widget that labels this one
- `described_by` - ID of the widget that describes this one
- `error_message` - ID of the widget showing the error message for this one
- `disabled` - override disabled state for AT
- `position_in_set` - 1-based position within a set (lists, radio groups, tabs)
- `size_of_set` - total items in the set
- `active_descendant` - ID of the currently active child in a composite widget
- `radio_group` - list of IDs of all radio buttons in a group
- `label_from` - (build-time only) prop name to derive label from
- `has_popup` - popup type: `"listbox"`, `"menu"`, `"dialog"`, `"tree"`, `"grid"`

# `has_popup`

```elixir
@type has_popup() :: String.t() | nil
```

# `live`

```elixir
@type live() :: :polite | :assertive
```

# `orientation`

```elixir
@type orientation() :: :horizontal | :vertical
```

# `role`

```elixir
@type role() ::
  :window
  | :tree_item
  | :tree
  | :tooltip
  | :toolbar
  | :text_input
  | :column_header
  | :table_cell
  | :table_row
  | :table
  | :tab_panel
  | :tab_list
  | :tab
  | :switch
  | :status
  | :static_text
  | :slider
  | :separator
  | :search
  | :scroll_view
  | :scroll_bar
  | :region
  | :radio_group
  | :radio_button
  | :progress_indicator
  | :navigation
  | :multiline_text_input
  | :meter
  | :menu_item
  | :menu_bar
  | :menu
  | :list_item
  | :list
  | :link
  | :label
  | :image
  | :heading
  | :group
  | :generic_container
  | :document
  | :dialog
  | :combo_box
  | :check_box
  | :canvas
  | :button
  | :alert_dialog
  | :alert
```

# `role_input`

```elixir
@type role_input() ::
  role()
  | :text_editor
  | :row
  | :radio
  | :progress_bar
  | :generic
  | :container
  | :checkbox
  | :cell
```

# `t`

```elixir
@type t() :: %Plushie.Type.A11y{
  active_descendant: String.t() | nil,
  busy: boolean() | nil,
  described_by: String.t() | nil,
  description: String.t() | nil,
  disabled: boolean() | nil,
  error_message: String.t() | nil,
  expanded: boolean() | nil,
  has_popup: has_popup(),
  hidden: boolean() | nil,
  invalid: boolean() | nil,
  label: String.t() | nil,
  label_from: atom() | nil,
  labelled_by: String.t() | nil,
  level: pos_integer() | nil,
  live: live() | nil,
  mnemonic: String.t() | nil,
  modal: boolean() | nil,
  orientation: orientation() | nil,
  position_in_set: non_neg_integer() | nil,
  radio_group: [String.t()] | nil,
  read_only: boolean() | nil,
  required: boolean() | nil,
  role: role() | nil,
  selected: boolean() | nil,
  size_of_set: non_neg_integer() | nil,
  toggled: boolean() | nil,
  value: String.t() | nil
}
```

# `busy`

```elixir
@spec busy(a11y :: t(), busy :: boolean()) :: t()
```

Suppresses AT announcements until cleared.

Sliders set this automatically during drag. For custom continuous
interactions, set explicitly to prevent rapid-fire announcements
on live regions.

# `cast`

```elixir
@spec cast(a11y :: t() | map() | keyword()) :: {:ok, t()} | :error
```

Normalizes a struct, map, or keyword list into an `A11y` struct.

Accepts an `A11y` struct, a bare map with atom keys, or a keyword list.
Unknown keys are silently ignored. Role aliases like `:checkbox` and
`:radio` are normalized to their canonical forms.

## Examples

    iex> Plushie.Type.A11y.cast(%{role: :heading, level: 1})
    {:ok, %Plushie.Type.A11y{role: :heading, level: 1}}

    iex> Plushie.Type.A11y.cast(role: :heading, level: 1)
    {:ok, %Plushie.Type.A11y{role: :heading, level: 1}}

    iex> a11y = %Plushie.Type.A11y{label: "Close"}
    iex> Plushie.Type.A11y.cast(a11y)
    {:ok, %Plushie.Type.A11y{label: "Close"}}

# `described_by`

```elixir
@spec described_by(a11y :: t(), id :: String.t()) :: t()
```

Sets the ID of the widget that describes this one.

# `description`

```elixir
@spec description(a11y :: t(), description :: String.t()) :: t()
```

Sets the longer accessible description.

# `disabled`

```elixir
@spec disabled(a11y :: t(), disabled :: boolean()) :: t()
```

Overrides the disabled state for assistive technology.

# `error_message`

```elixir
@spec error_message(a11y :: t(), id :: String.t() | nil) :: t()
```

Sets the ID of the widget showing the error message.

# `expanded`

```elixir
@spec expanded(a11y :: t(), expanded :: boolean()) :: t()
```

Sets the expanded/collapsed state.

# `from_opts`

```elixir
@spec from_opts(opts :: keyword()) :: t()
```

Constructs an `A11y` struct from a keyword list.

# `has_popup`

```elixir
@spec has_popup(a11y :: t(), popup :: String.t()) :: t()
```

Sets the popup type (`"listbox"`, `"menu"`, `"dialog"`, `"tree"`, `"grid"`).

# `hidden`

```elixir
@spec hidden(a11y :: t(), hidden :: boolean()) :: t()
```

Sets whether the node is hidden from the accessibility tree.

# `invalid`

```elixir
@spec invalid(a11y :: t(), invalid :: boolean()) :: t()
```

Sets the form validation failure state.

# `label`

```elixir
@spec label(a11y :: t(), label :: String.t()) :: t()
```

Sets the accessible label (name announced by screen readers).

# `labelled_by`

```elixir
@spec labelled_by(a11y :: t(), id :: String.t()) :: t()
```

Sets the ID of the widget that labels this one.

# `level`

```elixir
@spec level(a11y :: t(), level :: pos_integer()) :: t()
```

Sets the heading level (1-6).

# `live`

```elixir
@spec live(a11y :: t(), live :: live()) :: t()
```

Sets the live region semantics (`:polite` or `:assertive`).

# `merge`

Merges widget-default a11y with user-provided overrides.

Non-nil fields in the override replace the corresponding default.
Nil fields in the override keep the default value. This allows
widgets to declare default roles and labels while letting users
override specific fields.

# `mnemonic`

```elixir
@spec mnemonic(a11y :: t(), mnemonic :: String.t()) :: t()
```

Sets the Alt+letter keyboard shortcut (single character).

# `modal`

```elixir
@spec modal(a11y :: t(), modal :: boolean()) :: t()
```

Sets whether a dialog is modal.

# `new`

```elixir
@spec new() :: t()
```

Creates an empty A11y struct with all fields nil.

# `orientation`

```elixir
@spec orientation(a11y :: t(), orientation :: orientation()) :: t()
```

Sets the orientation (`:horizontal` or `:vertical`).

# `position_in_set`

```elixir
@spec position_in_set(a11y :: t(), pos :: non_neg_integer()) :: t()
```

Sets the 1-based position within a set.

# `read_only`

```elixir
@spec read_only(a11y :: t(), read_only :: boolean()) :: t()
```

Sets the read-only state.

# `required`

```elixir
@spec required(a11y :: t(), required :: boolean()) :: t()
```

Marks a form field as required.

# `resolve`

Resolves derived a11y values from the widget's props.

If `label_from` is set and `label` is nil, copies the value of the
named prop as the accessible label. The `label_from` field is then
cleared (it's a build-time directive, not a wire prop).

# `role`

```elixir
@spec role(a11y :: t(), role :: role_input()) :: t()
```

Sets the accessibility role.

Supported aliases normalize to canonical roles:
`:checkbox` -> `:check_box`, `:radio` -> `:radio_button`,
`:text_editor` -> `:multiline_text_input`, `:progress_bar` -> `:progress_indicator`,
`:generic` / `:container` -> `:generic_container`, `:row` -> `:table_row`,
and `:cell` -> `:table_cell`.

# `selected`

```elixir
@spec selected(a11y :: t(), selected :: boolean()) :: t()
```

Sets the selected state.

# `size_of_set`

```elixir
@spec size_of_set(a11y :: t(), size :: non_neg_integer()) :: t()
```

Sets the total number of items in the set.

# `toggled`

```elixir
@spec toggled(a11y :: t(), toggled :: boolean()) :: t()
```

Sets the toggled/checked state.

# `value`

```elixir
@spec value(a11y :: t(), value :: String.t()) :: t()
```

Sets the current value as a string.

---

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