kopia lustrzana https://github.com/shoelace-style/shoelace
a11y improvements; fixes #579
rodzic
a923d1effc
commit
0135a37af8
|
@ -4,15 +4,17 @@
|
||||||
|
|
||||||
Avatars are used to represent a person or object.
|
Avatars are used to represent a person or object.
|
||||||
|
|
||||||
|
Like [images](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/img), you should always provide `alt` text for avatars as alternate text for assistive devices.
|
||||||
|
|
||||||
```html preview
|
```html preview
|
||||||
<sl-avatar></sl-avatar>
|
<sl-avatar alt="User avatar"></sl-avatar>
|
||||||
```
|
```
|
||||||
|
|
||||||
```jsx react
|
```jsx react
|
||||||
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
|
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
|
||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<SlAvatar>Button</SlAvatar>
|
<SlAvatar alt="User avatar" />
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -25,7 +27,7 @@ To use an image for the avatar, set the `image` and `alt` attributes. This will
|
||||||
```html preview
|
```html preview
|
||||||
<sl-avatar
|
<sl-avatar
|
||||||
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
|
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
|
||||||
alt="Gray tabby kitten looking down"
|
alt="Avatar of a gray tabby kitten looking down"
|
||||||
></sl-avatar>
|
></sl-avatar>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -35,7 +37,7 @@ import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<SlAvatar
|
<SlAvatar
|
||||||
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
|
image="https://images.unsplash.com/photo-1529778873920-4da4926a72c2?ixlib=rb-1.2.1&auto=format&fit=crop&w=300&q=80"
|
||||||
alt="Gray tabby kitten looking down"
|
alt="Avatar of a gray tabby kitten looking down"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
@ -45,14 +47,14 @@ const App = () => (
|
||||||
When you don't have an image to use, you can set the `initials` attribute to show something more personalized than an icon.
|
When you don't have an image to use, you can set the `initials` attribute to show something more personalized than an icon.
|
||||||
|
|
||||||
```html preview
|
```html preview
|
||||||
<sl-avatar initials="SL"></sl-avatar>
|
<sl-avatar initials="SL" alt="Avatar with initials: SL"></sl-avatar>
|
||||||
```
|
```
|
||||||
|
|
||||||
```jsx react
|
```jsx react
|
||||||
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
|
import { SlAvatar } from '@shoelace-style/shoelace/dist/react';
|
||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<SlAvatar initials="SL" />
|
<SlAvatar initials="SL" alt="Avatar with initials: SL" />
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -61,15 +63,15 @@ const App = () => (
|
||||||
When no image or initials are set, an icon will be shown. The default avatar shows a generic "user" icon, but you can customize this with the `icon` slot.
|
When no image or initials are set, an icon will be shown. The default avatar shows a generic "user" icon, but you can customize this with the `icon` slot.
|
||||||
|
|
||||||
```html preview
|
```html preview
|
||||||
<sl-avatar>
|
<sl-avatar alt="Avatar with an image icon">
|
||||||
<sl-icon slot="icon" name="image"></sl-icon>
|
<sl-icon slot="icon" name="image"></sl-icon>
|
||||||
</sl-avatar>
|
</sl-avatar>
|
||||||
|
|
||||||
<sl-avatar>
|
<sl-avatar alt="Avatar with an archive icon">
|
||||||
<sl-icon slot="icon" name="archive"></sl-icon>
|
<sl-icon slot="icon" name="archive"></sl-icon>
|
||||||
</sl-avatar>
|
</sl-avatar>
|
||||||
|
|
||||||
<sl-avatar>
|
<sl-avatar alt="Avatar with a briefcase icon">
|
||||||
<sl-icon slot="icon" name="briefcase"></sl-icon>
|
<sl-icon slot="icon" name="briefcase"></sl-icon>
|
||||||
</sl-avatar>
|
</sl-avatar>
|
||||||
```
|
```
|
||||||
|
@ -79,15 +81,15 @@ import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<>
|
<>
|
||||||
<SlAvatar>
|
<SlAvatar alt="Avatar with an image icon">
|
||||||
<SlIcon slot="icon" name="image" />
|
<SlIcon slot="icon" name="image" />
|
||||||
</SlAvatar>
|
</SlAvatar>
|
||||||
|
|
||||||
<SlAvatar>
|
<SlAvatar alt="Avatar with an archive icon">
|
||||||
<SlIcon slot="icon" name="archive" />
|
<SlIcon slot="icon" name="archive" />
|
||||||
</SlAvatar>
|
</SlAvatar>
|
||||||
|
|
||||||
<SlAvatar>
|
<SlAvatar alt="Avatar with a briefcase icon">
|
||||||
<SlIcon slot="icon" name="briefcase" />
|
<SlIcon slot="icon" name="briefcase" />
|
||||||
</SlAvatar>
|
</SlAvatar>
|
||||||
</>
|
</>
|
||||||
|
@ -99,9 +101,9 @@ const App = () => (
|
||||||
Avatars can be shaped using the `shape` attribute.
|
Avatars can be shaped using the `shape` attribute.
|
||||||
|
|
||||||
```html preview
|
```html preview
|
||||||
<sl-avatar shape="square"></sl-avatar>
|
<sl-avatar shape="square" alt="Square avatar"></sl-avatar>
|
||||||
<sl-avatar shape="rounded"></sl-avatar>
|
<sl-avatar shape="rounded" alt="Rounded avatar"></sl-avatar>
|
||||||
<sl-avatar shape="circle"></sl-avatar>
|
<sl-avatar shape="circle" alt="Circle avatar"></sl-avatar>
|
||||||
```
|
```
|
||||||
|
|
||||||
```jsx react
|
```jsx react
|
||||||
|
@ -109,9 +111,9 @@ import { SlAvatar, SlIcon } from '@shoelace-style/shoelace/dist/react';
|
||||||
|
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<>
|
<>
|
||||||
<SlAvatar shape="square" />
|
<SlAvatar shape="square" alt="Square avatar" />
|
||||||
<SlAvatar shape="rounded" />
|
<SlAvatar shape="rounded" alt="Rounded avatar" />
|
||||||
<SlAvatar shape="circle" />
|
<SlAvatar shape="circle" alt="Circle avatar" />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
```
|
```
|
||||||
|
@ -122,10 +124,25 @@ You can group avatars with a few lines of CSS.
|
||||||
|
|
||||||
```html preview
|
```html preview
|
||||||
<div class="avatar-group">
|
<div class="avatar-group">
|
||||||
<sl-avatar image="https://images.unsplash.com/photo-1490150028299-bf57d78394e0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80&crop=right"></sl-avatar>
|
<sl-avatar
|
||||||
<sl-avatar image="https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"></sl-avatar>
|
image="https://images.unsplash.com/photo-1490150028299-bf57d78394e0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80&crop=right"
|
||||||
<sl-avatar image="https://images.unsplash.com/photo-1456439663599-95b042d50252?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"></sl-avatar>
|
alt="Avatar 1 of 4"
|
||||||
<sl-avatar image="https://images.unsplash.com/flagged/photo-1554078875-e37cb8b0e27d?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=top&q=80"></sl-avatar>
|
></sl-avatar>
|
||||||
|
|
||||||
|
<sl-avatar
|
||||||
|
image="https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
|
||||||
|
alt="Avatar 2 of 4"
|
||||||
|
></sl-avatar>
|
||||||
|
|
||||||
|
<sl-avatar
|
||||||
|
image="https://images.unsplash.com/photo-1456439663599-95b042d50252?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
|
||||||
|
alt="Avatar 3 of 4"
|
||||||
|
></sl-avatar>
|
||||||
|
|
||||||
|
<sl-avatar
|
||||||
|
image="https://images.unsplash.com/flagged/photo-1554078875-e37cb8b0e27d?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=top&q=80"
|
||||||
|
alt="Avatar 4 of 4"
|
||||||
|
></sl-avatar>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
@ -155,10 +172,25 @@ const css = `
|
||||||
const App = () => (
|
const App = () => (
|
||||||
<>
|
<>
|
||||||
<div className="avatar-group">
|
<div className="avatar-group">
|
||||||
<SlAvatar image="https://images.unsplash.com/photo-1490150028299-bf57d78394e0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80&crop=right" />
|
<SlAvatar
|
||||||
<SlAvatar image="https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80" />
|
image="https://images.unsplash.com/photo-1490150028299-bf57d78394e0?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&q=80&crop=right"
|
||||||
<SlAvatar image="https://images.unsplash.com/photo-1456439663599-95b042d50252?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80" />
|
alt="Avatar 1 of 4"
|
||||||
<SlAvatar image="https://images.unsplash.com/flagged/photo-1554078875-e37cb8b0e27d?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=top&q=80" />
|
/>
|
||||||
|
|
||||||
|
<SlAvatar
|
||||||
|
image="https://images.unsplash.com/photo-1503454537195-1dcabb73ffb9?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
|
||||||
|
alt="Avatar 2 of 4"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SlAvatar
|
||||||
|
image="https://images.unsplash.com/photo-1456439663599-95b042d50252?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=left&q=80"
|
||||||
|
alt="Avatar 3 of 4"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SlAvatar
|
||||||
|
image="https://images.unsplash.com/flagged/photo-1554078875-e37cb8b0e27d?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=256&h=256&crop=top&q=80"
|
||||||
|
alt="Avatar 4 of 4"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<style>{css}</style>
|
<style>{css}</style>
|
||||||
|
|
|
@ -8,10 +8,7 @@
|
||||||
name="description"
|
name="description"
|
||||||
content="Shoelace provides a collection of professionally designed, every day UI components built on a framework-agnostic technology."
|
content="Shoelace provides a collection of professionally designed, every day UI components built on a framework-agnostic technology."
|
||||||
/>
|
/>
|
||||||
<meta
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
name="viewport"
|
|
||||||
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"
|
|
||||||
/>
|
|
||||||
<meta name="custom-elements-manifest" content="dist/custom-elements.json" />
|
<meta name="custom-elements-manifest" content="dist/custom-elements.json" />
|
||||||
<meta property="og:title" content="Shoelace" />
|
<meta property="og:title" content="Shoelace" />
|
||||||
<meta
|
<meta
|
||||||
|
|
|
@ -8,6 +8,10 @@ _During the beta period, these restrictions may be relaxed in the event of a mis
|
||||||
|
|
||||||
## Next
|
## Next
|
||||||
|
|
||||||
|
- Improved a11y of `<sl-avatar>` by representing it as an image with an `alt` [#579](https://github.com/shoelace-style/shoelace/issues/579)
|
||||||
|
- Improved a11y of the scroll buttons in `<sl-tab-group>`
|
||||||
|
- Improved a11y of the close button in `<sl-tab>`
|
||||||
|
- Improved a11y of `<sl-tab-panel>` by removing an invalid `aria-selected` attribute [#579](https://github.com/shoelace-style/shoelace/issues/579)
|
||||||
- Moved `role` from the shadow root to the host element in `<sl-menu>`
|
- Moved `role` from the shadow root to the host element in `<sl-menu>`
|
||||||
- Removed redundant `role="menu"` in `<sl-dropdown>`
|
- Removed redundant `role="menu"` in `<sl-dropdown>`
|
||||||
- Slightly faster animations for showing and hiding `<sl-dropdown>`
|
- Slightly faster animations for showing and hiding `<sl-dropdown>`
|
||||||
|
|
|
@ -8,7 +8,7 @@ describe('<sl-avatar>', () => {
|
||||||
|
|
||||||
describe('when provided no parameters', async () => {
|
describe('when provided no parameters', async () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
el = await fixture<SlAvatar>(html` <sl-avatar></sl-avatar> `);
|
el = await fixture<SlAvatar>(html` <sl-avatar alt="Avatar"></sl-avatar> `);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes accessibility test', async () => {
|
it('passes accessibility test', async () => {
|
||||||
|
@ -71,7 +71,7 @@ describe('<sl-avatar>', () => {
|
||||||
describe('when provided initials parameter', async () => {
|
describe('when provided initials parameter', async () => {
|
||||||
const initials = 'SL';
|
const initials = 'SL';
|
||||||
before(async () => {
|
before(async () => {
|
||||||
el = await fixture<SlAvatar>(html`<sl-avatar initials="${initials}"></sl-avatar>`);
|
el = await fixture<SlAvatar>(html`<sl-avatar initials="${initials}" alt="Avatar"></sl-avatar>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes accessibility test', async () => {
|
it('passes accessibility test', async () => {
|
||||||
|
@ -88,7 +88,7 @@ describe('<sl-avatar>', () => {
|
||||||
['square', 'rounded', 'circle'].forEach(shape => {
|
['square', 'rounded', 'circle'].forEach(shape => {
|
||||||
describe(`when passed a shape attribute ${shape}`, () => {
|
describe(`when passed a shape attribute ${shape}`, () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
el = await fixture<SlAvatar>(html`<sl-avatar shape="${shape}"></sl-avatar>`);
|
el = await fixture<SlAvatar>(html`<sl-avatar shape="${shape as any}" alt="Shaped avatar"></sl-avatar>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes accessibility test', async () => {
|
it('passes accessibility test', async () => {
|
||||||
|
@ -106,7 +106,7 @@ describe('<sl-avatar>', () => {
|
||||||
|
|
||||||
describe('when passed a <span>, on slot "icon"', async () => {
|
describe('when passed a <span>, on slot "icon"', async () => {
|
||||||
before(async () => {
|
before(async () => {
|
||||||
el = await fixture<SlAvatar>(html`<sl-avatar><span slot="icon">random content</span></sl-avatar>`);
|
el = await fixture<SlAvatar>(html`<sl-avatar alt="Avatar"><span slot="icon">random content</span></sl-avatar>`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('passes accessibility test', async () => {
|
it('passes accessibility test', async () => {
|
||||||
|
|
|
@ -48,12 +48,13 @@ export default class SlAvatar extends LitElement {
|
||||||
'avatar--rounded': this.shape === 'rounded',
|
'avatar--rounded': this.shape === 'rounded',
|
||||||
'avatar--square': this.shape === 'square'
|
'avatar--square': this.shape === 'square'
|
||||||
})}
|
})}
|
||||||
|
role="img"
|
||||||
aria-label=${this.alt}
|
aria-label=${this.alt}
|
||||||
>
|
>
|
||||||
${this.initials
|
${this.initials
|
||||||
? html` <div part="initials" class="avatar__initials">${this.initials}</div> `
|
? html` <div part="initials" class="avatar__initials">${this.initials}</div> `
|
||||||
: html`
|
: html`
|
||||||
<div part="icon" class="avatar__icon">
|
<div part="icon" class="avatar__icon" aria-hidden="true">
|
||||||
<slot name="icon">
|
<slot name="icon">
|
||||||
<sl-icon name="person-fill" library="system"></sl-icon>
|
<sl-icon name="person-fill" library="system"></sl-icon>
|
||||||
</slot>
|
</slot>
|
||||||
|
|
|
@ -339,6 +339,7 @@ export default class SlTabGroup extends LitElement {
|
||||||
this.syncIndicator();
|
this.syncIndicator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO - i18n scroll to start/end labels
|
||||||
render() {
|
render() {
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
|
@ -362,6 +363,7 @@ export default class SlTabGroup extends LitElement {
|
||||||
exportparts="base:scroll-button"
|
exportparts="base:scroll-button"
|
||||||
name="chevron-left"
|
name="chevron-left"
|
||||||
library="system"
|
library="system"
|
||||||
|
label="Scroll to start"
|
||||||
@click=${this.handleScrollToStart}
|
@click=${this.handleScrollToStart}
|
||||||
></sl-icon-button>
|
></sl-icon-button>
|
||||||
`
|
`
|
||||||
|
@ -381,6 +383,7 @@ export default class SlTabGroup extends LitElement {
|
||||||
exportparts="base:scroll-button"
|
exportparts="base:scroll-button"
|
||||||
name="chevron-right"
|
name="chevron-right"
|
||||||
library="system"
|
library="system"
|
||||||
|
label="Scroll to end"
|
||||||
@click=${this.handleScrollToEnd}
|
@click=${this.handleScrollToEnd}
|
||||||
></sl-icon-button>
|
></sl-icon-button>
|
||||||
`
|
`
|
||||||
|
|
|
@ -35,13 +35,7 @@ export default class SlTabPanel extends LitElement {
|
||||||
this.style.display = this.active ? 'block' : 'none';
|
this.style.display = this.active ? 'block' : 'none';
|
||||||
|
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div part="base" class="tab-panel" role="tabpanel" aria-hidden=${this.active ? 'false' : 'true'}>
|
||||||
part="base"
|
|
||||||
class="tab-panel"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-selected=${this.active ? 'true' : 'false'}
|
|
||||||
aria-hidden=${this.active ? 'false' : 'true'}
|
|
||||||
>
|
|
||||||
<slot></slot>
|
<slot></slot>
|
||||||
</div>
|
</div>
|
||||||
`;
|
`;
|
||||||
|
|
|
@ -59,6 +59,7 @@ export default class SlTab extends LitElement {
|
||||||
// If the user didn't provide an ID, we'll set one so we can link tabs and tab panels with aria labels
|
// If the user didn't provide an ID, we'll set one so we can link tabs and tab panels with aria labels
|
||||||
this.id = this.id || this.componentId;
|
this.id = this.id || this.componentId;
|
||||||
|
|
||||||
|
// TODO - i18n close label
|
||||||
return html`
|
return html`
|
||||||
<div
|
<div
|
||||||
part="base"
|
part="base"
|
||||||
|
@ -79,11 +80,11 @@ export default class SlTab extends LitElement {
|
||||||
<sl-icon-button
|
<sl-icon-button
|
||||||
name="x"
|
name="x"
|
||||||
library="system"
|
library="system"
|
||||||
|
label="Close"
|
||||||
exportparts="base:close-button"
|
exportparts="base:close-button"
|
||||||
class="tab__close-button"
|
class="tab__close-button"
|
||||||
@click=${this.handleCloseClick}
|
@click=${this.handleCloseClick}
|
||||||
tabindex="-1"
|
tabindex="-1"
|
||||||
aria-hidden="true"
|
|
||||||
></sl-icon-button>
|
></sl-icon-button>
|
||||||
`
|
`
|
||||||
: ''}
|
: ''}
|
||||||
|
|
Ładowanie…
Reference in New Issue