Accessible Properties Cheatsheet

I want to hide an element

When hiding content, first ask: to whom do I want to show the content?

  • Do we want to show it to everyone? πŸ‘‚βœ… πŸ‘βœ…
  • Do we want to hide it from everyone? πŸ‘‚βŒ πŸ‘βŒ
  • Do we want an audible affordance? πŸ‘‚βœ… πŸ‘βŒ β€” let screen reader users hear it, yet hide it from sighted users?
  • Do we want a visual affordance? πŸ‘‚βŒ πŸ‘βœ… β€” let sighted users see it, yet hide it from screen reader users?

Hidden to everyone πŸ‘‚βŒ πŸ‘βŒ

To hide from everyone, add the hidden attribute.

Test

it("is hidden", () => {
  expect(screen.queryByRole('link', { name: 'Profile' })).toBeNull();
})

Markup

<a href="/profile" hidden>Profile</a>

Styling

[hidden] {
  display: none !important;
}

Assistive technology affordance (visually hidden) πŸ‘‚βœ… πŸ‘βŒ

To show to people using assistive technology such as screen readers, yet hide from visual users, define and use a visually-hidden class.

Test

it("has text that can still be read by tests and by screen readers", () => {
  const link = screen.getByRole('link', { name: 'Profile' });
  expect(link).toBeInTheDocument();
})

Markup

<a href="/profile">
  <span class="icon-user"></span> <!-- e.g. πŸ‘€ -->
  <span class="visually-hidden">Profile</span>
</a>

Styling

.visually-hidden {
  position: absolute;
  overflow: hidden;
  clip: rect(0 0 0 0);
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  border: 0;
}

Visual affordance (hidden to assistive technology) πŸ‘‚βŒ πŸ‘βœ…

To show visually but hide to assistive technology, add the aria-hidden attribute.

Test

it("does not include emoji in accessible name", () => {
  expect(
    screen.getByRole('link', { name: 'Profile' })
  ).toBeVisible();
})

Markup

<a href="/profile">
  <span aria-hidden=true>πŸ‘€</span>
  Profile
</a>

Notes


I want to say this element is current

Current page

Imagine a navigation bar full of links. If you visit one of these links, a popular pattern is to differentiate the current page’s link visually. For example, it might be underlined.

To hide to everyone, add the hidden attribute.

Test

it("is current page", () => {
  const link = screen.getByRole('link', { name: 'Pricing' });
  expect(link).toHaveAttribute('aria-current', 'page');
})

Markup

<a href="/features">Features</a>
<a href="/pricing" aria-current=page>Pricing</a>
<a href="/terms">Terms</a>

Styling

a {
  border-bottom: 2px solid transparent;
}
a[aria-current=page] {
  border-bottom-color: pink;
}

Notes


I want to say this element is selected

  • Selected tab in a list

I want to say this element is invalid

Invalid email field

Test

it("is invalid", () => {
  const emailField = screen.getByRole('textbox', { name: 'Email' });
  expect(emailField).toBeInvalid();
})

it("shows error message", () => {
  const emailField = screen.getByRole('textbox', { name: 'Email' });
  expect(emailField).toHaveAccessibleDescription('Email must be a valid address');
})

Markup

<label for=email-field>Email</label>
<input
  id=email-field
  type=email
  name=email
  aria-invalid
  aria-describedby=email-error
>
<p id=email-error>Email must be a valid address</p>

I want to say this element is required

Required form field

Test

it("is required", () => {
  const emailField = screen.getByRole('textbox', { name: 'Email' })
  expect(emailField).toBeRequired();
})

Markup

<label>
  Email
  <input type=email name=email required>
</label>

I want to disable an element

Read-only form field

Test

it("is disabled", () => {
  const emailField = screen.getByRole('textbox', { name: 'Email' });
  expect(emailField).toBeDisabled();
})

Markup

<label>
  Email
  <input type=email name=email disabled>
</label>