> ## Oymyakon DS index
> Fetch the full index at: https://super-dollop-pzmo65r.pages.github.io/llms.txt
> HTML preview: https://super-dollop-pzmo65r.pages.github.io/chips-area.html
> Source: specs/components/chips-area/chips-area.md, specs/components/chips-area/chips-area-a11y.md, specs/components/chips-area/capabilities.json

---

# ChipsArea · Oymyakon DS 3

> A row of chips that belong together — filters, options to pick, or suggestions to insert — laid out
> as one line that scrolls sideways or as lines that wrap.

**Version:** 3.0.1 · **Status:** Ready · **Figma:** `[ChipsArea] 3.0` (main components file `7vdl5YkZFDWvh9QvSmydsH`, page `10532:6003`; the component `9992:13780` (key `cf4ef6b1…`) with one INSTANCE_SWAP property `Layout`; the eight layout sets in the "Building blocks" section `9996:16574` — `ChipsAreaFilterHorizontal` `10008:19829` / `ChipsAreaFilterVertical` `10008:21270`, `ChipsAreaMultiChoiceHorizontal` `10008:23365` / `ChipsAreaMultiChoiceVertical` `10008:23513`, `ChipsAreaSingleChoiceHorizontal` `22962:18991` / `ChipsAreaSingleChoiceVertical` `22962:19020`, `ChipsAreaSuggestHorizontal` `10008:22048` / `ChipsAreaSuggestVertical` `10008:22405`; Specification frames `10532:11742` (ChipsArea), `10568:16605` (Filter), `10568:21888` (Choice), `10568:24075` (Suggest); "Chips Structure and Skeleton Behavior" `24135:15672`)

---

## 1. Description

ChipsArea is a container for a group of chips (Figma: "a component for creating a group of ChipsItem,
which is used for creating filters, selectors, and suggestions"). It has no fill and no content of its
own: it lays the chips out, gives them one size, and makes them one group.

What sits inside is the area's preset — the eight Figma layout sets are four presets in two layouts:

- **Filter** (Figma `ChipsAreaFilter`) — [ChipsChoice](https://super-dollop-pzmo65r.pages.github.io/chips-choice.md) chips with the
  control off: each turns on and off on its own and filters the content on the screen ("Content
  filter. Used for quickly filtering content on the page").
- **Multi Choice** (Figma `ChipsAreaMultiChoice`) — ChipsChoice Multi Choice chips: several options
  on at once, each a checkbox — services added to an order.
- **Single Choice** (Figma `ChipsAreaSingleChoice`) — ChipsChoice Single Choice chips: one option on,
  the area is the radio group — a pickup time, a type of cargo.
- **Suggest** (Figma `ChipsAreaSuggest`) — [ChipsAction](https://super-dollop-pzmo65r.pages.github.io/chips-action.md) chips:
  ready-made values a tap puts into a field, or a quick reply ("Offers the user ready-made value
  options for filling in a text field").

Two layouts:

- **Scroll** (Figma "Horizontal", the Specification frame's "Horizontal Scroll") — the chips stand in
  one line that scrolls sideways. For filters and wherever the screen's height is short.
- **Flow** (Figma "Vertical", the frame's "Flow (Wrap in Figma)") — the chips wrap onto new lines. For
  a few chips that are the main, or one of the key, actions on the screen.

The chips keep their own anatomy, states and behaviour; the area sets their size and their group.

### Platform implementations

| Platform | Entry point | Version | Notes |
|---|---|---|---|
| iOS | `DsChipsArea` (DS3) | Figma 3.0 | `init(items: [DsChipsItem])` — each item `.choice(DsChipsChoice(…))` or `.action(DsChipsAction(…))`; `.axis(DsChipsAreaAxis.horizontal())` (a ScrollView that scrolls the tapped chip into view) / `.vertical()` (a wrapping stack, `DsChipsAreaAlignment` `.left` by default); `size(_:)` is forced onto every chip; `isLoading` draws five placeholder chips; a Single Choice group's bookkeeping is the caller's (manifest bindings) |
| Android | `DsChipArea` (`core.compose.ds.v3.chip`) | Figma 3.0 | `DsChipArea.Layout.SingleLine` / `.MultiLine`; `isSelectionSingle`, `isSelectionRequired`; the chips are `DsChip.Choice(type = Filter / MultiSelect / SingleSelect)` passed into the `content` lambda; the size lives on the chips (`DsChipArea.Size` has one value, M, the s4 spacing) (manifest bindings) |
| Flutter | — | — | Not shipped |
| Web | `src/shared/shared.css` (`.chips-area`) | 3.0.0 | Reference implementation, this repo; preview page `src/chips-area.njk` |

Neither platform ships Suggest as its own type: iOS fills a suggest area with action chips; the
Android bridge reads it as Filter chips or an action chip — flagged there for a human.

---

## 2. Anatomy

```
ChipsArea (.chips-area)                       fill × hug · padding s8 top / bottom, s16 left / right
└── Layout (.chips-area__layout)              the chips, s4 apart · one line (Scroll) or wrapping (Flow)
    └── Chips                                 the preset's chips — ChipsChoice or ChipsAction instances
```

| Part | Required | Description |
|---|---|---|
| **Container** | Required | Figma `[ChipsArea] 3.0`: fills its parent's width and hugs its height; padding `s16` left and right, `s8` top and bottom; no fill; Figma clips it at its own edges. On the web the Scroll root clips — the chips run under the side padding to the edge; a Flow row wraps and has nothing to clip |
| **Layout** | Required | Figma's `Layout` instance — one of the eight sets: the chips in a row, `s4` apart. Scroll keeps one line; Flow wraps with `s4` between the lines |
| **Chips** | Required | The preset's chips, each a full [ChipsChoice](https://super-dollop-pzmo65r.pages.github.io/chips-choice.md) or [ChipsAction](https://super-dollop-pzmo65r.pages.github.io/chips-action.md) in its own state. Figma stages six (ten in Suggest); there is no fixed count |

- The chips in one area are one size and one kind — the preset's.
- The area adds no fill, border, label or divider. The group's name is the screen's own label next to
  the area ("Options", "Pickup time") — not part of the component.

### RTL layout

`RTL = on` mirrors the area: the first chip stands at the right edge, Scroll starts at the right and
scrolls to the left, Flow lines start at the right. Each chip mirrors as its own spec says. On the web
the root's direction (`dir="rtl"` on it or an ancestor) mirrors the area.

---

## 3. Variants and sizes

The Figma component swaps one of eight layout sets (preset × layout); each set has Size × RTL. The web
folds them into axes on one root.

| Axis | Values | Default | Web mechanism |
|---|---|---|---|
| **Preset** | `filter` · `multi-choice` · `single-choice` · `suggest` | `filter` (Figma's default swap, `ChipsAreaFilterHorizontal`) | The chips inside — ChipsChoice with the control off (`role="checkbox"`), ChipsChoice Multi Choice (`role="checkbox"`), ChipsChoice Single Choice (`role="radio"`), ChipsAction (`<button>`) — and the root's role: `group`, or `radiogroup` for Single Choice. `data-ds-preset` |
| **Layout** | `scroll` · `flow` | `scroll` (Figma "Horizontal") | `.chips-area--scroll` / `.chips-area--flow`. `data-ds-variant` |
| **Size** | `m` · `s` | `m` | Every chip takes the size — the bare chip (M) or its `--s` modifier (S). The area itself has no size class |
| **Alignment** | `start` · `center` · `end` | `start` | Flow only. The bare root (start), `.chips-area--center`, `.chips-area--end` |
| **State** | `standard` · `disabled` · `skeleton` | `standard` | See § 4. `data-ds-state` |
| **RTL** | `off` · `on` | `off` | `dir="rtl"` on the root or an ancestor |

- **Alignment** is the Specification frame's "Left-aligned — default value · Centered — recommended for
  Flow layout when you need to visually highlight a group of chips · Right-aligned — used for specific
  interface scenarios", on the logical sides (start is the left in LTR and the right in RTL). Figma has
  no property for it — it is the Layout instance's auto-layout alignment; iOS ships it on the vertical
  axis (`DsChipsAreaAlignment`). A Scroll row starts at the start edge.
- **Size** is the chips' (S and M — "ChipsArea supports all the ChipsItem sizes"): a row of M chips is
  `s48` high, a row of S chips `s40`.

---

## 4. States

The Specification frame: "States such as Disabled, Skeleton the same for all presets"; per chip, "See
ChipsItem specs for states and styles".

| State | What shows |
|---|---|
| **Standard** | The chips, each in its own state — Standard or Active (ChipsChoice), Standard (ChipsAction); a chip may be disabled on its own |
| **Disabled** | Every chip Disabled — the chips' own Disabled: the Standard fill and border, the content `TextAndIcon/Disabled`; a chosen ChipsChoice keeps its check or dot. A Filter chip has no glyph, so a disabled Filter area shows no choice at all — the chosen chips take the Standard fill too; the choice is still announced (flagged, § 6). The area keeps its layout and its scroll |
| **Skeleton** | Five placeholder chips — empty pills at the chips' minimum width `s64`, the size's height, in `Skeleton/OnWhite` with the DS shimmer — laid out by the area's layout: one line in Scroll, wrapping in Flow when the area is narrow |

- The Figma names: the area has no State property — Disabled and Skeleton are the chips' states, and the
  frame's skeleton block shows the area's placeholder row. iOS draws it with `isLoading`.
- A Skeleton area keeps the size and the layout it is given, so the content that replaces it lands in
  the same place.

---

## 5. Animation and behavior

Reference: [`motion-rules.md`](https://super-dollop-pzmo65r.pages.github.io/motion.md). The area has no motion of its
own; the chips move as their specs say.

| Event | Pattern | Token | Notes |
|---|---|---|---|
| A chip's press, hover, choice | the chip's | — | `pushButton`, `Transforming/State/Default` — the chips' own |
| Skeleton shimmer | `Patterns/Shimmer` | `var(--pattern-shimmer)` | Each placeholder's sweep, the chips' own; reversed in RTL |
| Skeleton → content | — | — | The host swaps the placeholders for the chips; no transition of its own |
| Scroll after a choice (Scroll) | — | — | The platform's own scrolling — no DS motion token; at once under reduced motion |

- **Scroll** — the row scrolls sideways under a swipe; no scrollbar is drawn — a chip cut by the edge
  shows the row goes on. The chips run under the side padding to the area's edge; at either end of the
  scroll the first or last chip rests `s16` from the edge.
- **Scroll after a choice** (the Specification frame's "behavior" block): when a chip is activated the
  row scrolls toward the screen edge nearest to it — the last or the second-to-last chip scrolls the
  row to its end, the first or the second to its start; any other chip is scrolled fully into view
  (iOS: "scrolls the tapped chip into view").
- **Flow** — no scroll; the chips wrap, the lines aligned by the Alignment.
- **Selection** belongs to the preset: Filter and Multi Choice toggle each chip; Single Choice turns the
  tapped chip on and the one that was on off; a Suggest chip runs its action (it puts its value into the
  field) and has no choice of its own.
- A disabled area and a Skeleton do not react.
- Reduced motion: the chips' tokens are zeroed at the token; the scroll after a choice jumps at once.

---

## 6. Color tokens

The area has no colour of its own — no fill, no border, no text. Every colour is a chip's.

| Element | Token | Notes |
|---|---|---|
| Background | — | None — the screen shows through between the chips |
| The chips | the chips' | [ChipsChoice § 6](https://super-dollop-pzmo65r.pages.github.io/chips-choice.md#6-color-tokens), [ChipsAction § 6](https://super-dollop-pzmo65r.pages.github.io/chips-action.md#6-color-tokens) |
| Skeleton fill | `var(--skeleton-on-white)` | The placeholders — the chips' own Skeleton |
| Skeleton wave | `var(--skeleton-wave)` | The shimmer's highlight |

- A Filter chip shows its choice by the fill and the `Border/Active` border alone — it has no glyph
  (ChipsChoice with the control off). On focus the border turns `Border/Inform`, so the fill (≈ 1.1:1
  against the Standard one) is the only cue left; a disabled chip takes the Standard fill and border
  whatever its choice, so a disabled Filter area shows no choice at all — only the announcement keeps
  it. Accepted (owner, 2026-11-12), as on ChipsChoice.

---

## 7. Typography

The area sets no text. The chips' Title (`Body/Main Body`) and Subtitle (`Caption/Caption`) are theirs.

---

## 8. Spacing

| Property | Token | @100% | Notes |
|---|---|---|---|
| Width | — | fill | The parent's width |
| Height | — | hug | One line of chips plus the padding — 64 (M) / 56 (S) in Scroll |
| Padding — left / right | `var(--sp-s16)` | 16 | The Specification frame calls it "vertical padding" — the DS names a padding by the measurement's direction |
| Padding — top / bottom | `var(--sp-s8)` | 8 | The frame's "horizontal padding" |
| Gap — between chips | `var(--sp-s4)` | 4 | |
| Gap — between lines (Flow) | `var(--sp-s4)` | 4 | Two lines of M chips: 8 + 48 + 4 + 48 + 8 = 116 |
| Placeholder width (Skeleton) | `var(--sp-s64)` | 64 | The chips' minimum width — "For sizes S and M, the width of the elements corresponds to the minimum width of the chips" |

- The frame notes "All spacings (S) could be changed depending on design needs" — the padding and the
  gaps are defaults an instance may re-point to another `--sp-*` step.
- Figma flags: the two Vertical sets `ChipsAreaFilterVertical` and `ChipsAreaSuggestVertical` set their
  line gap as a raw 4, not bound to `SP/100% ratio/s4` (the Multi and Single Choice Vertical sets bind
  it).

---

## 9. Usage context

### When to use ChipsArea

- Filters above a list or a map that narrow what it shows — Filter, Scroll ("Activity: All, City, City
  to City, Delivery, Cargo").
- Options that can be added together — services for an order — Multi Choice, Flow ("Options: Kaspi,
  One Mover, Two Movers").
- One value out of a short set shown in full — a pickup time, a type of cargo — Single Choice.
- Ready-made values under a text field, or quick replies in a chat — Suggest, Scroll ("I am here",
  "Where are you?").

### When not to use

- For a single chip — a [ChipsChoice](https://super-dollop-pzmo65r.pages.github.io/chips-choice.md) or a
  [ChipsAction](https://super-dollop-pzmo65r.pages.github.io/chips-action.md) on its own.
- For a long list of options, or options that need a description — a list of
  [Cells](https://super-dollop-pzmo65r.pages.github.io/cell.md) with a [Checkbox](https://super-dollop-pzmo65r.pages.github.io/checkbox.md) or a
  [RadioButton](https://super-dollop-pzmo65r.pages.github.io/radiobutton.md).
- To switch between views of the same content — a [SegmentedControl](https://super-dollop-pzmo65r.pages.github.io/segmented-control.md).
- To mix kinds in one group — on the web one area holds one preset. The Figma Choice frame also shows a
  choice row that ends in an action chip ("Pickup time: 10–20 min · Up to 1 hour · Schedule delivery",
  the chip opening a sheet), and iOS takes mixed items (`.choice` / `.action`); the web does not build
  it — a radio group cannot own a button. The action chip stands after the area, outside the group —
  the contract on every platform (owner, 2026-11-12): it may continue the row visually, but it is never a
  member of the choice group, and on iOS the mixed items keep it out of the group for assistive
  technology.
- Flow for many chips — it grows tall; use Scroll.

### Related components

- [ChipsChoice](https://super-dollop-pzmo65r.pages.github.io/chips-choice.md) — the chips of the Filter, Multi Choice and Single Choice presets.
- [ChipsAction](https://super-dollop-pzmo65r.pages.github.io/chips-action.md) — the chips of the Suggest preset.
- [SegmentedControl](https://super-dollop-pzmo65r.pages.github.io/segmented-control.md) — switching views, not filtering.
- [Skeleton](https://super-dollop-pzmo65r.pages.github.io/skeleton.md) — the DS loading pattern the placeholders follow.

---

## 10. Accessibility

Brief summary. Full spec: [`chips-area-a11y.md`](https://super-dollop-pzmo65r.pages.github.io/chips-area.md).

- **The area is the group; the chips are the controls.** Each chip keeps its own contract — a
  checkbox, a radio or a button; the area names the group and holds the selection model.
- **Name** — the screen's label next to the area ("Options", "Pickup time"), pointed at from the group.
- **Single Choice** — the area is the radio group: the position in the set is announced on TalkBack and
  the web (VoiceOver announces none); on the web one Tab stop for the group and the arrow keys move the
  choice.
- **Scroll** — every chip stays reachable: a screen reader's swipe, or the keyboard (Tab; the arrow keys
  in Single Choice), brings an off-screen chip into view (`s16` clear of the edge on the web).
- **Disabled** — every chip announces "Disabled" (TalkBack) / "Dimmed" (VoiceOver).
- **Skeleton** — not announced, not focusable; the loading region around the area announces once.
- **Figma's own preview differs**: it voices a Single Choice group on VoiceOver as one adjustable element
  ("Pizza. Adjustable. Swipe up or down…"); the DS keeps the ChipsChoice contract — each chip a button
  with the selected trait — the DS contract stands (owner, 2026-11-12); the preview is to be updated.

---

## 11. Analytics and coverage contract

Contract per [`prototype-analytics-and-coverage.md`](https://github.com/inDriver/oymyakon-ds/blob/main/docs/prototype-analytics-and-coverage.md).

| Field | Value |
|---|---|
| `data-ds-component` | `chips-area` |
| Coverage unit | yes — the area is one DS instance; each chip inside is its own instance and its own unit |
| Tap target model | none of its own — the chips are the targets and emit their own taps |
| Actions | none — the chips' `toggle` / `select` / `tap` |
| Internal targets | none |
| Emits value | no — each ChipsChoice carries its own choice in `data-ds-state` |

Attribute model: `data-ds-preset` = the preset (`filter`, `multi-choice`, `single-choice`, `suggest`);
`data-ds-variant` = the layout (`scroll`, `flow`); `data-ds-state` = `standard`, `disabled` or
`skeleton`. The size is the chips' `data-ds-preset`; RTL is the reading direction, not a state.

```html
<!-- Filter, Scroll — ChipsChoice chips with the control off; the row is a group named by the screen's label -->
<p id="activity">Activity</p>   <!-- the screen's own label; not part of the component -->
<div class="chips-area chips-area--scroll" role="group" aria-labelledby="activity"
     data-ds-component="chips-area" data-ds-component-id="activity.filter"
     data-ds-preset="filter" data-ds-variant="scroll" data-ds-state="standard">
  <div class="chips-area__layout">
    <button type="button" class="chips-choice" role="checkbox" aria-checked="true"
            data-ds-component="chips-choice" data-ds-component-id="activity.filter.all"
            data-ds-preset="m" data-ds-variant="multi" data-ds-state="active" data-ds-action="toggle">
      <span class="chips-choice__content">
        <span class="chips-choice__text"><span class="chips-choice__title">All</span></span>
      </span>
    </button>
    …
  </div>
</div>

<!-- Single Choice, Flow, centred — the area is the radio group; one Tab stop -->
<p id="pickup-time">Pickup time</p>
<div class="chips-area chips-area--flow chips-area--center" role="radiogroup" aria-labelledby="pickup-time"
     data-ds-component="chips-area" data-ds-component-id="delivery.pickup"
     data-ds-preset="single-choice" data-ds-variant="flow" data-ds-state="standard">
  <div class="chips-area__layout">
    <button type="button" class="chips-choice chips-choice--s" role="radio" aria-checked="true" tabindex="0" …>…</button>
    <button type="button" class="chips-choice chips-choice--s" role="radio" aria-checked="false" tabindex="-1" …>…</button>
  </div>
</div>

<!-- Skeleton — five placeholders at the chips' minimum width; the loading region announces -->
<div class="chips-area chips-area--scroll chips-area--skeleton" aria-hidden="true" inert
     data-ds-component="chips-area" data-ds-component-id="activity.filter"
     data-ds-preset="filter" data-ds-variant="scroll" data-ds-state="skeleton">
  <div class="chips-area__layout">
    <button type="button" class="chips-choice chips-choice--skeleton" aria-hidden="true" inert tabindex="-1"
            data-ds-component="chips-choice" data-ds-component-id="activity.filter.skeleton-1"
            data-ds-preset="m" data-ds-variant="multi" data-ds-state="skeleton"></button>
    …
  </div>
</div>
```

The root takes the group role — `group` for Filter, Multi Choice and Suggest, `radiogroup` for Single
Choice — and its name from the screen's label through `aria-labelledby`. Every chip takes the area's
size and kind. A Disabled area is every chip with `aria-disabled="true"` — focusable, readable,
refusing the activation (owner, 2026-11-12) — and `data-ds-state="disabled"` on the root. A Skeleton area is `.chips-area--skeleton` with
`aria-hidden="true"` and `inert` and five Skeleton chips of the preset's kind (ChipsAction for Suggest), which the
area narrows to `s64`.

---

## Changelog

| Version | Date | Change |
|---|---|---|
| 3.0.1 | 2026-11-12 | Owner rulings: a disabled area marks its chips `aria-disabled="true"`, not `disabled` — focusable and readable, refusing the activation, so a disabled Scroll row keeps its clipped chips in the keyboard's reach in every browser (Safari had nothing to focus); a fully disabled Single Choice area stays one Tab stop. A choice row that ends in an action chip: the action stands after the area, outside the group, on every platform. One element per chip on VoiceOver stands over the adjustable group. The Filter cue gap and the S target accepted as on the chips. Status Draft → Ready (owner). |
| 3.0.0 | 2026-11-02 | Initial spec, from the Figma `[ChipsArea] 3.0` page (page `10532:6003`): the component `9992:13780` and its `Layout` swap, the eight layout sets (Filter / Multi Choice / Single Choice / Suggest × Horizontal / Vertical, each Size M / S × RTL), the four Specification frames and the "Chips Structure and Skeleton Behavior" frame. The eight sets become two axes on one root — Preset (`filter`, `multi-choice`, `single-choice`, `suggest`) × Layout (`scroll` = Figma Horizontal, `flow` = Figma Vertical) — plus Size (forced onto every chip), Alignment (start / center / end, Flow only — from the frame and iOS; Figma has no property), State (Standard / Disabled / Skeleton — five `s64` placeholders) and RTL. Container: padding `s16` left / right, `s8` top / bottom, no fill, clips; chips `s4` apart, `s4` between Flow lines. Scroll: no scrollbar, the chips run under the side padding, the row scrolls toward the near edge after a choice (the frame's behavior block). Composes the ChipsChoice (3.0.1) and ChipsAction (3.1.2) web builds. |

---

# ChipsArea — Accessibility

ChipsArea **is the group**; the chips inside are the controls. Each chip keeps its own contract — a
[ChipsChoice](https://super-dollop-pzmo65r.pages.github.io/chips-choice.md) is a checkbox or a radio, a
[ChipsAction](https://super-dollop-pzmo65r.pages.github.io/chips-action.md) is a button — and the area adds three things:

- **The group's name** — the screen's label next to the area ("Activity", "Options", "Pickup time"),
  read when the focus enters the group. The area has no visible text of its own.
- **The selection model** — Single Choice makes the area one radio group: one option on, the position
  in the set announced (TalkBack and the web; VoiceOver announces none). Filter and Multi Choice are a group of independent checkboxes; Suggest a group
  of buttons.
- **Reach in Scroll** — a chip out of view is still in the order: moving the focus onto it scrolls it
  into view.

| Visible | Announced (TalkBack) |
|---|---|
| Activity: All (Filter, Active — the fill and border, no glyph) | "All, Checked, Checkbox, Double-tap to toggle" — the chip's |
| Options: Two Movers ☐ (Multi Choice) | "Two Movers, Not checked, Checkbox, Double-tap to toggle" |
| Pickup time: 10–20 min ◉ (Single Choice, 1 of 2) | "10–20 min, Checked, 1 of 2, Radio button" |
| Hello (Suggest) | "Hello, Button, Double-tap to activate" — the ChipsAction's |

**Figma's own preview differs.** The Specification frame voices a Multi and a Single Choice chip as a
button — "Apple, Not selected, Button, Double-tap to select" / "Not selected, Apple, Button…" on
TalkBack — and a Single Choice group on VoiceOver as ONE adjustable element ("Pizza. Adjustable. Swipe
up or down with one finger to adjust the value"). The DS follows its ChipsChoice contract — a chip
that works like a checkbox or a radio is announced as one, and on iOS each chip is a button with the
selected trait. The adjustable group is a real alternative for a long Single Choice row (one swipe
stop instead of many) — considered and not taken: one element per chip stands (owner, 2026-11-12), so
every option stays visible to the reader as on the standalone chips.

---

## Android · TalkBack

| Element | Label | Value | Trait | Hint |
|---|---|---|---|---|
| Area — Single Choice | The screen's label for the row | — | — (`selectableGroup()`; the chips' `selectable(role = RadioButton)` give "[n] of [m]") | — |
| Area — Filter, Multi Choice, Suggest | The screen's label for the row | — | — (a container; the label is a heading or `paneTitle` next to it) | — |
| The chips | Their own — [ChipsChoice](https://super-dollop-pzmo65r.pages.github.io/chips-choice.md), [ChipsAction](https://super-dollop-pzmo65r.pages.github.io/chips-action.md) | | | |

Voiced previews:

- "Pickup time." then "10–20 min, Checked, 1 of 2, Radio button."
- "Options." then "Kaspi, Not checked, Checkbox, Double-tap to toggle."

### Edge states

- **Scroll:** swipe navigation walks the chips in order and scrolls an off-screen chip into view; a
  two-finger swipe scrolls the row. The row is not a node of its own.
- **After a tap:** the chip's new value is read ("Checked"); the row's scroll toward the near edge is
  not announced.
- **Disabled area:** every chip reads its value and "Disabled", without a hint; they stay in swipe
  navigation.
- **Skeleton:** not announced, not focusable — `clearAndSetSemantics {}` on the placeholders; the
  loading container announces once, politely.
- **RTL:** the row mirrors and scrolls from the right; swipe navigation follows the reading order.
- **Text scaling:** the chips scale to 150% (their Titles and Subtitles); Scroll rows grow taller, Flow
  rows wrap sooner.

---

## iOS · VoiceOver

| Element | Label | Value | Trait | Hint |
|---|---|---|---|---|
| Area | The screen's label for the row | — | Container — `accessibilityElement(children: .contain)` with the label | — |
| The chips | Their own — buttons with the selected trait when chosen | | | |

Voiced previews:

- "Two Movers, Selected, Button. Double tap to toggle setting."
- "Up to 1 hour, Button. Double tap to select."

VoiceOver announces no position in the set for a group of buttons — the "1 of 2" is TalkBack's and the
web's.

### Edge states

- **Scroll:** moving the VoiceOver cursor onto a chip scrolls the ScrollView to it; a three-finger swipe
  scrolls the row by a page.
- **After a tap:** the selected trait changes on the focused chip and VoiceOver reads it ("Selected");
  the row's scroll toward the near edge is not announced.
- **Disabled area:** every chip reads "Dimmed"; the hints are cleared.
- **Skeleton:** `accessibilityHidden(true)` on the placeholders (`isLoading` alone does not hide them);
  the loading container announces once.
- **RTL / Dynamic Type:** as on Android.

---

## Web preview (reference)

- **Root role and name:** the `.chips-area` root is `role="group"` (Filter, Multi Choice, Suggest) or
  `role="radiogroup"` (Single Choice), with `aria-labelledby` pointing at the screen's label. Without the
  group a screen reader hears loose checkboxes and has no set to announce.
- **The chips:** each per its own spec — Filter and Multi Choice `role="checkbox"`, Single Choice
  `role="radio"`, Suggest a `<button>`; all `type="button"`. Every chip in the area takes the area's
  size.
- **Keyboard — Filter, Multi Choice, Suggest:** Tab reaches each chip in order; Space toggles a
  checkbox chip; Enter does nothing on a checkbox chip (the Checkbox trap — the handler swallows the
  click Enter synthesizes) and activates a Suggest chip.
- **Keyboard — Single Choice:** the area is one Tab stop — roving `tabindex`, `0` on the checked option
  when it is enabled, else on the first enabled one; the arrow keys move the focus and the choice
  together, wrap, skip disabled options and follow the visual direction in RTL; the full model is
  ChipsChoice's.
- **Scroll:** the root is the scroll container; it holds focusable chips, so it is not a Tab stop of its
  own. When the focus lands on a chip partly out of view the browser scrolls it in, `s16` clear of the
  edge (`scroll-padding-inline`). No scrollbar is drawn: pointer users scroll with a trackpad, Shift +
  wheel, or a drag on touch; keyboard users reach every chip with Tab — with the arrow keys inside a
  Single Choice group.
- **After a choice:** the row scrolls toward the near edge (the last two chips → the end, the first two
  → the start, any other into view) — at once under `prefers-reduced-motion: reduce`. The focus stays on
  the chip. Enter on a choice chip changes nothing, so it does not scroll the row either.
- **Disabled area:** every chip carries `aria-disabled="true"`, not the native `disabled` (owner, 2026-11-12) — it
  stays focusable and readable, announced as disabled with its `aria-checked`, and refuses the
  activation; the root carries `data-ds-state="disabled"`. A Single Choice area stays one Tab stop on
  the checked option (else the first). So a disabled Scroll row keeps its clipped chips in the
  keyboard's reach in every browser — focusing a chip scrolls it into view — as mobile keeps disabled
  chips in swipe navigation. (With the native `disabled`, Safari left such a row nothing to focus.)
- **Skeleton:** the root has `aria-hidden="true"` and `inert` — a scroller with nothing focusable inside
  is a Tab stop of its own in Chrome and Firefox, and a hidden row must not take the focus; the
  placeholders are `inert` too, with no content and no role; the loading region around the area
  announces.
- **Forced colours:** the area draws nothing; each chip draws its own system-colour outline.
- **Target size:** the chips' — M `s48`, S `s40` (under the `s44` floor — an accepted exception, as Button S (owner, 2026-11-12)).
- **Reduced motion:** the chips' motion is zeroed at the token; the scroll after a choice jumps.

---

## Checklist

- [ ] The area is a group named by the screen's label; Single Choice is a radio group
- [ ] Each chip keeps its own role, name and state — the area adds none
- [ ] Single Choice: the position in the set is announced (TalkBack, web); one Tab stop on the web, on an
      enabled option
- [ ] Scroll: every chip is reachable by swipe navigation and by the keyboard (Tab; the arrow keys in
      Single Choice); the focused chip is scrolled into view clear of the edge
- [ ] Disabled area: every chip "Disabled" / "Dimmed", values still read
- [ ] Skeleton is hidden and not focusable (on the web the root is `inert` too); the loading region
      announces once

---

## Machine contract — `specs/components/chips-area/capabilities.json`

Axes, allowed values, defaults, constraints and CSS/Figma bindings. Read this instead of guessing what the component can do.

```json
{
  "$schema": "../component-capabilities.schema.json",
  "id": "chips-area",
  "name": "ChipsArea",
  "version": "3.0.1",
  "description": "The group a row of chips forms: one container (padding s16 left / right, s8 top / bottom, no fill) laying out the preset's chips s4 apart — one line that scrolls sideways (Scroll) or wrapping lines (Flow). Presets Filter (ChipsChoice, control off) · Multi Choice (ChipsChoice checkboxes) · Single Choice (ChipsChoice radios; the area is the radiogroup) · Suggest (ChipsAction). Size forced onto every chip; Alignment in Flow; States Standard · Disabled · Skeleton (five s64 placeholders).",
  "files": {
    "spec": "specs/components/chips-area/chips-area.md",
    "a11y": "specs/components/chips-area/chips-area-a11y.md",
    "preview": "src/chips-area.njk",
    "css": "src/shared/shared.css"
  },
  "figma": {
    "library": "🕹️ Oymyakon (components) — main file",
    "fileKey": "7vdl5YkZFDWvh9QvSmydsH",
    "componentSets": {
      "[ChipsArea] 3.0": {
        "key": "cf4ef6b180c73e4f885289f0c793f7d390f9ecb6",
        "note": "Component 9992:13780 on page [ChipsArea] 3.0 (10532:6003): the container (padding SP s16 left / right, s8 top / bottom, gap s0, clips, no fill) with one INSTANCE_SWAP property Layout#9992:81 — default ChipsAreaFilterHorizontal; preferred values the eight layout sets."
      },
      "ChipsAreaFilterHorizontal": {
        "key": "d08eed2ba432e4f221edefaabce4ef25dd2816a6",
        "note": "Set 10008:19829 — Size (M, S) × RTL; a SLOT of the page-local .M-ChipsChoice (no control). One line, gap s4."
      },
      "ChipsAreaFilterVertical": {
        "key": "e2f18e1e7102b8a160bf6a56afd5327f33688fd3",
        "note": "Set 10008:21270 — wraps, gap s4, line gap a raw 4 (not bound)."
      },
      "ChipsAreaMultiChoiceHorizontal": {
        "key": "dcb0f33c176963c3dc16d453383ea076e80969db",
        "note": "Set 10008:23365 — Size × RTL × Slot (off); checkbox chips."
      },
      "ChipsAreaMultiChoiceVertical": {
        "key": "f9073ec20dc089c576daf6cb9cbbd7cb6672ad51",
        "note": "Set 10008:23513 — wraps; line gap bound to s4."
      },
      "ChipsAreaSingleChoiceHorizontal": {
        "key": "9dd25c29995ae788c82a6dbf49d798f7e99aa42a",
        "note": "Set 22962:18991 — Size × RTL × Slot (off); radio chips."
      },
      "ChipsAreaSingleChoiceVertical": {
        "key": "6b5c129746ae88afe6c2694e365b46df706f03ff",
        "note": "Set 22962:19020 — wraps; line gap bound to s4."
      },
      "ChipsAreaSuggestHorizontal": {
        "key": "fe4960a195a9cfca29dd886973775c0afb1613b2",
        "note": "Set 10008:22048 — Size × RTL; the page-local .M-ChipsButton / .S-ChipsButton (action chips), ten staged."
      },
      "ChipsAreaSuggestVertical": {
        "key": "7a099e0c343874fbce8b620dd83dc166c208cee8",
        "note": "Set 10008:22405 — wraps, line gap a raw 4 (not bound)."
      }
    },
    "capturedAt": "2026-11-02"
  },
  "root": {
    "class": "chips-area",
    "dataDsComponent": "chips-area"
  },
  "anatomy": {
    "layout": {
      "class": "chips-area__layout",
      "notes": "Figma's Layout instance: the chips in a row, gap s4. Scroll keeps one line (flex: none — it is as wide as its chips and the root scrolls it); Flow wraps with s4 between lines. Its children are ChipsChoice / ChipsAction instances, each with its own data-ds-* — the area's parts are only the container and the layout."
    }
  },
  "axes": {
    "preset": {
      "title": "Preset",
      "type": "enum",
      "values": [
        "filter",
        "multi-choice",
        "single-choice",
        "suggest"
      ],
      "default": "filter",
      "css": {
        "mechanism": "No class — the chips inside and the root's role: filter = ChipsChoice Multi with the control off (role=\"checkbox\"), root role=\"group\"; multi-choice = ChipsChoice Multi (checkbox glyph), role=\"group\"; single-choice = ChipsChoice Single (role=\"radio\"), root role=\"radiogroup\" with a roving tab stop; suggest = ChipsAction (<button>), role=\"group\". data-ds-preset."
      },
      "figma": {
        "kind": "component-set",
        "values": {
          "filter": "ChipsAreaFilterHorizontal / ChipsAreaFilterVertical",
          "multi-choice": "ChipsAreaMultiChoiceHorizontal / ChipsAreaMultiChoiceVertical",
          "single-choice": "ChipsAreaSingleChoiceHorizontal / ChipsAreaSingleChoiceVertical",
          "suggest": "ChipsAreaSuggestHorizontal / ChipsAreaSuggestVertical"
        },
        "notes": "Chosen through the [ChipsArea] 3.0 Layout swap (INSTANCE_SWAP), together with the layout."
      },
      "constraints": [
        "One preset per area — every chip in it is the preset's kind.",
        "Suggest chips are ChipsAction buttons with no choice of their own."
      ]
    },
    "layout": {
      "title": "Layout",
      "type": "enum",
      "values": [
        "scroll",
        "flow"
      ],
      "default": "scroll",
      "css": {
        "modifierTemplate": ".chips-area--{value}",
        "mechanism": "scroll = one line, the root scrolls sideways (overflow-x auto, no scrollbar, scroll-padding-inline s16), the chips run under the side padding; flow = the layout wraps, s4 between lines. data-ds-variant."
      },
      "figma": {
        "kind": "component-set",
        "values": {
          "scroll": "…Horizontal (the Specification frame's \"Horizontal Scroll\")",
          "flow": "…Vertical (the frame's \"Flow (Wrap in Figma)\")"
        }
      }
    },
    "size": {
      "title": "Size",
      "type": "enum",
      "values": [
        "m",
        "s"
      ],
      "default": "m",
      "css": {
        "mechanism": "No class on the area — every chip takes the size: the bare chip (M, s48) or its --s modifier (S, s40: .chips-choice--s / .chips-action--s)."
      },
      "figma": {
        "kind": "variant-property",
        "property": "Size",
        "values": {
          "m": "M",
          "s": "S"
        }
      },
      "constraints": [
        "Every chip in an area is the area's size — iOS forces it onto every chip."
      ]
    },
    "alignment": {
      "title": "Alignment",
      "type": "enum",
      "values": [
        "start",
        "center",
        "end"
      ],
      "default": "start",
      "css": {
        "mechanism": "start = the bare root; .chips-area--center; .chips-area--end — justify-content on the layout, on the logical sides (start = left in LTR, right in RTL). Flow only."
      },
      "figma": {
        "kind": "layout",
        "notes": "No property — the Layout instance's auto-layout alignment. The Specification frame: Left (default) · Center (recommended for Flow) · Right. iOS DsChipsAreaAlignment (.left default) on the vertical axis."
      },
      "constraints": [
        "Flow only — a Scroll row starts at the start edge."
      ]
    },
    "state": {
      "title": "State",
      "type": "enum",
      "values": [
        "standard",
        "disabled",
        "skeleton"
      ],
      "default": "standard",
      "css": {
        "mechanism": "standard = the chips in their own states; disabled = every chip with the native disabled attribute (data-ds-state=\"disabled\" on the root); skeleton = .chips-area--skeleton, aria-hidden and inert, five Skeleton chips of the preset's kind narrowed to s64. data-ds-state."
      },
      "figma": {
        "kind": "none",
        "notes": "No State property on the area — Disabled and Skeleton are the chips' states; the Specification frame's skeleton block shows five placeholders at the chips' minimum width. iOS isLoading."
      }
    },
    "rtl": {
      "title": "RTL",
      "type": "boolean",
      "default": false,
      "css": {
        "mechanism": "dir=\"rtl\" on the root or an ancestor: the first chip at the right, Scroll starts at the right, Flow lines start at the right; each chip mirrors as its spec says"
      },
      "figma": {
        "kind": "variant-property",
        "property": "RTL",
        "values": {
          "false": "off",
          "true": "on"
        }
      }
    },
    "padding": {
      "title": "Padding",
      "type": "tokenScale",
      "scale": "sp",
      "default": "--sp-s16",
      "customizable": "any --sp-* step (\"All spacings (S) could be changed depending on design needs\")",
      "css": {
        "mechanism": "padding s8 top / bottom, s16 left / right on the root; an instance re-points them inline"
      },
      "figma": {
        "kind": "layout",
        "notes": "Bound to SP/100% ratio/s16 (left / right) and s8 (top / bottom); the frame calls them the \"vertical\" and \"horizontal\" padding — the DS names a padding by its measurement's direction."
      }
    }
  },
  "states": {
    "default": [
      "standard",
      "disabled",
      "skeleton"
    ]
  },
  "constraints": [
    "The area is the group, the chips are the controls: the root is role=\"group\" (Filter, Multi Choice, Suggest) or role=\"radiogroup\" (Single Choice), named by the screen's label through aria-labelledby; each chip keeps its own role, name and state.",
    "One preset and one size per area — the chips are the preset's kind at the area's size.",
    "No fill, border, text or colour of its own; padding s16 left / right and s8 top / bottom; the chips s4 apart; Flow lines s4 apart.",
    "Scroll: one line; the root scrolls sideways with no scrollbar; the chips run under the side padding to the edge; the focused chip is scrolled in s16 clear of the edge; after a choice the row scrolls toward the near edge (the last two chips → the end, the first two → the start, any other into view), at once under reduced motion.",
    "Flow: the chips wrap; Alignment start / center / end applies only here.",
    "Disabled = every chip disabled (native disabled); never opacity on the area. A disabled Filter area shows no choice — the chips have no glyph and a disabled chip takes the Standard fill; the choice is still announced (flagged for the owner).",
    "Skeleton = five placeholders at the chips' minimum width s64, the area aria-hidden and inert; the loading region around it announces.",
    "No motion of its own — the chips' pushButton / Transforming/State/Default / Patterns/Shimmer; no motion token declared and no reduced-motion override."
  ],
  "analytics": {
    "dataDsComponent": "chips-area",
    "actions": [],
    "notes": "Coverage unit — the area is one instance; every chip inside is its own instance (its own data-ds-*) and emits its own taps. data-ds-preset = filter / multi-choice / single-choice / suggest; data-ds-variant = scroll / flow; data-ds-state = standard / disabled / skeleton. No value of its own — each ChipsChoice carries its choice."
  },
  "rtl": {
    "supported": true,
    "notes": "The area mirrors: the first chip at the right; Scroll starts at the right; Flow lines start at the right (Alignment is logical). Each chip mirrors as its own spec says."
  }
}
```
