Ta strona jest publikowana po angielsku i ukraińsku; poniżej wersja angielska.
Lists and per-language fields
For developers, in code: repeated rows addressed by position, an empty list that names its fields, and switchers that decide which version of a field is shown.
Two shapes defeat a flat scan of controls: a repeated list (five FAQ chips are fifteen
controls, each a chance to write into the neighbouring row) and a variant switcher (one field
holding an English, a Ukrainian and a Polish version, only one on show). Eight attributes
describe both, and the names are yours — nothing in the widget knows what faq or lang means.
A repeated list
<div data-sa-list="faq" data-sa-fields="enabled,question:lang,delete">
<div data-sa-row>
<input type="checkbox" data-sa-field="enabled" aria-label="Show this chip" />
<input data-sa-field="question" data-sa-variant-of="lang" aria-label="Question" />
<button data-sa-field="delete">Delete</button>
</div>
…
</div>
<button data-sa-add="faq">Add a chip</button>
| Attribute | On | Meaning |
|---|---|---|
data-sa-list="faq" |
the container | The list becomes one manifest entry and is addressed as faq[2].question — rows counted in document order from 1. |
data-sa-row |
each row | One repeated row. |
data-sa-field="question" |
a control inside a row | The field’s name in the row. Also names a row’s own buttons; a delete keeps its consent gate. Descends into a component host. |
data-sa-add="faq" |
the button that appends a row | Declares the press harmless: the widget presses it itself, as many times as the asked-for row number needs, so the assistant never predicts a new row’s identity. Never on Save, Send or Delete. |
data-sa-fields="enabled,question:lang,delete" |
the container | What a row contains, for an empty list — with no row on screen there is nothing to read field names off. A field with a switcher names its axis (question:lang), or the first write into it is refused for lack of the keys. |
What the assistant sees is one line: [list faq] "FAQ chips" — 2 row(s) · fields: enabled (checkbox), question (input, lang: en/uk, showing en), delete (button) · add: "Add a chip". A
row index one past the end is the creation: the widget presses the add control the missing
number of times, waits for the page to render, re-counts, and fails the step if no row appeared.
Rows created are reported by position, never by the ids inside them. At most 20 rows are created
per call; faq[999] costs one refused step, not 999 presses.
Marking a list replaces the symbolic $new address the assistant uses on unmarked screens, for
that list: every control inside a marked row leaves the flat manifest. A hint
(data-sa-hint) inside a row is dropped — put it on the list’s own controls outside the rows.
A variant switcher
<div role="group" aria-label="Language" data-sa-variants="lang">
<button aria-pressed="true" data-sa-variant="en">English</button>
<button aria-pressed="false" data-sa-variant="uk">Українська</button>
</div>
<input data-sa-variant-of="lang" aria-label="Greeting" />
| Attribute | On | Meaning |
|---|---|---|
data-sa-variants="lang" |
the switcher | A switcher that decides which version of a field is shown; the axis name is yours (languages, currencies, sizes, plans). |
data-sa-variant="uk" |
each option | The key of that version. Must be non-empty. |
data-sa-variant-of="lang" |
the field | This field holds a version per key of that axis. |
data-sa-variant-options="en=English,uk=Українська" |
a dropdown switcher | Its options exist only while it is open, and a scan must not open it. Declare key=label pairs; the widget then operates it like any combobox — open, match the label, click. |
The assistant sends one value per key — {"en": "…", "uk": "…"} — and the widget operates
the switcher itself, switching to every version including the one already on show, writing,
and reading back. A plain string into a field that has an axis is refused with the keys named:
which version is showing is screen state, and writing into it is right only by luck. The governing
switcher is the one inside the field’s nearest common ancestor, so two switchers of the same
axis on one screen do not collide.
Declaring a switcher also tells the assistant that pressing it saves nothing — a different
version is shown, no value changed — which is what stops a language click being reported as an
unsaved edit or triggering an auto-save. A switcher that governs no field by name declares an
empty axis: data-sa-variants="" with a key on each option.
An unmarked switcher deliberately still counts as a change. The cost is a needless note about saving; the other way round is an edit reported saved and silently lost.
Without markup: $new
On an unmarked screen the assistant can still build a list: it presses Add and addresses the
controls that press created as $new, $new.2, $new.3 in document order — resolved by the
widget against what the presses actually put on the page, so a $new with nothing behind it
fails that step rather than writing elsewhere. It works, and it is the source of most of the
mistakes the list attributes exist to remove; mark the list once and the guessing stops.
What the ack says
A batch that created rows reports them by position (faq: rows 3–4 created), a variant write
reports each version (uk: written, pl: replaced), and a refused write names why:
needs_variants (a plain string into an axis field — the keys follow), variants_unknown (a
dropdown switcher with no declared options), no_such_variant, switch_failed (the option is
disabled or did not take), no_new_row (the add control added nothing), too_many_rows.