studio/frontend: include filename in attachment aria-label + img alt (#5594)

* studio/frontend: include filename in attachment aria-label and img alt

When a chat has multiple attachments of the same kind, the rendered
tiles all share the generic accessible name "Image attachment" or
"Document attachment". Sighted users get the filename from the Radix
tooltip that pops on hover, but:

  - screen-reader users hear "Image attachment, Image attachment,
    Image attachment" with no way to distinguish three PNGs;
  - touch-device users (no hover) lose the filename entirely;
  - keyboard-only users would have to focus and read a tooltip that
    isn't always announced.

Fold the filename into both the button's aria-label and the thumbnail
<img alt>, falling back to the existing labels when the attachment has
no filename. Sighted UX is unchanged: the Radix tooltip already shows
the same name on hover, and the visible aria-label has no rendered
counterpart.

Found while running a multi-image attach probe in the autonomous Studio
UX loop (cycle 8). Repro:

  await page.evaluate(`Array.from(document.querySelectorAll(
    'button[aria-label*="attachment" i]'
  )).map(b => b.getAttribute('aria-label'))`)

Before: ["Image attachment", "Document attachment", "Add Attachment"]
After:  ["Image attachment: test_red_circle.png",
         "Document attachment: notes.txt",
         "Add Attachment"]

* studio/frontend: shorten attachment a11y comment

---------

Co-authored-by: danielhanchen <michaelhan2050@gmail.com>
This commit is contained in:
Daniel Han 2026-05-19 06:56:25 -07:00 committed by GitHub
commit 75ee380a07
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -120,12 +120,13 @@ const AttachmentPreviewDialog: FC<PropsWithChildren> = ({ children }) => {
const AttachmentThumb: FC = () => {
const src = useAttachmentSrc();
const name = useAuiState(({ attachment }) => attachment.name);
if (src) {
return (
<img
src={src}
alt="Attachment preview"
alt={name || "Attachment preview"}
className="h-full w-full object-cover"
/>
);
@ -143,6 +144,7 @@ const AttachmentUI: FC = () => {
const isComposer = aui.attachment.source === "composer";
const isImage = useAuiState(({ attachment }) => attachment.type === "image");
const name = useAuiState(({ attachment }) => attachment.name);
const typeLabel = useAuiState(({ attachment }) => {
const type = attachment.type;
switch (type) {
@ -156,6 +158,11 @@ const AttachmentUI: FC = () => {
throw new Error(`Unknown attachment type: ${type as string}`);
}
});
// Include filename in accessible name so screen readers distinguish
// same-typed attachments. Sighted users get it via the tooltip.
const accessibleName = name
? `${typeLabel} attachment: ${name}`
: `${typeLabel} attachment`;
return (
<Tooltip>
@ -175,7 +182,7 @@ const AttachmentUI: FC = () => {
"aui-attachment-tile-composer border-foreground/20",
)}
id="attachment-tile"
aria-label={`${typeLabel} attachment`}
aria-label={accessibleName}
type="button"
>
<AttachmentThumb />