Focus
Interactive elements, including form controls, links, and buttons, are by default focusable and tabbable. Tabbable elements are part of the document’s sequential focus navigation order. Other elements are inert, meaning they are not interactive. With HTML attributes, it’s possible to make interactive elements inert and to make inert elements interactive.
| Note: For usability and accessibility, always make sure that the user
knows which element has focus. Include CSS
:focus,
:focus-visible,
and (optionally)
:focus-within
styles. There are modules dedicated to focus styles in both
Learn CSS and
Learn Accessibility.
By default, the navigation focus order is the same as the visual order, which is the source code order. There are HTML attributes that can alter this order and CSS properties that can alter the visual order of content. Changing the tabbing order with HTML or visual rendering order with CSS can harm user experience.
Don’t alter the perceived and actual tabbing order with CSS and HTML. As the following two examples demonstrate, tab orders that differ from the visually expected order are confusing to users and bad for user experience.
In this example, the value of the
tabindex
attribute has made the tab order chaotic:
In this example, CSS has created a divergence between the tabbing order and the visual order of the content:
The
flex-flow: row-reverse;
declaration has reversed the visual order. In addition, the CSS
order property was applied
to the sixth word, “This”, which visually moved that one word. The tabbing
sequence is the order of the code, which no longer matches the visual order,
creating a disconnect for keyboard users. | Note: CSS features, including
flexbox,grid,
positioning,
transforms, and
multi-column,
can alter the visual order of content. It’s important you make sure your content
maintains a logical tabbing order, on all viewport sizes. Test your content by
tabbing through it with a keyboard: with shift + tab to
move backwards through the content. Use CSS to indicate which element has focus.
Don’t reorder focusable elements with CSS.
Make inert elements interactive
The contenteditable and tabindex attributes, being global attributes, can be
added to any element, making them focusable in the process. Focusable elements
can also be focused with a mouse or pointer, by having the
autofocus
attribute set, or by script, such as with
element.focus().
The tabindex attribute
The global
tabindex
attribute, introduced in
attributes, enables elements
that otherwise wouldn’t be able to receive focus to get focus, usually with
Tab, hence the name.
The tabindex attribute takes as its value an integer. A negative value makes
an element focusable but not tabbable. A tabindex value of 0 makes the
element focusable and tabbable, adding the element on which it’s applied to the
sequential focus navigation order in source code order. A value of 1 or greater
makes the element focusable and tabbable, but adds it to a prioritized tabbing
sequence and should be avoided.
On this page, the share button, <share-action>, is a
custom element. The tabindex="0" adds
this not-normally focusable element into the keyboard default tabbing order:
<share-action
authors="@front-end.social/@estellevw"
data-action="click"
data-category="web.dev"
data-icon="share"
data-label="share, mastodon"
role="button"
tabindex="0"
>
<svg aria-label="share" role="img" xmlns="http://www.w3.org/2000/svg">
<use href="#shareIcon" />
</svg>
<span>Share</span>
</share-action>| Note: The
role="button"
informs screen reader users that this element should behave like a
<button>. When
creating custom elements which mimic existing semantic elements, include an
ARIA role.
The element must provide all the features of the element being replicated. This
can be done by extending the replicated element, such as extending the
HTMLButtonElement,
or by adding tabindex="0" and using JavaScript to program all the
functionality of the element it’s mimicking, including handling pointer events
and the Enter and Space key presses. If <button> had been used for the button
instead of creating a custom element, the tabindex and role attributes would
have been unnecessary, and the browser would have provided the pointer and
keyboard events.
There’s another custom element on this page: the
local navigation has a
custom element with a negative tabindex value:
<web-navigation-drawer type="standard" tabindex="-1"></web-navigation-drawer>A tabindex attribute with a negative value makes the element focusable but not
tabbable. The element is capable of receiving focus, such as using
HTMLElement.focus(),
but it’s not part of the sequential focus navigation order. The convention for
non-tabbable, focusable elements is to use tabindex="-1". If you add
tabindex="-1" to an interactive element, it will no longer be tabbable.
The
element.focus()
method can be used to set focus to focusable elements. Browsers scroll focused
elements into view. For this reason, avoid the use of
element.focus({preventScroll:true}), as focusing on a non-visible element is a
bad user experience.
If you want to query the document to find out which element has focus, use the
read-only
Document.activeElement
property.
Elements with a tabindex of 1 or greater are included in a separate tab
sequence. As you’ll notice in the Codepen, tabbing begins in a separate
sequence, in order of lowest value to highest value, before going through those
in the regular sequence (no tabindex set, or tabindex="0") in source order:
tabindex with a positive value puts the element into a prioritized focus
sequence, which can lead to focus order chaos. Avoid
modifying the DOM order with tabindex.
Not only can altered tabbing orders create bad user experiences, they are
difficult for developers to manage and maintain.
The contenteditable attribute
The contenteditable
attribute was discussed earlier. Setting contenteditable="true" on any element
makes it editable, focusable, and part of the tab order. The focus behavior is
similar to setting tabindex="0", but not the same. Nested contenteditable
elements are focusable but not tabbable. To make a nested contenteditable
element tabbable, add tabindex="0", which add it to the sequential focus
navigation order.
Give autofocus to interactive elements
While the boolean
autofocus
is a global attribute that can be set on any element, it doesn’t make an inert
element interactive. When the page loads, the first focusable element with the
autofocus attributes receives focus, as long as that element is displayed and
not nested in a <dialog>.
Automatically setting focus on content can be confusing. Setting autofocus on
a form control means that the form control scrolls into view on page load. All
your users, including screen reader users and users with small viewports, may
not “see” the instructions for the form, possibly even scrolling past the form
control’s normally visible label. The autofocus attribute doesn’t alter the
document’s sequential focus navigation order. The elements in the sequence
coming before the auto-focused element are skipped. For these reasons, it’s not
advised to include the autofocus attribute.
The exception to the “don’t use autofocus” recommendation is including the
autofocus attribute within <dialog> elements. When a dialog is opened, the
browser automatically focuses on the first focusable interactive element within
the <dialog>, which means it’s unnecessary to add autofocus to an element.
If you want to be sure a specific interactive element within the dialog receives
focus when the dialog opens, add the autofocus attribute to that element.
<dialog open>
<form method="dialog">
<button type="submit" autofocus>close</button>
</form>
</dialog>The autofocus attribute set on the close <button> allow it to receive focus
when the dialog is opened. As the first element in the dialog, it would have
received focus in any case. By default, when a dialog is opened, the first
focusable element within the dialog receives focus unless a different element
within the dialog has the autofocus attribute set.
Make interactive elements inert
There are also HTML attributes that can remove interactive elements from the
tabbing sequence. Including a negative tabindex to focusable elements, adding
the disabled attribute to supporting form controls, and adding the global
inert attribute to a container all make elements un-tabbable. These three
attributes are NOT interchangeable.
Negative tabindex value
A tabindex attribute with a negative value makes an element focusable, but not
tabbable. While adding tabindex="0" to a focusable-by-default element,
including links, buttons, form controls, and elements that are contenteditable
is not necessary; including a tabindex with a negative value removes normally
tabbable elements from the sequential focus navigation order.
A negative tabindex value prevents keyboard users from focusing on interactive
elements, but doesn’t disable the element. Pointer users can still focus on the
element. To disable an element, use the disabled attribute.
Disabled
The boolean disabled attribute makes the form controls on which it’s applied and their descendants, if any, unfocusable. Disabled form controls can’t be focused, don’t get click events, and are not submitted upon form submission.
disabled is not a global attribute. It applies to <button>, <input>,
<optgroup>, <option>, <select>, <textarea>, form-associated custom
elements, and
<fieldset>.
When set on <optgroup> or <fieldset>, all the child form controls are
disabled, except for the contents of the <fieldset>’s first
<legend>.
The same elements that support disabled are also targetable with the
:disabled and
:enabled
pseudo-classes. Elements that are disabled with the disabled attribute are
generally styled as light gray with the user-agent stylesheet, even if an
accent-color is
set.
Being a boolean attribute, the presence of the attribute disables the otherwise
enabled element; you can’t set it to false. To re-enable a disabled element,
the attribute has to be removed, generally with
Element.removeAttribute('disabled').
The
HTMLInputElement.disabled
property lets you check if an input is disabled. As disabled is not a global
attribute, it’s not inherited from the HTMLElement, but every supporting element
interface, like
HTMLSelectElement,
HTMLTextareaElement,
has the same read-only property.
The disabled attribute doesn’t apply to normally inert elements that are
made focusable with tabindex or contenteditable, nor does it apply to the
<form> element. To disable these elements, the global inert attribute can be
used.
The inert attribute
When the inert global boolean attribute is added to an element, that element
and all nested content become disabled, which means they cannot be clicked or
tabbed to. They are also removed from the accessibility tree. While inert can
be applied to any element, it’s generally used for sections of content, such as
offscreen or otherwise hidden content.
When applying disabled to form controls, the browser provides default styling
and can be styled using the
:disabled pseudo
class. The inert attribute provides no visual indicators and has no matching
pseudoclass (though the [inert]
attribute selector
matches).
Using inert on visible content without styles indicating the inertness can
lead to poor user experience. As inert content is not available to screen reader
users, it can lead to confusion when sighted screen reader users see content on
screen that is not available to the accessibility tools. Make inertness very
apparent with CSS.
Make sure that the focus never moves to non-visible content. Anything rendered off-screen that does not automatically come into view when focused should be made inert. If content is hidden, but comes into view when focused, like a skip to content link, it does not need to be made inert.
Learn HTML © 2022 by Estelle Weyl, CC BY 4.0.