Skip to content

feat: add HasAriaDescription interface - #25117

Merged
mcollovati merged 14 commits into
mainfrom
feat/has-aria-description
Aug 4, 2026
Merged

mcollovati merged 14 commits into
mainfrom
feat/has-aria-description

Conversation

@vursen

@vursen vursen commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Description

Adds a HasAriaDescription mixin interface for setting the accessible description of a component, following the HasAriaLabel pattern:

  • setAriaDescription(String) / getAriaDescription() for the aria-description attribute
  • setAriaDescribedBy(String) / getAriaDescribedBy() for the aria-describedby attribute
  • setAriaDescribedBy(Component) for referencing a description component directly, generating an id for it lazily when needed (same mechanism as setAriaLabelledBy(Component))

Also adds the aria-description and aria-describedby attribute name constants to ElementConstants.

Part of vaadin/web-components#11975

Type of change

  • Feature

vursen and others added 9 commits August 3, 2026 13:20
Adds a mixin interface for setting the accessible description of a
component via the aria-description and aria-describedby attributes,
following the HasAriaLabel pattern. The aria-describedby value can also
be set by referencing another component, generating an id for it lazily
when needed.

Unlike HasAriaLabel, the getters return the plain attribute value or
null instead of Optional, as no obvious reason was found to support
Optional.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
aria-describedby also works with hidden elements, so the guidance to
choose between the attributes based on visibility was misleading.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vursen
vursen marked this pull request as ready for review August 3, 2026 09:42
@github-actions github-actions Bot added the +0.1.0 label Aug 3, 2026
@github-actions

github-actions Bot commented Aug 3, 2026 •

Copy link
Copy Markdown
Contributor

Test Results

 1 449 files  +1   1 450 suites  +1   1h 27m 44s ⏱️ + 1m 33s
10 393 tests +5  10 326 ✅ +5  67 💤 ±0  0 ❌ ±0 
10 829 runs  +5  10 761 ✅ +5  68 💤 ±0  0 ❌ ±0 

Results for commit fffde7b. ± Comparison against base commit ab58985.

♻️ This comment has been updated with latest results.

@vursen
vursen requested review from mcollovati and tltv August 3, 2026 10:18
*
* @return the aria-description or {@code null} if none has been set
*/
default String getAriaDescription() {

@vursen vursen Aug 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getters return the plain attribute value or null, which diverges from the Optional<String> getters in HasAriaLabel. Why Optional was chosen there isn't obvious to me.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't have strong opinions on that, but it is definitely weird to have a different return type for a similar use case.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, changed the getters to return Optional.

}
ComponentUtil.resolveOrGenerateIdLater(getElement(),
descriptionComponent, "ariadescribedby-",
this::setAriaDescribedBy);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The utility class takes a Consumer<String> that is actually wrong, since the instance is then captured by other lambdas stored in the VaadinSession.
This should be fixed in ComponentUtil.resolveOrGenerateIdLater, maybe in a separated PR.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created #25118

Comment on lines +109 to +111
ComponentUtil.resolveOrGenerateIdLater(getElement(),
descriptionComponent, "ariadescribedby-",
this::setAriaDescribedBy);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If I call this method and then call setAriaDescribedBy(String), the former will anyway override the latter, because the beforeClientResponse gets not cancelled.
I guess this is a general issue for all components using the ComponentUtil.resolveOrGenerateIdLater method.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right. I guess we could also sidestep this by setting aria-describedby inside a beforeClientResponse callback, but I'm not sure deferring all the work to these callbacks is a good approach in general.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's keep this also in a separate PR (#25119)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can merge this PR, but then it should be "fixed" in #25127.
Or the other way around, we first look at #25127 and the update this PR.
But if this is block flow-components developments, let's just merge this first.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd appreciate it if this one could be merged first, so I can continue working on the Flow components.

vursen and others added 5 commits August 3, 2026 19:11
Aligns the getters with the Optional<String> return type used by
HasAriaLabel. Also adds a test for setting the description component id
after calling setAriaDescribedBy(Component) and switches the test class
to qualified Assertions calls.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Addresses review feedback asking for attribute purpose references at the
class level. The class javadoc now explains the accessibility purpose
and links the setter methods, while attribute names and spec links stay
in the method javadocs.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@sonarqubecloud

sonarqubecloud Bot commented Aug 4, 2026

Copy link
Copy Markdown

@mcollovati
mcollovati added this pull request to the merge queue Aug 4, 2026
Merged via the queue into main with commit a0e264a Aug 4, 2026
39 checks passed
@mcollovati
mcollovati deleted the feat/has-aria-description branch August 4, 2026 07:25
@knoobie

knoobie commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Asking for a friend.. if no component supports setAriaDescription - why was it added in the first place? Wouldn't it be more sane to have two interfaces, one for HasAriaDescription and one for HasAriaDescribedBy so that the fields only implement the later (which they clearly support?)

@vursen

vursen commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Asking for a friend.. if no component supports setAriaDescription - why was it added in the first place? Wouldn't it be more sane to have two interfaces, one for HasAriaDescription and one for HasAriaDescribedBy so that the fields only implement the later (which they clearly support?)

Huh, that's actually a good point... I think I'm going to open a PR :) thanks!

@vursen

vursen commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Huh, that's actually a good point... I think I'm going to open a PR :) thanks!

On the other hand, these two methods really go hand in hand, and they even interact: one of them can end up shadowing the other. Also, field components could technically support aria-description by creating an invisible node with that text and pointing to it via aria-describedby. It's just out of scope for now.

I'll discuss this with the team.

vursen added a commit that referenced this pull request Aug 5, 2026
Removes setAriaDescription(String) and getAriaDescription() from the
HasAriaDescription interface (added in #25117, unreleased) because
assistive technology support for the aria-description attribute is
still not ideal. The API can be reconsidered later once support
improves.

Also removes the now-unused ARIA_DESCRIPTION_ATTRIBUTE_NAME constant
from ElementConstants.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vursen

vursen commented Aug 5, 2026 •

Copy link
Copy Markdown
Contributor Author

I discussed this with the team, and we decided to drop setAriaDescription from HasAriaDescription for now, since its support isn't ideal anyway. We can come back to this later.

@knoobie

knoobie commented Aug 5, 2026

Copy link
Copy Markdown
Contributor

Thanks for checking with the team!

exijn pushed a commit to exijn/vaadin-flow that referenced this pull request Aug 9, 2026
…#25135)

## Description

Removes `setAriaDescription(String)` and `getAriaDescription()` from the
`HasAriaDescription` interface (added in vaadin#25117, unreleased) because
assistive technology support for the `aria-description` attribute is
still not ideal. The API can be reconsidered later once support
improves.

Part of vaadin/web-components#11975

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@vaadin-bot

Copy link
Copy Markdown
Collaborator

This ticket/PR has been released with Vaadin 25.3.0-alpha8.

Artur- pushed a commit to vaadin/docs that referenced this pull request Sep 15, 2026
## Summary

Vaadin 25.3 added two accessibility mixin interfaces that the *Mixin
Interfaces* page doesn't list, even though that list is where a reader
looks for what a component can implement:

- `vaadin/flow#24782` — `HasAriaRole` (`setAriaRole()` /
`getAriaRole()`)
- `vaadin/flow#25117`, narrowed by `vaadin/flow#25135` —
`HasAriaDescription` (`setAriaDescribedBy(String)`,
`setAriaDescribedBy(Component)`, `getAriaDescribedBy()`)

## What changed

In `articles/flow/component-internals/mixins.adoc`, under *Other Useful
Mixin Interfaces*:

- added `HasAriaDescription` — the accessible description is set by
referencing existing elements with `setAriaDescribedBy()`, and
supplements the accessible name
- added `HasAriaRole` — sets the `role` attribute on the root element by
default, and the methods can be overridden to target another element
- fixed the name of `HasAriaLabel`, which the same list spelled
`HasArialLabel` (the only occurrence in the docs)

## How to test

Documentation only, no code examples added, so nothing to compile. The
descriptions match the javadoc of `HasAriaRole` and `HasAriaDescription`
in 25.3.

Co-authored-by: totally-not-ai[bot] <290682512+totally-not-ai[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants