Docs for visually hidden util, remove visually hidden component per #232

This commit is contained in:
Lea Verou
2024-12-19 21:15:25 -05:00
parent d1b38af039
commit cd172ede8c
12 changed files with 119 additions and 164 deletions

View File

@@ -167,7 +167,7 @@ Create a split button using a button and a dropdown. Use a [visually hidden](/do
<wa-button variant="brand">Save</wa-button>
<wa-dropdown placement="bottom-end">
<wa-button slot="trigger" variant="brand" caret>
<wa-visually-hidden>More options</wa-visually-hidden>
<span class="wa-visually-hidden">More options</span>
</wa-button>
<wa-menu>
<wa-menu-item>Save</wa-menu-item>

View File

@@ -1,46 +0,0 @@
---
title: Visually Hidden
description: The visually hidden utility makes content accessible to assistive devices without displaying it on the screen.
tags: component
noAlpha: true
---
According to [The A11Y Project](https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/), "there are real world situations where visually hiding content may be appropriate, while the content should remain available to assistive technologies, such as screen readers. For instance, hiding a search field's label as a common magnifying glass icon is used in its stead."
Since visually hidden content can receive focus when tabbing, the element will become visible when something inside receives focus. This behavior is intentional, as sighted keyboard user won't be able to determine where the focus indicator is without it.
```html {.example}
<div style="min-height: 1.875rem;">
<wa-visually-hidden>
<a href="#">Skip to main content</a>
</wa-visually-hidden>
</div>
```
## Examples
### Links That Open in New Windows
In this example, the link will open a new window. Screen readers will announce "opens in a new window" even though the text content isn't visible to sighted users.
```html {.example}
<a href="https://example.com/" target="_blank">
Visit External Page
<wa-icon name="arrow-up-right-from-square" variant="regular"></wa-icon>
<wa-visually-hidden>opens in a new window</wa-visually-hidden>
</a>
```
### Content Conveyed By Context
Adding a label may seem redundant at times, but they're very helpful for unsighted users. Rather than omit them, you can provide context to unsighted users with visually hidden content that will be announced by assistive devices such as screen readers.
```html {.example}
<wa-card style="width: 100%; max-width: 360px;">
<header>
<wa-visually-hidden>Personal Info</wa-visually-hidden>
</header>
<wa-input label="Name" style="margin-bottom: .5rem;"></wa-input>
<wa-input label="Email" type="email"></wa-input>
</wa-card>
```

View File

@@ -379,12 +379,8 @@ hasOutline: false
background-color: var(--wa-color-neutral-fill-quiet);
}
/**
<wa-visually-hidden>, but without the :not(:focus-within),
the reason is that it shows the default browser file uploader.
*/
.hidden-label::part(form-control-label),
.file-uploader input {
/** Visually hidden */
.hidden-label::part(form-control-label) {
position: absolute !important;
width: 1px !important;
height: 1px !important;
@@ -522,7 +518,7 @@ hasOutline: false
<wa-icon name="minus" library="fa-classic-regular" slot="collapse-icon"></wa-icon>
<wa-input name="project-name" value="" placeholder="Project name" label="What are you working on?"></wa-input>
<div>
<label class="file-uploader" style="display: block;" aria-describedby="file-uploader-description">
<label class="file-uploader wa-visually-hidden-force" style="display: block;" aria-describedby="file-uploader-description">
<input name="project-logo" type="file" accept="image/*">
Add logo
</label>
@@ -537,7 +533,7 @@ hasOutline: false
<wa-button value="[choose]" appearance="plain" id="icon-chooser-trigger" class="logo-chooser">
<wa-tooltip for="icon-chooser-trigger" distance="-3" hoist>Browse Icons</wa-tooltip>
<wa-icon name="ellipsis" library="fa-classic-regular"></wa-icon>
<wa-visually-hidden>Browse icons</wa-visually-hidden>
<span class="wa-visually-hidden">Browse icons</span>
</wa-button>
<small slot="hint" style="display: inline-block; line-height: var(--wa-line-height-condensed);">It's dangerous to go alone. Take these!</small>
</wa-radio-group>
@@ -2270,17 +2266,17 @@ hasOutline: false
<table style="margin-bottom: 0;">
<thead>
<tr>
<th><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Check all</wa-visually-hidden></wa-checkbox></th>
<th><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><span class="wa-visually-hidden">Check all</span></wa-checkbox></th>
<th>Customer</th>
<th>Conversation</th>
<th>Assigned To</th>
<th style="text-align: center;">Status</th>
<th><wa-visually-hidden>Actions</wa-visually-hidden></th>
<th><span class="wa-visually-hidden">Actions</span></th>
</tr>
</thead>
<tbody>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><span class="wa-visually-hidden">Completed</span></wa-checkbox></td>
<td>Keanu Reeves</td>
<td class="desc"><span style="font-weight: bold">Am I dead?</span><br><span class="excerpt">Okey dokey... free my mind. Right, no problem, free my mind, free my mind, no problem, right...</span></td>
<td><wa-avatar image="/assets/images/themer/avatar-chad.jpg" label="Chad" style="margin-right: var(--wa-space-xs)"></wa-avatar> Chad Stahelski</td>
@@ -2310,7 +2306,7 @@ hasOutline: false
</td>
</tr>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><span class="wa-visually-hidden">Completed</span></wa-checkbox></td>
<td>Lawrence Fishburne</td>
<td class="desc"><span style="font-weight: bold">We have a rule</span><br><span class="excerpt">We never free a mind once it's reached a certain age. It's dangerous, the mind has trouble letting go.</span></td>
<td><wa-avatar image="/assets/images/themer/avatar-char.jpg" label="Char" style="margin-right: var(--wa-space-xs)"></wa-avatar> Char McCoy</td>
@@ -2340,7 +2336,7 @@ hasOutline: false
</td>
</tr>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)" checked><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)" checked><span class="wa-visually-hidden">Completed</span></wa-checkbox></td>
<td>Carrie-Ann Moss</td>
<td class="desc"><span style="font-weight: bold">Was it the same cat?</span><br><span class="excerpt">A déjà vu is usually a glitch in the Matrix. It happens when they change something.</span></td>
<td><wa-avatar initials="DE" label="Avatar with initials: DE" style="margin-right: var(--wa-space-xs)"></wa-avatar> Debbie Evans</td>
@@ -2370,7 +2366,7 @@ hasOutline: false
</td>
</tr>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><span class="wa-visually-hidden">Completed</span></wa-checkbox></td>
<td>Joe Pantoliano</td>
<td class="desc"><span style="font-weight: bold">Ignorance is bliss</span><br><span class="excerpt">Why oh why didn't I take the blue pill?</span></td>
<td></td>
@@ -2400,7 +2396,7 @@ hasOutline: false
</td>
</tr>
<tr>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><wa-visually-hidden>Completed</wa-visually-hidden></wa-checkbox></td>
<td><wa-checkbox size="large" style="padding-left: var(--wa-space-s)"><span class="wa-visually-hidden">Completed</span></wa-checkbox></td>
<td>Hugo Weaving</td>
<td class="desc"><span style="font-weight: bold">I'd like to share a revelation</span><br><span class="excerpt">I need the codes, I have to get inside Zion and you have to tell me how.</span></td>
<td><wa-avatar image="/assets/images/themer/avatar-dara.jpg" label="Dara" style="margin-right: var(--wa-space-xs)"></wa-avatar> Dara Prescott</td>

View File

@@ -31,10 +31,12 @@ During the alpha period, things might break! We take breaking changes very serio
- Added [appearance utilities](/docs/utilities/appearance/)
- Added [size utilities](/docs/utilities/size/)
- Added [layout utilities](/docs/layout/#utilities)
- Added [visually hidden](/docs/utilities/a11y/#visually-hidden) utility
- Added [`.wa-visually hidden`](/docs/utilities/a11y/#visually-hidden) utility
- Added [`<wa-page>`](/docs/components/page/#styles) native styles and utilities
### Components
- Removed `<wa-visually-hidden>` in favor of the utility class
- `<wa-page>`: `mobile-breakpoint` now takes any CSS length, not just pixels
- Added `checked` and `disabled` custom states to `<wa-checkbox>` and `<wa-radio>`
- Added `disabled`, `expanded`, `indeterminate`, and `selected` custom states to `<wa-tree-item>`

View File

@@ -0,0 +1,96 @@
---
title: Visually Hidden
description: The visually hidden utility makes content accessible to assistive devices without displaying it on the screen.
noAlpha: true
---
`.wa-visually-hidden` `.wa-visually-hidden-force`
> "there are real world situations where visually hiding content may be appropriate, while the content should remain available to assistive technologies, such as screen readers. For instance, hiding a search field's label as a common magnifying glass icon is used in its stead."
> — [The A11Y Project](https://www.a11yproject.com/posts/2013-01-11-how-to-hide-content/)
Since visually hidden content can receive focus when tabbing, the element will become visible when something inside receives focus.
This behavior is intentional, as otherwise sighted keyboard users wouldn't be able to determine where the focus indicator is.
```html {.example}
<div style="min-height: 1.875rem;">
<a href="#" class="wa-visually-hidden">Skip to main content</a>
</div>
```
## Examples
### Links That Open in New Windows
In this example, the link will open a new window. Screen readers will announce "opens in a new window" even though the text content isn't visible to sighted users.
```html {.example}
<a href="https://example.com/" target="_blank">
Visit External Page
<wa-icon name="arrow-up-right-from-square" variant="regular"></wa-icon>
<span class="wa-visually-hidden">opens in a new window</span>
</a>
```
### Content Conveyed By Context
Adding a label may seem redundant at times, but they're very helpful for unsighted users. Rather than omit them, you can provide context to unsighted users with visually hidden content that will be announced by assistive devices such as screen readers.
```html {.example}
<wa-card style="width: 100%; max-width: 360px;">
<header class="wa-visually-hidden">
Personal Info
</header>
<wa-input label="Name" style="margin-bottom: .5rem;"></wa-input>
<wa-input label="Email" type="email"></wa-input>
</wa-card>
```
### Force visually hidden
There are cases where you want to _always_ visually hide certain content, even when it's focused.
For example when hiding a checkbox to render a custom one:
```html {.example}
<label>
<span class="checkbox">
<input type="checkbox" class="wa-visually-hidden-force" />
</span>
I have read the terms and conditions
</label>
<style>
.checkbox {
display: inline-flex;
vertical-align: middle;
width: var(--wa-font-size-l);
height: var(--wa-font-size-l);
background: var(--wa-color-neutral-fill-quiet);
color: var(--wa-color-neutral-on-quiet);
border-radius: var(--wa-border-radius-xs);
margin-inline-end: var(--wa-space-xs);
&::after {
content: "✓" / "";
margin: auto;
transition: opacity var(--wa-transition-slow) var(--wa-transition-easing);
}
&:has(:checked) {
background: var(--wa-color-brand-fill-loud);
color: var(--wa-color-brand-on-loud);
}
&:not(:has(:checked)) {
&::after {
opacity: 0;
}
}
&:focus-within {
outline: var(--wa-focus-ring);
outline-offset: var(--wa-focus-ring-offset);
}
}
</style>
```

View File

@@ -18,6 +18,7 @@ import { watch } from '../../internal/watch.js';
import { WebAwesomeFormAssociatedElement } from '../../internal/webawesome-element.js';
import formControlStyles from '../../styles/shadow/form-control.css';
import sizeStyles from '../../styles/utilities/size.css';
import visuallyHidden from '../../styles/utilities/visually-hidden.css';
import { LocalizeController } from '../../utilities/localize.js';
import '../button-group/button-group.js';
import '../button/button.js';
@@ -26,7 +27,6 @@ import type WaDropdown from '../dropdown/dropdown.js';
import '../icon/icon.js';
import '../input/input.js';
import type WaInput from '../input/input.js';
import '../visually-hidden/visually-hidden.js';
import styles from './color-picker.css';
interface EyeDropperConstructor {
@@ -106,7 +106,7 @@ declare const EyeDropper: EyeDropperConstructor;
*/
@customElement('wa-color-picker')
export default class WaColorPicker extends WebAwesomeFormAssociatedElement {
static shadowStyle = [sizeStyles, formControlStyles, styles];
static shadowStyle = [visuallyHidden, sizeStyles, formControlStyles, styles];
static shadowRootOptions = { ...WebAwesomeFormAssociatedElement.shadowRootOptions, delegatesFocus: true };

View File

@@ -5,11 +5,11 @@ import { WaCopyEvent } from '../../events/copy.js';
import { WaErrorEvent } from '../../events/error.js';
import { animateWithClass } from '../../internal/animate.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import visuallyHidden from '../../styles/utilities/visually-hidden.css';
import { LocalizeController } from '../../utilities/localize.js';
import '../icon/icon.js';
import '../tooltip/tooltip.js';
import type WaTooltip from '../tooltip/tooltip.js';
import '../visually-hidden/visually-hidden.js';
import styles from './copy-button.css';
/**
@@ -44,7 +44,7 @@ import styles from './copy-button.css';
*/
@customElement('wa-copy-button')
export default class WaCopyButton extends WebAwesomeElement {
static shadowStyle = styles;
static shadowStyle = [visuallyHidden, styles];
private readonly localize = new LocalizeController(this);
@@ -201,7 +201,7 @@ export default class WaCopyButton extends WebAwesomeElement {
@click=${this.handleCopy}
>
<!-- Render a visually hidden label to appease the accessibility checking gods -->
<wa-visually-hidden>${this.currentLabel}</wa-visually-hidden>
<span class="wa-visually-hidden">${this.currentLabel}</span>
<slot part="copy-icon" name="copy-icon">
<wa-icon library="system" name="copy" variant="regular" fixed-width></wa-icon>
</slot>

View File

@@ -195,19 +195,6 @@ slot:not([name]) {
grid-area: main-footer;
}
/* Visually hidden */
.skip-to-content:not(:focus-within) {
position: absolute !important;
width: 1px !important;
height: 1px !important;
clip: rect(0 0 0 0) !important;
clip-path: inset(50%) !important;
border: none !important;
overflow: hidden !important;
white-space: nowrap !important;
padding: 0 !important;
}
.skip-to-content {
position: absolute;
top: var(--wa-space-m);

View File

@@ -4,6 +4,7 @@ import { live } from 'lit/directives/live.js';
import { unsafeHTML } from 'lit/directives/unsafe-html.js';
import { toLength, toPx } from '../../internal/css-values.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import visuallyHidden from '../../styles/utilities/visually-hidden.css';
import styles from './page.css';
import mobileStyles from './page.mobile.styles.js';
@@ -78,7 +79,7 @@ if (typeof ResizeObserver === 'undefined') {
*/
@customElement('wa-page')
export default class WaPage extends WebAwesomeElement {
static shadowStyle = styles;
static shadowStyle = [visuallyHidden, styles];
private headerResizeObserver = this.slotResizeObserver('header');
private subheaderResizeObserver = this.slotResizeObserver('subheader');
@@ -262,7 +263,7 @@ export default class WaPage extends WebAwesomeElement {
render() {
return html`
<a href="#main-content" part="skip-to-content" class="skip-to-content">
<a href="#main-content" part="skip-to-content" class="wa-visually-hidden">
<slot name="skip-to-content">Skip to content</slot>
</a>

View File

@@ -1,11 +0,0 @@
:host(:not(:focus-within)) {
position: absolute !important;
width: 1px !important;
height: 1px !important;
clip: rect(0 0 0 0) !important;
clip-path: inset(50%) !important;
border: none !important;
overflow: hidden !important;
white-space: nowrap !important;
padding: 0 !important;
}

View File

@@ -1,43 +0,0 @@
import { expect } from '@open-wc/testing';
import { html } from 'lit';
import { fixtures } from '../../internal/test/fixture.js';
describe('<wa-visually-hidden>', () => {
for (const fixture of fixtures) {
describe(`with "${fixture.type}" rendering`, () => {
it('should render but not display visually hidden content', async () => {
const el = await fixture(html`
<wa-visually-hidden>
<a href="#">Skip to main content</a>
</wa-visually-hidden>
`);
const { width, height, overflow, clipPath } = getComputedStyle(el);
expect(width).to.equal('1px');
expect(height).to.equal('1px');
expect(overflow).to.equal('hidden');
expect(clipPath).to.equal('inset(50%)');
});
// should show visually hidden content when focused
it('should show visually hidden content when focused', async () => {
const el = await fixture(html`
<wa-visually-hidden>
<a href="#">Skip to main content</a>
</wa-visually-hidden>
`);
const a = el.querySelector('a')!;
a.focus();
const { width, height, overflow, clipPath } = getComputedStyle(el);
expect(width).not.to.equal('1px');
expect(height).not.to.equal('1px');
expect(overflow).not.to.equal('hidden');
expect(clipPath).not.to.equal('inset(50%)');
});
});
}
});

View File

@@ -1,27 +0,0 @@
import { html } from 'lit';
import { customElement } from 'lit/decorators.js';
import WebAwesomeElement from '../../internal/webawesome-element.js';
import styles from './visually-hidden.css';
/**
* @summary The visually hidden utility makes content accessible to assistive devices without displaying it on the screen.
* @documentation https://backers.webawesome.com/docs/components/visually-hidden
* @status stable
* @since 2.0
*
* @slot - The content to be visually hidden.
*/
@customElement('wa-visually-hidden')
export default class WaVisuallyHidden extends WebAwesomeElement {
static shadowStyle = styles;
render() {
return html` <slot></slot> `;
}
}
declare global {
interface HTMLElementTagNameMap {
'wa-visually-hidden': WaVisuallyHidden;
}
}