You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
Repository navigation
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
List toolbar below sm: the Filter, Export and Add record buttons render icon-only with no accessible name, so a screen reader announces "button" #12500
The toolbar's accessibility tree reads - button, - button "Display".
The first entry is toolbar-filter-trigger: role button, 38 × 28 px, accessible name empty, no aria-label, no title.
Studio Data pillar (/studio/app.objectstack.hotcrm/data?surface=object:crm_account):
The tree reads - button, - button "Refresh", - button.
The unnamed entries are the Filter trigger and the Add record button.
getByRole('button', { name: 'Filter' }) finds 0 at 375 px and 1 at 1280 px, on both surfaces.
reach: the console list toolbar and the Studio Records toolbar (UI doors), measured once each as above.
Cause
packages/plugin-list/src/ListView.tsx on objectui 03cf7b2 (line numbers unchanged from d4bc81e). Each of these buttons stays visible below sm. Its only text is a <span className="hidden sm:inline"> label, and it carries no aria-label:
button
label line
note
Filter (toolbar-filter-trigger)
:5140
When filters are active, the count badge (:5141–5145) becomes the button's whole name, a bare number.
Export
:5504
Wide form only (!compactToolbar). Not measured at 375 px; the markup is the same.
Add record, top (add-record-button)
:5657
Add record, bottom (add-record-button)
:5901
Neighbouring buttons already name themselves: Print sets aria-label (:5568), Share sets title (:5544), and Display, Search and Refresh set both. The Sort, Group, Hide fields and Color triggers are hidden sm:inline-flex as a whole, so below sm they are absent rather than unnamed.
Fix
Give each of the four buttons an aria-label from the same i18n key as its visible label: list.filter, list.export, list.addRecord. An aria-label replaces the badge text, so with active filters the Filter label carries the count itself: a new key (for example list.filterActive, "Filter, {{count}} active") in every locale pack. Keep the existing data-testids.
Acceptance
At 375 px, in both the runtime console and Studio, getByRole('button', { name: … }) finds Filter and Add record. Export is found in a non-compact toolbar.
A unit test renders the toolbar and asserts each button's accessible name, including Filter with one active filter.
CONTROL: at 1280 px each visible label is unchanged and no button is announced twice ("Filter Filter").
Who acts
Triage grades and routes it. The fix reader is the domain:ui lane that owns plugin-list. It is not part of epic #12446.
Dedupe (2026-10-15, MCP search_issues on objectstack-ai/objectui, open and closed)
"list toolbar icon-only buttons accessible name aria-label mobile Filter Add record Export": 8 hits, none this defect. Relevant toolbar cards came back, so the channel was live: objectui#12462 (open), objectui#12423, objectui#11984, objectui#10512 and objectui#5153.
"unnamed button screen reader small viewport list view toolbar label hidden sm:inline": 0 hits.
Filed by seat domain:ui#4 (session session_01SJn1GEkrbbBQkhGC26wEDw) from a Studio walk of HotCRM requested by the maintainer.
Triage: first grade · priority:p3 · pm:queue · lane kept domain:ui · triage seat · session_01AavokzJ5DndAwitDXvKy4U · 2026-10-15T14:03Z
Read fresh. Below sm, the list toolbar's Filter, Export and Add record buttons keep only a hidden sm:inline label, with no aria-label. A screen reader announces "button", and with active filters the Filter button's whole name is a bare count. Measured at 375 px on the console list and the Studio Records toolbar.
Grade p3: an accessibility defect on narrow viewports. The controls work, and their names are missing for assistive technology.
Lane domain:ui (packages/plugin-listListView.tsx).
Scope:
Each icon-only state carries an accessible name, the same words as its wide label, through the existing i18n keys.
Pins:getByRole('button', { name: 'Filter' }) finds one at 375 px. With filters active, the name includes "Filter" as well as the count.
Claim: PM loop round 1
Session: session_01XVp19E1MYUTFM3dnkwz8gB
Account: baozhoutao (the seat's linked user as GET /user answers it; the card's assignee)
Branch: claude/issue-12500-toolbar-accessible-names
Worktree: objectui-issue-12500
Domain: domain:ui
Seat: domain:ui#2
File surface: packages/plugin-list/src/ListView.tsx (only the Filter, Export and both Add record buttons' accessible names), packages/i18n/src/locales/*.ts (one new key for the active-filter name, in all ten packs) plus the plugin-list default translations if the list keeps a fallback table, the tests beside them, and one .changeset/12500-*.md (stop on breach; explain in the report)
Container & model: S, mode:subagent, model: opus (default tier; dispatch-gates --tier --repo objectstack-ai/objectui reads no path-derived mandate)
Clause-②: no
Responsibility: objectui plugin-list's ListView toolbar buttons whose only label is hidden below sm | none: neighbouring buttons already carry aria-label or title | screen-reader users of the console list and the Studio Records toolbar on narrow viewports
Thread-read: 6193958927
Serial constraints cleared: this seat's PR #12561 (objectui#12528, being patched) edits ListView.tsx (the description row, the sort popover and the record-count footer) and adds one key to the ten locale packs. Those regions are different, and the locale packs may need a trivial merge. Whichever lands second merges origin/main, as a merge commit, never a rebase. No other open PR touches ListView.tsx.
Claimed at: 2026-10-15T19:35Z
{
"issue": 12500,
"status": "done",
"branch": "claude/issue-12500-toolbar-accessible-names",
"pr": "https://github.com/objectstack-ai/objectui/pull/12575",
"session": "session_01XVp19E1MYUTFM3dnkwz8gB (subagent; the parent's id, per the claim)",
"premise_still_valid": true,
"summary": "The Filter (toolbar-filter-trigger), Export (wide form) and both Add record (add-record-button) buttons in packages/plugin-list ListView now carry an aria-label from the visible label's i18n key (list.filter / list.export / list.addRecord). With filters active, the Filter name is the new list.filterActive ('Filter, {{count}} active'). That key is an i18next count family in all ten packs, with every CLDR slot each language selects, and the en rows are in LIST_DEFAULT_TRANSLATIONS. A single key was not enough: count-families-11445 rejects a {{count}} leaf outside a family, and the list of exempt keys may only shrink. A3 confirmed by reading the code: StudioDesignSurface renders the same plugin-list ListView in the Data pillar's renderListView slot, so one fix covers both surfaces. All data-testids are unchanged. Merged origin/main once before opening the PR, as a merge commit.",
"tests": "Final tree 260589c. New ListView.toolbarAccessibleNames-12500.test.tsx: 4/4 pass. The below-sm cases mount the base Tailwind rule '.hidden { display: none }'. Covered: getByRole('button',{name:'Filter'}) finds the trigger; two toolbar 'Add record' buttons are found by name; 'Export' is found by name in a non-compact toolbar; with one active filter the accessible name is 'Filter, 1 active'. CONTROL (wide, no rule): text is exactly the label and the accessible name is the label once (A1 measured: 'Filter', not 'Filter Filter'). Ablation, after committing the fix: ListView.tsx restored to the branch base a90c131 (0 fix markers on disk) gave 'Tests 3 failed | 1 passed (4)'. The active-filter case received the accessible name '1', the card's bare-count symptom. Restored with git checkout HEAD, then git diff HEAD empty and the blob hash matching HEAD (HASH-MATCH). Under os-verify-lock: closure build '--filter @object-ui/plugin-list^... --filter @object-ui/i18n^...' exit 0. type-check for plugin-list and i18n (both scripts echoed) at 260589c, exit 0. 'vitest run packages/plugin-list/ packages/i18n/' at 260589c: 'Test Files 219 passed (219) / Tests 2763 passed | 13 skipped'. The first package run, before the sweep fix, failed 4 cases in ListView.fieldListRead-11984: its sweep keys triggers by name minus digits and read 'Filter, active'. Fixed by keying on the text before the comma. An earlier targeted run failed count-families-11445 on the single-key form, which is why the key is now a family. Gates at 260589c, exit 0: check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, check-hand-rolled-comment-mask, check-changeset-fixed, check-changeset-presence ('12 source file(s) of 2 released package(s) changed ... declares 1 changeset'), check-changeset-no-major, check-new-cross-file-line-citations ('0 new citation(s)'). check:spec-floors exit 1, as on main: recorded, not fixed. Lint narrowed to the 13 changed files with eslint --format json: 0 errors (187 warnings, all no-explicit-any or react-hooks: existing ones in ListView.tsx plus 2 any in the new test's data-source double). Full pnpm lint is left to CI. CI: in_progress, not awaited.",
"mcp_calls": "0",
"api_writes": "2 relay strokes (each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create = POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos/objectstack-ai/objectui/issues/12575/assignees [baozhoutao]; tool read-back: 3978 bytes sent, 3978 stored, identical. (2) comment = POST /repos/objectstack-ai/objectui/issues/12500/comments (this os-dev-report). Plus git pushes of the branch (not REST). Reads: GET issues/12500 and its comments. Labels: zero writes, because the dispatch named none and objectui's skip-changeset has no readers.",
"open_questions": [],
"out_of_scope_findings": [
"carrier: none -- noted, not filed: the ar and ru list.filterActive slots are agent translations of a short phrase; a native reader may want to review the adjective agreement (in PR Acceptance notes)."
],
"deviations": [
"list.filterActive landed as a count family (base + _one/_other + each pack's CLDR slots), not one flat key. count-families-11445 requires a family and only ever shrinks its exempt list; the ruling's intent (a new key in all ten packs plus the defaults table, i18n gates green) is unchanged.",
"Edited packages/plugin-list/src/__tests__/ListView.fieldListRead-11984.test.tsx, a test beside ListView in the file surface's 'tests beside them' clause, so its sweep keys the Filter trigger by its label rather than the new active-filter name.",
"The harness asked for a commit trailer with a model name. Commits use the model-free pair that objectui AGENTS.md requires (Claude-Session + Co-authored-by: Claude), and the PR footer is the session-URL form from the role file."
],
"files_changed": [
".changeset/12500-toolbar-accessible-names.md",
"packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts",
"packages/plugin-list/src/ListView.tsx",
"packages/plugin-list/src/__tests__/ListView.toolbarAccessibleNames-12500.test.tsx",
"packages/plugin-list/src/__tests__/ListView.fieldListRead-11984.test.tsx"
],
"gates": "see tests; exit codes recorded in scratchpad issue-12500/gates-final.txt"
}
The list toolbar's Filter, Export and both Add record buttons carry an aria-label from their visible label's key, so below sm a screen reader names them.
While filters are active, the Filter button's name includes the count (list.filterActive, a count family in all ten packs).
Owed to the maintainer: the browser reading at 375 px on the console list and the Studio Records toolbar.
pm:dispatched is removed with this note; the card closed completed on merge.
Activity
objectstack-fleet commentedon Oct 15, 2026
Triage: first grade ·
priority:p3·pm:queue· lane keptdomain:ui· triage seat ·session_01AavokzJ5DndAwitDXvKy4U· 2026-10-15T14:03ZRead fresh. Below
sm, the list toolbar's Filter, Export and Add record buttons keep only ahidden sm:inlinelabel, with noaria-label. A screen reader announces "button", and with active filters the Filter button's whole name is a bare count. Measured at 375 px on the console list and the Studio Records toolbar.Grade p3: an accessibility defect on narrow viewports. The controls work, and their names are missing for assistive technology.
Lane
domain:ui(packages/plugin-listListView.tsx).Scope:
getByRole('button', { name: 'Filter' })finds one at 375 px. With filters active, the name includes "Filter" as well as the count.objectstack-fleet commentedon Oct 15, 2026
Claim: PM loop round 1
Session:
session_01XVp19E1MYUTFM3dnkwz8gBAccount:
baozhoutao(the seat's linked user asGET /useranswers it; the card's assignee)Branch:
claude/issue-12500-toolbar-accessible-namesWorktree:
objectui-issue-12500Domain:
domain:uiSeat:
domain:ui#2File surface:
packages/plugin-list/src/ListView.tsx(only the Filter, Export and both Add record buttons' accessible names),packages/i18n/src/locales/*.ts(one new key for the active-filter name, in all ten packs) plus the plugin-list default translations if the list keeps a fallback table, the tests beside them, and one.changeset/12500-*.md(stop on breach; explain in the report)Container & model:
S,mode:subagent,model: opus(default tier;dispatch-gates --tier --repo objectstack-ai/objectuireads no path-derived mandate)Clause-②: no
Responsibility:
objectui plugin-list's ListView toolbar buttons whose only label is hidden below sm | none: neighbouring buttons already carry aria-label or title | screen-reader users of the console list and the Studio Records toolbar on narrow viewportsThread-read: 6193958927
Serial constraints cleared: this seat's PR #12561 (objectui#12528, being patched) edits
ListView.tsx(the description row, the sort popover and the record-count footer) and adds one key to the ten locale packs. Those regions are different, and the locale packs may need a trivial merge. Whichever lands second mergesorigin/main, as a merge commit, never a rebase. No other open PR touchesListView.tsx.Claimed at: 2026-10-15T19:35Z
objectstack-fleet commentedon Oct 15, 2026
os-dev-report
{ "issue": 12500, "status": "done", "branch": "claude/issue-12500-toolbar-accessible-names", "pr": "https://github.com/objectstack-ai/objectui/pull/12575", "session": "session_01XVp19E1MYUTFM3dnkwz8gB (subagent; the parent's id, per the claim)", "premise_still_valid": true, "summary": "The Filter (toolbar-filter-trigger), Export (wide form) and both Add record (add-record-button) buttons in packages/plugin-list ListView now carry an aria-label from the visible label's i18n key (list.filter / list.export / list.addRecord). With filters active, the Filter name is the new list.filterActive ('Filter, {{count}} active'). That key is an i18next count family in all ten packs, with every CLDR slot each language selects, and the en rows are in LIST_DEFAULT_TRANSLATIONS. A single key was not enough: count-families-11445 rejects a {{count}} leaf outside a family, and the list of exempt keys may only shrink. A3 confirmed by reading the code: StudioDesignSurface renders the same plugin-list ListView in the Data pillar's renderListView slot, so one fix covers both surfaces. All data-testids are unchanged. Merged origin/main once before opening the PR, as a merge commit.", "tests": "Final tree 260589c. New ListView.toolbarAccessibleNames-12500.test.tsx: 4/4 pass. The below-sm cases mount the base Tailwind rule '.hidden { display: none }'. Covered: getByRole('button',{name:'Filter'}) finds the trigger; two toolbar 'Add record' buttons are found by name; 'Export' is found by name in a non-compact toolbar; with one active filter the accessible name is 'Filter, 1 active'. CONTROL (wide, no rule): text is exactly the label and the accessible name is the label once (A1 measured: 'Filter', not 'Filter Filter'). Ablation, after committing the fix: ListView.tsx restored to the branch base a90c131 (0 fix markers on disk) gave 'Tests 3 failed | 1 passed (4)'. The active-filter case received the accessible name '1', the card's bare-count symptom. Restored with git checkout HEAD, then git diff HEAD empty and the blob hash matching HEAD (HASH-MATCH). Under os-verify-lock: closure build '--filter @object-ui/plugin-list^... --filter @object-ui/i18n^...' exit 0. type-check for plugin-list and i18n (both scripts echoed) at 260589c, exit 0. 'vitest run packages/plugin-list/ packages/i18n/' at 260589c: 'Test Files 219 passed (219) / Tests 2763 passed | 13 skipped'. The first package run, before the sweep fix, failed 4 cases in ListView.fieldListRead-11984: its sweep keys triggers by name minus digits and read 'Filter, active'. Fixed by keying on the text before the comma. An earlier targeted run failed count-families-11445 on the single-key form, which is why the key is now a family. Gates at 260589c, exit 0: check:phantom-deps, self-import, unreferenced-sources, doc-example-readers, handler-key-reads, side-effects-array, element-data-source-declaration, spec-symbols, action-forward-parity, designer-field-key-parity, icon-record-names, i18n-keys, i18n-drift, i18n-designer-parity, control-bytes, check-hand-rolled-comment-mask, check-changeset-fixed, check-changeset-presence ('12 source file(s) of 2 released package(s) changed ... declares 1 changeset'), check-changeset-no-major, check-new-cross-file-line-citations ('0 new citation(s)'). check:spec-floors exit 1, as on main: recorded, not fixed. Lint narrowed to the 13 changed files with eslint --format json: 0 errors (187 warnings, all no-explicit-any or react-hooks: existing ones in ListView.tsx plus 2 any in the new test's data-source double). Full pnpm lint is left to CI. CI: in_progress, not awaited.", "mcp_calls": "0", "api_writes": "2 relay strokes (each one POST /repos/objectstack-ai/objectstack/dispatches executed as objectstack-fleet[bot]): (1) pr_create = POST /repos/objectstack-ai/objectui/pulls (draft) + POST /repos/objectstack-ai/objectui/issues/12575/assignees [baozhoutao]; tool read-back: 3978 bytes sent, 3978 stored, identical. (2) comment = POST /repos/objectstack-ai/objectui/issues/12500/comments (this os-dev-report). Plus git pushes of the branch (not REST). Reads: GET issues/12500 and its comments. Labels: zero writes, because the dispatch named none and objectui's skip-changeset has no readers.", "open_questions": [], "out_of_scope_findings": [ "carrier: none -- noted, not filed: the ar and ru list.filterActive slots are agent translations of a short phrase; a native reader may want to review the adjective agreement (in PR Acceptance notes)." ], "deviations": [ "list.filterActive landed as a count family (base + _one/_other + each pack's CLDR slots), not one flat key. count-families-11445 requires a family and only ever shrinks its exempt list; the ruling's intent (a new key in all ten packs plus the defaults table, i18n gates green) is unchanged.", "Edited packages/plugin-list/src/__tests__/ListView.fieldListRead-11984.test.tsx, a test beside ListView in the file surface's 'tests beside them' clause, so its sweep keys the Filter trigger by its label rather than the new active-filter name.", "The harness asked for a commit trailer with a model name. Commits use the model-free pair that objectui AGENTS.md requires (Claude-Session + Co-authored-by: Claude), and the PR footer is the session-URL form from the role file." ], "files_changed": [ ".changeset/12500-toolbar-accessible-names.md", "packages/i18n/src/locales/{ar,de,en,es,fr,ja,ko,pt,ru,zh}.ts", "packages/plugin-list/src/ListView.tsx", "packages/plugin-list/src/__tests__/ListView.toolbarAccessibleNames-12500.test.tsx", "packages/plugin-list/src/__tests__/ListView.fieldListRead-11984.test.tsx" ], "gates": "see tests; exit codes recorded in scratchpad issue-12500/gates-final.txt" }objectstack-fleet commentedon Oct 15, 2026
Landed ·
domain:uiseat 2 ·session_01XVp19E1MYUTFM3dnkwz8gB· 2026-10-15T21:36ZPR #12575 merged through the merge queue as
3821a3a, which is onmain. Verified by content:origin/mainare blob-identical to the accepted head260589c.ListView.tsxand the ten locale packs) also carry PR feat(plugin-list,plugin-view,plugin-grid): list chrome on fewer rows — description on the active tab, sort note as an info control, one footer row (objectui#12528) #12561's changes, which landed first. The queue merge's own patch (3821a3a^1..3821a3a) is line-for-line identical to the PR's patch.What landed:
aria-labelfrom their visible label's key, so belowsma screen reader names them.list.filterActive, a count family in all ten packs).Owed to the maintainer: the browser reading at 375 px on the console list and the Studio Records toolbar.
pm:dispatchedis removed with this note; the card closedcompletedon merge.Generated by Claude Code
fix(plugin-list): name the icon-only Filter, Export and Add record to…