Skip to content

Commit dcd3bce

Browse files
feat(spec)!: retire action.aria — no action renderer ever applied it (ADR-0049) (#20398)
Fixes #20323 Clause-②: yes ## What this does Retires `action.aria` under ADR-0049 enforce-or-remove, following the triage direction on the card (RETIRE, on the chart-config precedent `2bf6ef18d`). No maintainer word reversed it to ENFORCE. - **Schema.** `ActionSchema.aria` becomes a `retiredKey()` tombstone. It is a `tsc` error (the input type is `never`) and a parse error that carries the prescription. The accessible name that IS applied is the action's required `label`, and the placing node's `aria` block (`page.components[].aria` or the list view `aria`) names the region. `AriaPropsSchema` is untouched: this is a key retirement, not a def retirement. - **ADR-0087.** The D2 conversion `action-aria-removed` (protocol 18, `retiredFromLoadPath`) strips the key from `actions[]` and `objects[].actions[]` as a lossless delete. The D3 semantic entry `action-aria-retired` is its own family, per the one-entry-per-family rule. The retired key `ui/Action:aria` is registered under 18, the major of the chart-config sibling. - **Ledger.** `liveness/action.json` regrades `aria` from `live` to `dead`. Its `REMOVED` note records that the uncited 「PARTIAL — honored by a few objectui renderers」 claim had no reader behind it. The undrilled-container row `action/aria` goes, and `state-counts.md` is regenerated. - **Docs.** Two hand-written pages taught `aria` on an action and are corrected: `protocol/objectui/actions.mdx` and `widget-contract.mdx`. The three reference pages that render `ActionSchema` are regenerated. - **Changeset.** `minor`, BREAKING, with `Clause-②: yes`, the FROM → TO table and the ADR-0087 `registered` marker. ## Premise, re-measured on this checkout - **objectui at the `.objectui-sha` pin `f8a9d0fb05`.** A reader grep for an action's `aria` (`action`, `actionDef`, `def`, `spec`, `btn`, `a`, `act`, `item` followed by `.aria`) over `packages/**` and `apps/**` non-test sources hits **0** lines. The control, the same grep for `.variant`, hits **18** files. Every `schema.aria` reader there is a placing node: the `record:*` components, `ListView`, `ObjectView` and `element:button`'s props. - **Icon-only reversal condition.** Triage named "icon-only actions have no accessible name" as the condition that would reverse this to ENFORCE. It does not hold. `action-icon.tsx:243` renders `aria-label={schema.label || schema.name}`, `action-menu.tsx:341` renders `aria-label={schema.label || moreActionsLabel}`, and `action-button.tsx:346` renders `{schema.label}` as the visible text. - **Framework.** A grep for `.aria` over `packages/**` non-test TS outside `packages/spec` hits **0** lines. `action.form.ts` has 0 `aria` rows; its one hit is the `variant` substring. - **Authors in this repo.** Across `examples/**`, the published `skills/**` and `packages/**` fixtures, **0** actions author `aria`. The control is **15** `variant:` lines in `examples/**`. - **Authors in HotCRM `2f7b2326`.** **0** actions author `aria`. Its 6 `aria:` blocks are all page-level `page.aria`, which stays live. The control: 7 files under `src/**/actions/` declare `locations:`, 17 times. - **Served schema.** `metadata-protocol` drops a tombstone's `{ not: {} }` node from the served JSON Schema, via `stripUnauthorableProperties`. So the Studio "More fields" form stops offering `aria` once this ships. - **Pinned sibling.** objectui's `ActionRunner.ts:410` mirrors the key as `aria?: SpecActionInput['aria']`. A type probe against this branch's built `dist` compiles that mirror at exit 0, because it evaluates to `undefined`. An authored block on it is refused with TS2322, and the control leg without `@ts-expect-error` exits 2. So the Console Pin Gate's objectui build is not broken by this change. ## Hand-over review (the stopped run's six commits, read hunk by hunk) | commit | verdict | | --- | --- | | `0c5dbbff` sources, ledger, tests, changeset | **kept, with corrections.** The prescription said "removed in @objectstack/spec 17"; it now says `17.5.0`, the spelling every sibling retirement on this line uses. The refusal pin in `action.test.ts` now also asserts the tombstone's own issue kind (`invalid_type` at `aria`, and no root `unrecognized_keys`, which is what a bare deletion would answer instead). The changeset's BREAKING sentence now names the replacement. The HotCRM control is re-measured: 7 files and 17 lines, not "5 action files". | | `6334c137` regenerated artifacts | kept, and re-derived after both merges | | `bfc86375` the stored-row pin uses a parseable script action | kept | | `0f20c62f` the `action/aria` undrilled-container row goes | kept; `check:liveness` is green | | `8cbcfa46` merge of `main` | kept | | `90b8fcdc` regenerated state counts | kept; superseded by the post-merge regeneration | Added in this round: - `33407119` and `c38c18ae` merge `origin/main` through `scripts/pm/os-regen-merge.sh`. Both merges stopped on the two registries. Each was settled by stacking both sides: main's conversions and rationale paragraphs first, then this branch's. The merges brought in #20262, #20352, #20251 and #20353. - `2b15085d` and `7faf0e9f` are the deferred regenerations. - `68b038ef` carries the corrections above, plus two more: - a new pin that the stored-row seam reaches an `object` row's nested action (the changeset's second at-rest coordinate); - one sentence on the `action` row of `liveness/README.md`. That row said "makes the dead set three", which this change makes false in a published file (`liveness` is in `@objectstack/spec`'s `files`). - `8dd3a2ce` regenerates the reference pages for the 17.5.0 prescription. ## Verification Head `7faf0e9f`, on `origin/main` `15bf186f`: - `pnpm --filter @objectstack/spec test`: `Test Files 558 passed (558)` · `Tests 16509 passed | 1 todo (16510)`. - `pnpm --filter @objectstack/spec test:repo`: `Test Files 35 passed (35)` · `Tests 634 passed (634)`. - `pnpm --filter @objectstack/spec check:generated`: all 15 generated artifacts up to date, measured over a spec build made on this head. - Gates: `node scripts/pm/dispatch-gates.mjs --commands` derives 114 commands. All 114 exit 0, and `--ran` reconciles them as `114 derived, 114 run, 0 NOT-MEASURED, 0 UNRUN`. The run covers every package's build closure, rebuilt on this head, so the gates that read `dist` measured it. Head `8dd3a2ce`, before the second `main` merge, on `origin/main` `862b6ce8`. The second merge touched none of these packages' interaction with this diff; the incoming commits retire other keys and touch no action surface. | package | command | Test Files | Tests | | --- | --- | --- | --- | | `@objectstack/lint` | `vitest run` | 112 passed | 4640 passed | | `@objectstack/cli` | `vitest run --project unit` | 231 passed | 3309 passed | | `@objectstack/runtime` | `vitest run --project local` | 282 passed | 4059 passed, 1 skipped | | `@objectstack/metadata-protocol` | `vitest run` | 189 passed, 3 skipped | 2736 passed, 19 skipped | | `@objectstack/metadata-core` | `vitest run` | 16 passed | 285 passed | | `@objectstack/objectql` | `vitest run --project local` | 322 passed | 5857 passed | `pnpm --filter @objectstack/spec typecheck` was green at the same head. The cli `integration` layer is declared to CI: this diff touches no spawn entry and no integration file. **Ablation**, from the committed state at `8dd3a2ce`. The blobs of `action.zod.ts` and of the three pin files are byte-identical at `7faf0e9f`. - The mutation goes through `scripts/ablation-replace.mjs`: the anchor ` aria: retiredKey(` becomes ` aria: z.any().optional().describe(`, so the key is accepted again. The tool reports the anchor going from 1 to 0, the replacement from 0 to 1, and the blob from `2e0a17b14b06` to `45e6bd9b1156`. The wrapper also arms a `trap` that restores the file. - The control leg runs the three pin files on the committed state: `Tests 166 passed (166)`. - The mutant leg: `Tests 4 failed | 162 passed (166)`. The four red tests: - `action.test.ts` · refuses an action carrying `aria`; - `aria-carrier-tombstones.test.ts` · the action tombstone fires and prescribes; - `aria-carrier-tombstones.test.ts` · the object-nested coordinate; - `action-aria-removed.test.ts` · the stored-row seam. - The restore is proven by content, not by an exit code. The file's blob equals HEAD's blob `2e0a17b14b06`, `git diff HEAD` is empty, and the porcelain status has 0 lines. - The pins import `./action.zod` and `../ui/action.zod.js` relatively. They read `src`, not `dist`, so this ablation has no dist leg. ## Acceptance notes - **objectui, owned by seat 4 after landing; not in this PR.** `ActionDefaultInspector` should list `aria` in its `RETIRED_FIELDS`, per triage note 3. `ActionRunner.ts:410` should drop its `aria?: SpecActionInput['aria']` mirror, which evaluates to `undefined` once this ships and still compiles (the probe above). - **#19332, owned by seat 4 after landing.** The disposition of `action.aria` goes to #19332's item that waits on this card. - **Advisory lint.** No `lint-liveness-properties` non-warn pin is added. The `aria` row never carried `authorWarn`, so the advisory lint's behaviour is unchanged: it was silent before and is silent now. - **Aliases.** `ActionSchema` never aliased `accessibility` or `ariaProps` onto `aria`, unlike the chart config. A probe shows both already refused as `unrecognized_keys`, so no alias refusal pin is owed. --- _Generated by [Claude Code](https://claude.ai/code/session_01ARcDurZ5j34RdqsGgc4jgH)_ --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent df3ba16 commit dcd3bce

19 files changed

Lines changed: 536 additions & 31 deletions

File tree

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
---
2+
'@objectstack/spec': minor
3+
---
4+
5+
**BREAKING** — `aria` on an action (`ActionSchema`, top-level `actions[]` and `objects[].actions[]`) is now refused at parse: no action surface ever applied it. Write the accessible name in the action's rendered `label`, and name the region that places the actions with `ariaLabel` / `ariaDescribedBy` / `role` in the placing node's `aria` block (`page.components[].aria` or the list view `aria`).
6+
7+
Clause-②: yes
8+
9+
`ActionSchema` declared a per-action ARIA block, and the liveness ledger graded it `live` on an uncited note — 「PARTIAL — honored by a few objectui renderers, not the core action buttons/menus」 — with no reader behind it. Re-measured at this checkout's own `.objectui-sha` pin `f8a9d0fb05`: none of the surfaces that render an action reads an action's `aria` — not `action:button`, `action:icon`, `action:menu`, `action:group` or `action:bar`, not the grid's row and bulk action menus, not `record:quick_actions`, not the declared-actions bar. The only `schema.aria` readers there are the placing nodes' own blocks (the `record:*` page components, the list view, `element:button`'s props), none of which looks inside an action. So an author — or an AI — who filled in `aria` got no accessible name on the rendered button, and nothing said so.
10+
11+
It is the fourth member of the `aria` family retired for exactly this, after `dashboard.aria`, `dashboard.widgets[].aria` and the chart config's `aria`.
12+
13+
**Removed rather than enforced** (ADR-0049 enforce-or-remove; the triage direction on the card, following the chart config retirement `2bf6ef18d`). The capability is already delivered under another key. Every one of those surfaces derives the accessible name from the action's **required** `label` — the visible button or menu-item text, and the `aria-label` of the icon-only `action:icon` and of the overflow-menu trigger — and the node that places the actions carries the node-level `ariaLabel` / `ariaDescribedBy` / `role`. The reversal condition the triage named (an icon-only action rendered with no accessible name at all) was measured and does not hold on any of them. A per-action block would be a second spelling of both, behind a precedence rule nobody has written.
14+
15+
## FROM → TO
16+
17+
| you wrote (17.4 and earlier) | write instead |
18+
| --- | --- |
19+
| `aria: { ariaLabel: 'Escalate this case' }` on an action, top-level or under `objects[].actions[]` | the name in the action's `label` — it is what every action renderer announces |
20+
| `aria: { ariaDescribedBy: … }` / `aria: { role: … }` on an action | delete it; to describe or role the toolbar or list the actions sit in, put it in the `aria` block of the node that places them — `page.components[].aria` or the list view `aria` |
21+
| `ariaLabel` / `ariaDescribedBy` / `role` on a page, page component or list view | unchanged — the shared `AriaProps` block stays live there |
22+
23+
**The one-line fix:** delete `aria` from the action; put the accessible name in its `label`.
24+
25+
`os migrate meta --from 17` lists the mechanical edits for existing sources; apply them by hand.
26+
27+
## The retirement kit
28+
29+
- **A `retiredKey()` tombstone, not a bare deletion** — even though `ActionSchema` is a `strictObject`. A bare delete would still be loud, but only as a generic unrecognized-key report that cannot carry the prescription; the tombstone types the key `never` for `tsc` and raises the upgrade text at parse. The key therefore stays in the walked shape: its liveness row stays (regraded `live` → `dead` with a `REMOVED` note that records the uncited 「PARTIAL」 claim it replaces) and the authorable-surface baseline marks `ui/Action:aria` `[RETIRED]`.
30+
- **The D2 conversion `action-aria-removed`** (protocol 18, retired from the load path) strips the key from stack `actions[]` and from `objects[].actions[]` as a pure lossless delete — it never had an effect to lose. Its D3 record is the semantic entry `action-aria-retired`: its own family, not a member of the chart config's.
31+
- **`AriaPropsSchema` is untouched** — a key retirement, not a def retirement; it stays live on pages, page components, the list view and the element props.
32+
- **No form input and no locale bundle move.** The key never reached `action.form.ts`. The Studio action inspector's "More fields" section is derived from the served schema, where a tombstone node is dropped from the payload, so the served `aria` column goes with this release.
33+
34+
## Reach, measured
35+
36+
- This repository: **0** authors of `aria` on an action in `examples/**`, `packages/**` fixtures or the published skills (control: 15 `variant:` lines in `examples/**`). Two hand-written docs pages taught the key and are corrected here.
37+
- HotCRM at `origin/main` `2f7b2326`: **0** on an action; its 6 `aria:` blocks are all page-level `page.aria`, which stays live (control: HotCRM authors actions — 7 files under `src/**/actions/` declare `locations:`, 17 times).
38+
- Other out-of-repo authors: NOT MEASURED.
39+
40+
## What an operator with a STORED action sees
41+
42+
A `sys_metadata` `action` or `object` row written before this release can carry the key. Nothing breaks at read: the conversion replays on rehydration and strips it, so the row is served canonical and parses. `os migrate meta --stored --apply` rewrites the rows.
43+
44+
<!-- adr-0087: registered action-aria-removed, action-aria-retired -->

‎content/docs/protocol/objectui/actions.mdx‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -244,12 +244,14 @@ interface Action {
244244

245245
// AI (ADR-0011)
246246
ai?: ActionAi; // Opt-in AI tool exposure
247-
248-
// Misc
249-
aria?: AriaProps; // Accessibility attributes
250247
}
251248
```
252249

250+
An action has no `aria` block. Its accessible name is its required `label`: the
251+
visible button or menu-item text, and the `aria-label` of an icon-only action.
252+
To name the toolbar or list the actions sit in, author `aria` on the node that
253+
places them (`page.components[].aria` or the list view's `aria`).
254+
253255
<Callout type="info">
254256
Action `name` is the configuration ID and **must** be lowercase `snake_case` (`approve_request`, not `approveRequest` or `Approve Request`). JavaScript function names referenced by `body`/`target` may still use camelCase.
255257
</Callout>

‎content/docs/protocol/objectui/widget-contract.mdx‎

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -223,8 +223,11 @@ declare the registry.
223223
## Accessibility
224224

225225
`AriaProps` (`packages/spec/src/ui/i18n.zod.ts`) is the shared ARIA shape carried
226-
by the live UI schemas — views, pages, page components, charts and actions all
227-
declare an `aria:` block. The supported attributes are intentionally minimal:
226+
by the live UI schemas — list views, pages and page components declare an `aria:`
227+
block. A chart config and an action do not: both `aria` keys were retired because
228+
no renderer applied them. A chart's accessible name is its `description`, an
229+
action's is its `label`, and the region that places either is named by its own
230+
`aria:` block. The supported attributes are intentionally minimal:
228231

229232
{/* os:check */}
230233
```typescript

‎content/docs/references/data/object.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,7 +478,7 @@ const result = ApiMethod.parse(data);
478478
| **opensInNewTab** | `boolean` | optional | Open the action result in a new tab. The renderer pre-opens the tab synchronously on click (popup-blocker-safe) and navigates it to the handler's redirectUrl. |
479479
| **newTabUrl** | `string` | optional | Direct new-tab URL template (`{recordId}` placeholder). When set with opensInNewTab, the renderer navigates the pre-opened tab here immediately — no action POST. The endpoint must enforce auth itself. |
480480
| **onSuccess** | `{ navigate: string; openIn?: Enum<'self' \| 'newTab'> }` | optional | Post-success navigation for type:'api' and type:'script' actions. `navigate` is a route/URL template interpolating $`{param.*}`, $`{ctx.*}` and $`{result.*}` (the server response); `openIn` defaults 'self'. The handler-return convention (`{ redirectUrl }` without openIn) keeps its 17.0.0 new-tab behavior. |
481-
| **aria** | `{ ariaLabel?: string \| Record<string, string>; ariaDescribedBy?: string; role?: string }` | optional | ARIA accessibility attributes |
481+
| **aria** | `never` | optional | [REMOVED] `action.aria` was removed in @objectstack/spec 17.5.0 (ADR-0049 enforce-or-remove) — no action surface ever applied it: the button, icon, menu, group and bar renderers, the row and bulk action menus and the record quick-actions toolbar all take the accessible name from the action's `label` and never read this block, so ARIA attributes declared here parsed and then silently did not reach the DOM. Delete the key. The accessible name that IS applied is the action's required `label` — the visible button or menu-item text, and the `aria-label` of an icon-only action — so write the name you meant there. To name the region that PLACES the actions, author `ariaLabel` / `ariaDescribedBy` / `role` in the `aria` block of the placing node: `page.components[].aria` (the component that renders the actions) or the list view `aria`. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; apply them by hand. |
482482
| **_lock** | `Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>` | optional | Item-level lock — controls overlay & delete (ADR-0010). |
483483
| **_lockReason** | `string` | optional | Human-readable reason shown when a write is refused by _lock. |
484484
| **_lockSource** | `Enum<'artifact' \| 'package' \| 'env-forced'>` | optional | Layer that set _lock (artifact \| package \| env-forced). |

‎content/docs/references/kernel/metadata-plugin.mdx‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -348,7 +348,7 @@ const result = MetadataBulkResultSchema.parse(data);
348348
| **opensInNewTab** | `boolean` | optional | Open the action result in a new tab. The renderer pre-opens the tab synchronously on click (popup-blocker-safe) and navigates it to the handler's redirectUrl. |
349349
| **newTabUrl** | `string` | optional | Direct new-tab URL template (`{recordId}` placeholder). When set with opensInNewTab, the renderer navigates the pre-opened tab here immediately — no action POST. The endpoint must enforce auth itself. |
350350
| **onSuccess** | `{ navigate: string; openIn?: Enum<'self' \| 'newTab'> }` | optional | Post-success navigation for type:'api' and type:'script' actions. `navigate` is a route/URL template interpolating $`{param.*}`, $`{ctx.*}` and $`{result.*}` (the server response); `openIn` defaults 'self'. The handler-return convention (`{ redirectUrl }` without openIn) keeps its 17.0.0 new-tab behavior. |
351-
| **aria** | `{ ariaLabel?: string \| Record<string, string>; ariaDescribedBy?: string; role?: string }` | optional | ARIA accessibility attributes |
351+
| **aria** | `never` | optional | [REMOVED] `action.aria` was removed in @objectstack/spec 17.5.0 (ADR-0049 enforce-or-remove) — no action surface ever applied it: the button, icon, menu, group and bar renderers, the row and bulk action menus and the record quick-actions toolbar all take the accessible name from the action's `label` and never read this block, so ARIA attributes declared here parsed and then silently did not reach the DOM. Delete the key. The accessible name that IS applied is the action's required `label` — the visible button or menu-item text, and the `aria-label` of an icon-only action — so write the name you meant there. To name the region that PLACES the actions, author `ariaLabel` / `ariaDescribedBy` / `role` in the `aria` block of the placing node: `page.components[].aria` (the component that renders the actions) or the list view `aria`. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; apply them by hand. |
352352
| **_lock** | `Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>` | optional | Item-level lock — controls overlay & delete (ADR-0010). |
353353
| **_lockReason** | `string` | optional | Human-readable reason shown when a write is refused by _lock. |
354354
| **_lockSource** | `Enum<'artifact' \| 'package' \| 'env-forced'>` | optional | Layer that set _lock (artifact \| package \| env-forced). |

‎content/docs/references/ui/action.mdx‎

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ const result = ActionSchema.parse(data);
6767
| **opensInNewTab** | `boolean` | optional | Open the action result in a new tab. The renderer pre-opens the tab synchronously on click (popup-blocker-safe) and navigates it to the handler's redirectUrl. |
6868
| **newTabUrl** | `string` | optional | Direct new-tab URL template (`{recordId}` placeholder). When set with opensInNewTab, the renderer navigates the pre-opened tab here immediately — no action POST. The endpoint must enforce auth itself. |
6969
| **onSuccess** | `{ navigate: string; openIn?: Enum<'self' \| 'newTab'> }` | optional | Post-success navigation for type:'api' and type:'script' actions. `navigate` is a route/URL template interpolating $`{param.*}`, $`{ctx.*}` and $`{result.*}` (the server response); `openIn` defaults 'self'. The handler-return convention (`{ redirectUrl }` without openIn) keeps its 17.0.0 new-tab behavior. |
70-
| **aria** | `{ ariaLabel?: string \| Record<string, string>; ariaDescribedBy?: string; role?: string }` | optional | ARIA accessibility attributes |
70+
| **aria** | `never` | optional | [REMOVED] `action.aria` was removed in @objectstack/spec 17.5.0 (ADR-0049 enforce-or-remove) — no action surface ever applied it: the button, icon, menu, group and bar renderers, the row and bulk action menus and the record quick-actions toolbar all take the accessible name from the action's `label` and never read this block, so ARIA attributes declared here parsed and then silently did not reach the DOM. Delete the key. The accessible name that IS applied is the action's required `label` — the visible button or menu-item text, and the `aria-label` of an icon-only action — so write the name you meant there. To name the region that PLACES the actions, author `ariaLabel` / `ariaDescribedBy` / `role` in the `aria` block of the placing node: `page.components[].aria` (the component that renders the actions) or the list view `aria`. Run `os migrate meta --from 17` to list the mechanical edits for existing sources; apply them by hand. |
7171
| **_lock** | `Enum<'none' \| 'no-overlay' \| 'no-delete' \| 'full'>` | optional | Item-level lock — controls overlay & delete (ADR-0010). |
7272
| **_lockReason** | `string` | optional | Human-readable reason shown when a write is refused by _lock. |
7373
| **_lockSource** | `Enum<'artifact' \| 'package' \| 'env-forced'>` | optional | Layer that set _lock (artifact \| package \| env-forced). |
@@ -148,14 +148,6 @@ L2 sandboxed JS body — runs inside an isolated VM with declared capabilities
148148
| **navigate** | `string` | ✅ | Route/URL template navigated to after the action succeeds. Interpolates $`{param.*}` (params-dialog values), $`{ctx.*}` (origin/apiBase/user/org/recordId/selection) and $`{result.*}` (the action's server response payload — NEW with this key, e.g. $`{result.id}`). Relative = SPA route hop; renderers MUST encodeURIComponent values in query positions. |
149149
| **openIn** | `Enum<'self' \| 'newTab'>` | optional (default: `"self"`) | Where to perform the post-success navigation: 'self' (default — in-place SPA navigation, immune to popup blocking) or 'newTab'. Closed enum — no general navigation DSL. |
150150

151-
### Nested Shape: `Action.aria`
152-
153-
| Property | Type | Required | Description |
154-
| :--- | :--- | :--- | :--- |
155-
| **ariaLabel** | `string \| Record<string, string>` | optional | Accessible label for screen readers (WAI-ARIA aria-label). Plain string, or an inline locale map — no translation-bundle slot addresses this key, so a plain string is announced in the source language. |
156-
| **ariaDescribedBy** | `string` | optional | ID of element providing additional description (WAI-ARIA aria-describedby) |
157-
| **role** | `string` | optional | WAI-ARIA role attribute (e.g., "dialog", "navigation", "alert") |
158-
159151

160152
---
161153

‎packages/spec/authorable-surface/ui.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
"ui/Action:_packageVersion",
1515
"ui/Action:_provenance",
1616
"ui/Action:ai",
17-
"ui/Action:aria",
17+
"ui/Action:aria [RETIRED]",
1818
"ui/Action:body",
1919
"ui/Action:bodyExtra",
2020
"ui/Action:bodyShape",

0 commit comments

Comments
 (0)