Rewrote most of the card.
- A common cause of non-accessible web pages are elements that were made interactive via JavaScript but cannot be focused or activated with anything but the mouse.
-## Bad- +## ❌ Bad example
- Let's take a look at a common example:
- ```html
-<div filter>- <input filter--query="true" id="query" name="query" type="text">- <label filter--reset="true">Clear Search</label>-</div>- +<form filter>
- + <input filter--query name="query" type="text">
- + <span filter--reset>Clear Search</span>
- +</form>
- ```
- The HTML above is being activated with an [Unpoly compiler](https://unpoly.com/up.compiler) like this:
- ```js
- up.compiler('[filter]', function(filterForm) {
- const resetButton = filterForm.querySelector('[filter--reset]')
- const queryInput = filterForm.querySelector('[filter--query]')
- function resetQuery() {
- queryInput.value = ''
- queryInput.focus()
- }
- up.on('click', resetQuery)
- })
- ```
-You can only use the "clear query field" shortcut by clicking on the "Clear Search" label with a mouse.- +The *Clear search* button has three issues:
-## Good- +- It cannot be focused with the keyboard
- +- It cannot be pressed with the keyboard
- +- A screen reader will not announce it as a button
-It's super easy to make this feature accessible: add the `tabindex` attribute to the element that usually receives the `click` event - it is now focusable with keyboards as well. Then, add a second listener for the `keydown` event, it should do exactly the same thing when pressing `<Space>`:- +
- +## ✔️ Good example
- +
- +To make the reset button focusable with the keyboard, add the `[tabindex]` attribute to the element that usually receives the `click` event.
- +Also set an `[role=button]` attribute so screen readers will announce it as a "button" when focused:
- ```html
-<div filter>- <input filter--query="true" id="query" name="query" type="text">- <label filter--reset="true" tabindex="0">Clear Search</label> <!-- now with a tabindex -->-</div>- +<form filter>
- + <input filter--query name="query" type="text">
- + <span filter--reset tabindex="0" role="button">Clear Search</span>
- +</form>
- ```
-The HTML above is being activated with an [Unpoly compiler](https://unpoly.com/up.compiler) like this:- +Now change your [Unpoly compiler](https://unpoly.com/up.compiler) to activate the button on `keydown` in addition to `click`:
- ```js
- up.compiler('[filter]', function(filterForm) {
- const resetButton = filterForm.querySelector('[filter--reset]')
- const queryInput = filterForm.querySelector('[filter--query]')
- function resetQuery() {- + function resetQuery(event) {
- + evt.preventDefault()
- +
- queryInput.value = ''
- queryInput.focus()
- }
- up.on('click', resetQuery)
- up.on('keydown', (evt) => {
- // ignore any other keys like tabbing or pressing shift etc.
- if (evt.code === 'Space') {
- evt.preventDefault() // prevent scrolling down on the page- resetQuery()
- }
- })
- })
- ```
-## Note-If you are listening on the `click` event on links, buttons or other default interactive elements you don't have to change anything. In fact, the best way to ensure accessibility is to use only elements with a corresponding semantic meaning. - +## Links and buttons do not need additional code
- +
- +Elements that are interactive by default, such as an `<a>` or `<button>`, do not need additional code:
- +
- +- They can be focused without an additional `[tabindex]` attribute
- +- They can be activated with the keyboard without custom JavaScript. They will emit a `click` event after keyboard activation.
- +
- +By using a `<button>` we can reduce the example to this:
- +
- +```html
- +<form filter>
- + <input filter--query name="query" type="text">
- + <button type="button" filter--reset>Clear Search</button>
- +</form>
- +```
- +
- +## Shorter solution with `[up-clickable]`
- +
- +If you cannot use an element like `<button>`, you may use the `[up-clickable]` attribute to make elements interactive. Elements with this attribute gain the following behavior:
- +
- +- The element is given an [`[role=link]`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/link_role)
- + attribute so screen readers announce it as link.
- +- The element shows [`pointer`](https://developer.mozilla.org/en-US/docs/Web/CSS/cursor) cursor when hovered over.
- +- The element can be focused with the keyboard.
- +- The element emits an `up:click` event when activated.
- +- You may also assign an [`[up-instant]`](https://unpoly.com/a-up-instant) attribute to make the element activate on `mousedown` instead of `click`.
- +
- +This reduces the code to the following:
- +
- +```html
- +<form filter>
- + <input filter--query name="query" type="text">
- + <span filter--reset up-clickable>Clear Search</span>
- +</form>
- +```
- +
- +```js
- +up.compiler('[filter]', function(filterForm) {
- + const resetButton = filterForm.querySelector('[filter--reset]')
- + const queryInput = filterForm.querySelector('[filter--query]')
- +
- + function resetQuery(event) {
- + event.preventDefault()
- + queryInput.value = ''
- + queryInput.focus()
- + }
- +
- + up.on('up:click', resetQuery)
- +})
- +```
- +
- +