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

Accessibility annotation type for widget nodes.

When attached to a widget via the `a11y` prop, these attributes override the
auto-inferred accessibility semantics on the renderer side. The renderer
automatically derives roles and labels from widget types and props (e.g. a
button's label becomes the accessible name), so most widgets need no explicit
`a11y` annotation. Use this for cases where auto-inference is insufficient.

## 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
- `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{
  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,
  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,
  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()) :: t()
```

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})
    %Plushie.Type.A11y{role: :heading, level: 1}

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

    iex> a11y = %Plushie.Type.A11y{label: "Close"}
    iex> Plushie.Type.A11y.cast(a11y)
    %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`).

# `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.

# `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*
