Skip to content

[BUG]: genui's Image ignores the catalog's description, so the image has no accessible name #2801

Closed as not planned

Description

@diegolopezrm
  • I have searched the existing issues to make sure this bug has not already been reported.

Describe the Bug

The basic catalog's Image carries the text a screen reader is supposed to announce, and genui neither accepts it nor announces anything.

The spec declares it on the component itself:

"description": {
  "$ref": "common_types.json#/$defs/DynamicString",
  "description": "Accessibility text for the image."
}

web_core declares the same property in its basic catalog. genui's image.dart schema has url, fit and variant, and nothing else, and no widget under packages/genui/lib passes a semanticLabel.

So an image is the one component whose entire accessible name has to come from the agent, and it is the one component that drops it.

Steps to Reproduce

import 'package:a2ui_core/a2ui_core.dart' as core;
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:genui/genui.dart';

void main() {
  testWidgets('Image with the spec description', (tester) async {
    final catalog = BasicCatalogItems.asCatalog().copyWith(catalogId: 'repro');
    final controller = SurfaceController(catalogs: [catalog]);
    controller.handleMessage(core.UpdateComponentsMessage(surfaceId: 's', components: [
      {
        'id': 'root',
        'component': 'Image',
        'url': 'https://example.com/chart.png',
        'description': 'Chart of weekly usage',
      },
    ]));
    controller.handleMessage(core.CreateSurfaceMessage(surfaceId: 's', catalogId: 'repro'));

    final handle = tester.ensureSemantics();
    await tester.pumpWidget(MaterialApp(
        home: Scaffold(body: Surface(surfaceContext: controller.contextFor('s')))));
    await tester.pumpAndSettle();

    final node = tester.getSemantics(find.byType(Image).first);
    expect(node.label, equals('Chart of weekly usage'));
    expect(node.getSemanticsData().flagsCollection.isImage, isTrue);

    handle.dispose();
    controller.dispose();
  });
}

Measured on genui main (171a4bb): the image renders, so the property is accepted and then ignored, Image.semanticLabel is null, the semantics node's label is empty and isImage is false. Assistive technology gets an unnamed node that is not even announced as an image.

Expected Behavior

description is part of the component's schema and reaches Image(semanticLabel:), which also gives the node the image role. It is a DynamicString, so a {"path": ...} or a {"call": ...} resolves like any other property.

Environment Details

  • OS: macOS 26.5.2
  • Browser/Platform: Flutter 3.44.6 stable, Dart 3.12.2
  • SDK/Package Name & Version: genui at main (171a4bb), a2ui_core 0.1.1
  • Protocol Version: v0.9.1

Additional Context

This is the Image role gap from #2738, filed separately as asked there.

It is next to #2697 but not the same thing. That issue is about ComponentCommon.accessibility being dropped for every component; this one is about the property the basic catalog puts on Image itself, which would still not arrive once the generic wrapper lands, and about the node not carrying the image role. It is also one of the basic catalog divergences between genui and web_core mentioned in #2738.

Happy to send the PR alongside the one for #2697, since both land in the same area.

Activity

  1. added
    status: needs-triageauto-managed: https://github.com/a2ui-project/a2ui/blob/main/scripts/triage.mjs
    on Sep 25, 2026
  2. nan-yu commented on Sep 25, 2026

    @nan-yu
    Collaborator

    A2UI Triage: Confirmed. The basic catalog specification declares description on Image as accessibility text (DynamicString), which web_core implements. In genui, image.dart omits description from the component schema and does not pass semanticLabel to Flutter's Image widget, leaving the image without an accessible name.

    Marked P2 and size: small, and assigned to @polina-c. A PR is welcome since you mentioned you are happy to send one alongside #2697; cc @polina-c to coordinate.

  3. removed
    status: needs-triageauto-managed: https://github.com/a2ui-project/a2ui/blob/main/scripts/triage.mjs
    on Sep 25, 2026
  4. diegolopezrm commented on Sep 25, 2026

    @diegolopezrm
    ContributorAuthor

    This is a duplicate of #2740, which I filed on the 22nd and completely missed when I opened this one. Sorry for the double triage. Closing this, #2740 already has gspencergoog assigned.

  5. moved this from In Progress to Done in A2UIon Sep 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions