-
Notifications
You must be signed in to change notification settings - Fork 231
fix(avatar): Avatar component now always includes alt attribute #5512
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'@spectrum-web-components/avatar': minor | ||
--- | ||
|
||
**Fixed** : Avatar component now always includes alt attribute for improved accessibility even when no label is specified |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -139,4 +139,40 @@ import { Avatar } from '@spectrum-web-components/avatar'; | |||||
|
||||||
## Accessibility | ||||||
|
||||||
The `label` attribute of the `<sp-avatar>` will be passed into the `<img>` element as the `alt` tag for use in defining a textual representation of the image displayed. | ||||||
The Avatar component is designed to be accessible by default. To ensure proper accessibility: | ||||||
|
||||||
1. Provide a `label` attribute when the avatar represents a user or has meaningful content: | ||||||
|
||||||
```html | ||||||
<sp-avatar | ||||||
label="Shantanu Narayen" | ||||||
src="https://picsum.photos/500/500" | ||||||
></sp-avatar> | ||||||
``` | ||||||
|
||||||
2. Use the `isdecorative` attribute when the avatar is purely decorative and should be hidden from screen readers: | ||||||
|
||||||
```html | ||||||
<sp-avatar isdecorative src="https://picsum.photos/500/500"></sp-avatar> | ||||||
``` | ||||||
|
||||||
3. If neither `label` nor `isdecorative` is provided, a warning will be logged to the console to help developers identify accessibility issues. | ||||||
|
||||||
### Accessibility Features | ||||||
|
||||||
- When a `label` is provided, it is used as the `alt` text for the image | ||||||
- When `isdecorative` is true, the avatar is hidden from screen readers using `aria-hidden="true"` | ||||||
- The component maintains focus management for keyboard navigation | ||||||
- Color contrast meets WCAG 2.1 Level AA requirements | ||||||
|
||||||
### Accessibility Best Practices | ||||||
|
||||||
- Always provide a `label` for avatars that represent users or have meaningful content | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
- Use `isdecorative` for purely decorative avatars | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
- Avoid using avatars without either a `label` or `isdecorative` attribute | ||||||
- Ensure the avatar image has sufficient contrast with its background | ||||||
- When using avatars in interactive contexts (e.g., as buttons), ensure they have appropriate ARIA roles and labels | ||||||
|
||||||
### Best Practices | ||||||
|
||||||
- Always provide a meaningful `label` |
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -20,9 +20,9 @@ import { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
property, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
query, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} from '@spectrum-web-components/base/src/decorators.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { ifDefined } from '@spectrum-web-components/base/src/directives.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { LikeAnchor } from '@spectrum-web-components/shared/src/like-anchor.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { Focusable } from '@spectrum-web-components/shared/src/focusable.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import { ifDefined } from '@spectrum-web-components/base/src/directives.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
import avatarStyles from './avatar.css.js'; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -48,6 +48,14 @@ export class Avatar extends LikeAnchor(Focusable) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@property() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public src = ''; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* Whether this avatar is decorative and should be hidden from screen readers. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* When true, aria-hidden will be set to true and alt will be empty. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
* When false and no label is provided, a warning will be logged. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
*/ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@property({ type: Boolean, reflect: true }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public isdecorative = false; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: two-word property names are usually camelCase and then surfaced as kebab case attributes, as in I would recommend naming this either with the property and attribute name |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
@property({ type: Number, reflect: true }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
public get size(): AvatarSize { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
return this._size; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -75,7 +83,8 @@ export class Avatar extends LikeAnchor(Focusable) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
const avatar = html` | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
<img | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
class="image" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
alt=${ifDefined(this.label || undefined)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
alt=${ifDefined(this.isdecorative ? '' : this.label)} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
aria-hidden=${this.isdecorative ? 'true' : 'false'} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the avatar is keyboard navigable it should not be hidden from a screenreader.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
src=${this.src} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
`; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
@@ -94,5 +103,16 @@ export class Avatar extends LikeAnchor(Focusable) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!this.hasAttribute('size')) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this.setAttribute('size', `${this.size}`); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
// Log a warning if neither label nor isdecorative is set | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
if (!this.label && !this.isdecorative) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
window.__swc.warn( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
this, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
`<${this.localName}> is missing a label and is not marked as decorative. Either provide a label or set isdecorative="true" for accessibility.`, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
'https://opensource.adobe.com/spectrum-web-components/components/avatar/#accessibility', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
{ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
type: 'accessibility', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+106
to
+116
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.