From 759cf21bf74a31c4e662ed8180e18215b638b85f Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 30 Aug 2026 04:23:52 -0700 Subject: [PATCH 1/2] Add tests for idle composer keyboard hint a11y The hint stays mounted in the action rail when idle. Opacity-only hide leaves it in the accessibility tree; these assertions require aria-hidden to flip with the visible state. --- packages/chat-ui/test/composer.test.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/packages/chat-ui/test/composer.test.tsx b/packages/chat-ui/test/composer.test.tsx index 1a55e8acd..082a9dc2c 100644 --- a/packages/chat-ui/test/composer.test.tsx +++ b/packages/chat-ui/test/composer.test.tsx @@ -265,6 +265,7 @@ describe("Composer keyboard hint", () => { mount(() => Promise.resolve(true)); expect(keyboardHint()).not.toBeNull(); expect(keyboardHint()?.getAttribute("data-visible")).toBe("false"); + expect(keyboardHint()?.getAttribute("aria-hidden")).toBe("true"); act(() => { textarea().focus(); @@ -273,6 +274,7 @@ describe("Composer keyboard hint", () => { await settle(); expect(keyboardHint()?.getAttribute("data-visible")).toBe("true"); + expect(keyboardHint()?.getAttribute("aria-hidden")).toBe("false"); expect(keyboardHint()?.textContent).toBe("Enter to send"); expect( container?.querySelectorAll(".chat-composer-actions > button").length, @@ -294,6 +296,7 @@ describe("Composer keyboard hint", () => { typeInto(textarea(), ""); await settle(); expect(keyboardHint()?.getAttribute("data-visible")).toBe("false"); + expect(keyboardHint()?.getAttribute("aria-hidden")).toBe("true"); }); }); From b41b82cab95f1f24c7bebdac2975e5719edc6f52 Mon Sep 17 00:00:00 2001 From: Sawyer Cutler Date: Sun, 30 Aug 2026 04:26:11 -0700 Subject: [PATCH 2/2] Hide idle composer keyboard hint from assistive tech MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Opacity-only hide left “Enter to send” in the accessibility tree while send was disabled. Restore visibility:hidden and set aria-hidden so the hint is announced only for a focused non-empty draft. --- packages/chat-ui/src/composer.tsx | 1 + packages/chat-ui/src/styles.css | 2 ++ 2 files changed, 3 insertions(+) diff --git a/packages/chat-ui/src/composer.tsx b/packages/chat-ui/src/composer.tsx index f670ae349..faece22d6 100644 --- a/packages/chat-ui/src/composer.tsx +++ b/packages/chat-ui/src/composer.tsx @@ -890,6 +890,7 @@ export const Composer = forwardRef< 0} + aria-hidden={!(focused && value.trim().length > 0)} > {CHAT_STRINGS.composerKeyboardHint} diff --git a/packages/chat-ui/src/styles.css b/packages/chat-ui/src/styles.css index 69fffe042..e41f12cd6 100644 --- a/packages/chat-ui/src/styles.css +++ b/packages/chat-ui/src/styles.css @@ -2560,12 +2560,14 @@ font-size: 0.7rem; font-weight: 600; letter-spacing: 0.01em; + visibility: hidden; opacity: 0; pointer-events: none; transition: opacity 120ms var(--chat-ease); } .chat-composer-keyboard-hint[data-visible="true"] { + visibility: visible; opacity: 1; }