Repository navigation
Description
Found while implementing objectstack#5430 (the nav-overlay Close item). Not fixed there — the fix lands in a No-Touch zone and needs a route decision.
What
Both auto-rendered close buttons in the Shadcn primitives carry their accessible name as a hardcoded English sr-only span:
| File | Line |
|---|---|
packages/components/src/ui/sheet.tsx |
80 |
packages/components/src/ui/dialog.tsx |
57 |
The button is icon-only (a lucide X), so that span is the control's name to a screen reader. Under a zh/ja/es session every drawer and every modal in the console announces "Close" in English.
Why it is live, and how #5430 surfaced it
NavigationOverlay is the one consumer that hides the primitive's own close ([&>button:last-of-type]:hidden) because it renders its own in the header — so there it is display:none and out of the accessibility tree. Everywhere else it renders normally. Roughly 20 SheetContent consumers plus every DialogContent consumer, e.g.:
packages/app-shell/src/layout/ChatDock.tsx,layout/ActivityFeed.tsxpackages/app-shell/src/views/metadata-admin/*(PackagesPage, PermissionMatrixEditor, AccessExplainPanel, ResourceEditPage, MetadataDetailDrawer)packages/app-shell/src/console/ai/AiChatPage.tsx,console/ai/BuildDebugDrawer.tsxpackages/fields/src/widgets/PeoplePicker.tsx,packages/plugin-dashboard/src/RecordDetailDrawer.tsx,packages/plugin-charts/src/ObjectChart.tsx,packages/plugin-chatbot/src/AiPendingActionsInbox.tsx
It surfaced in #5430 as a test artifact: getByRole('button', { name: 'Close' }) matched two elements under an en session, because jsdom does not apply the Tailwind hidden class. The #5430 test worked around it by addressing its own button via title; the primitive's label was left alone on purpose.
Why it is not a one-line fix
packages/components/src/ui/** is a No-Touch zone (AGENTS.md Commandment #7) — those files are overwritten by pnpm shadcn:update / scripts/shadcn-sync.js. Editing the literal in place would be silently reverted by the next sync. Options worth weighing:
- A. Wrapper in
packages/components/src/custom/— the documented escape hatch, butSheetContent/DialogContentrender the close internally, so a wrapper would have to hide the built-in one and re-render its own (whatNavigationOverlayalready does by hand). That is the pattern to generalize, at the cost of every consumer migrating to the wrapper. - B. Teach the sync script a patch/allowlist for this one literal, so the i18n call survives regeneration.
- C. Accept a prop (
closeLabel) threaded from consumers — pushes the burden onto ~20 call sites and leaves the default English.
A relevant precedent: common.close already exists in all ten locale packs (#5430 wired NavigationOverlay's own close to it), so only the delivery mechanism is open, not the key.
Acceptance
- The close affordance of
SheetContentandDialogContentreadscommon.closeunder every locale. - Whatever route is chosen survives
pnpm shadcn:check/ a sync run. - Tests: en positive + at least one non-en positive + a negative assertion on the English literal, for both primitives.
Generated by Claude Code
Activity
yinlianghui commentedon Aug 6, 2026
认领:objectui 分片 PM 并行批第 37 单
会话:
session_01GTRjn8xBqp75dk7kFupVRt分支:
claude/issue-5505-shadcn-close-i18n(落地仓 objectui)Worktree:
objectui-issue-5505文件面:
packages/components/src/ui/sheet.tsx+dialog.tsx(经 sync 补丁机制)、scripts/shadcn-sync.js(或其补丁清单)、packages/components测试、changeset。⛔ 不动语言包(common.close已在十包)。PM 路线裁定(issue 三选项):采 B——教 sync 脚本携带这一处 i18n 补丁,使
t('common.close')在再生成后存活。理由(两轴):A 要 ~20 个消费方迁移 wrapper 且复制 NavigationOverlay 的手工模式,爆炸半径大;C 把负担推给每个调用点、默认值仍是英文(消费端宽容形状);B 在唯一源头修、全体消费方自动受益。硬性要求:补丁必须可验证——pnpm shadcn:check/ sync 在补丁失配时响亮失败而非静默还原(声明即强制);若 sync 脚本结构不支持补丁机制,STOP 报 needs_decision,不得改走 A/C。如有异议此处否决。Generated by Claude Code
yinlianghui commentedon Aug 6, 2026
验收:ACCEPT → objectui PR #3455(并行批第 37 单,会话
session_01GTRjn8xBqp75dk7kFupVRt),已跑检查 9 绿 0 红(shards/Type Check/Lint 排队中,本地等价全绿);ready + auto-merge 因 GraphQL 配额排队,恢复即执行。复核要点:
--force旁路、无 check 可见——于是把补丁做成声明式数据,每次写入(含 --force)重放,--check双向验证(本地标记 + 上游锚点适用性)失配即响亮 exit 1。副产物:仅含已声明补丁的组件与上游比对相等,「拒绝覆盖」信号回归只指未声明漂移的本义。src/lib/close-label.tsx判合理:载荷放 ui/** 会被再生成吞掉,B 路线的结构前提;补丁因此缩到每文件两行锚点。bug+pm:queue+repo:objectui):objectstack#5803(fetch 缓存 403 错误体污染后续 --check,已复现)、objectstack#5805(shadcn-check.yml 的 continue-on-error 吞掉本 PR 刚赋予意义的退出码——门禁家族的下一环)。Generated by Claude Code