Accessible CSS-Only Tab View

Accessible CSS-only tab view

Accepted answer is not an accessible solution.

I have made some corrections and some observations here. Do not use the accepted answer in production if you stumble across this question in the future. It is an awful experience with a keyboard.

The answer below fixes some of the CSS issues to make it more accessible.

However I would recommend you reconsider the no JavaScript requirement.

I can understand having a good fall-back (which the example I give below with the fixes is) but there is no way you can make a fully accessible set of CSS only tabs.

Firstly you should use WAI-ARIA to complement your HTML to make things even more clear for screen readers. See the tabs examples on W3C to see what WAI-ARIA roles you should be using. This is NOT possible without JavaScript as states need to change (aria-hidden for example should change).

Secondly, you should be able to use certain shortcut keys. Press the home key for example in order to return to the first tab, something you can only do with a little JS help.

With that being said here are a few things I fixed with the accepted answer to at least give you a good starting point as your 'no JavaScript fallback'.

Problem 1 - tabindex on the label.

By adding this you are creating a focusable element that cannot be activated via keyboard (you cannot press space or Enter on the label to change selection, unless you use JavaScript).

In order to fix this I simply removed the tabindex from the labels.

Problem 2 - no focus indicators when navigating via keyboard.

In the example the tabs only work when you are focused on the radio buttons (which are hidden). However at this point there is no focus indicator as the styling is applying styling to the checkbox when it is focused and not to its label.

In order to fix this I adjusted the CSS with the following

/*make it so when the checkbox is focused we add a focus indicator to the label.*/
.tabs__input:focus + label {
outline: 2px solid #333;
}

Problem 3 - using the same state for :hover and :focus states.

This is another bad practice that needs to go away, always have a different way of showing hover and focus states. Some screen reader and screen magnifier users will use their mouse to check they have the correct item focused and orientate themselves on a page. Without a separate hover state it is difficult to check you are hovered over a focused item.

/*use a different colour background on hover, you should not use the same styling for hover and focus states*/
.tabs__label:hover{
background-color: #ccc;
}

Example

In the example I have added a hyperlink at the top so you can see where your focus indicator is when using a keyboard.

When your focus indicator is on one of the two tabs you can press the arrow keys to change tab (which is expected behaviour) and the focus indicator will adjust accordingly to make it clear which tab was selected.

.tabs {  background-color: #eee;  min-height: 400px;}
.tabs__list { border-bottom: 1px solid black; display: flex; flex-direction: row; list-style: none; margin: 0; padding: 0; position: relative;}
.tabs__tab { padding: 0.5rem;}
.tabs__content { display: none; left: 0; padding: 0.5rem; position: absolute; top: 100%;}
.tabs__input { position: fixed; top:-100px;}
.tabs__input+label { cursor: pointer;}

.tabs__label:hover{ background-color: #ccc;}
.tabs__input:focus + label { outline: 2px solid #333;}
.tabs__input:checked+label { color: red;}
.tabs__input:checked~.tabs__content { display: block;}
<a href="#">A link so you can see where your focus indicator is</a><div class="tabs">  <ul class="tabs__list">    <li class="tabs__tab">      <input class="tabs__input" type="radio" id="tab-0" name="tab-group" checked>      <label for="tab-0" class="tabs__label" role="button">Tab 0</label>      <div class="tabs__content">        Tab 0 content      </div>    </li>    <li class="tabs__tab">      <input class="tabs__input" type="radio" id="tab-1" name="tab-group">      <label for="tab-1" class="tabs__label" role="button">Tab 1</label>      <div class="tabs__content">        Tab 1 content      </div>    </li>  </ul></div>

Accessibility: Tabbing on mobile goes behind nav overlay at bottom of viewport

Trying to intercept tab etc. and adjust the scroll position yourself is one possibility as you have stated but that could potentially lead to unexpected behaviour.

A better way to handle this is to adjust your layout to account for the overlay at the bottom.

In essence all of your main content would sit in a container (this may be the <main> element if your overlay at the bottom is an <aside> otherwise just use a <div>).

Make that container the page height minus the height of the overlay.

That way no content will ever be behind the overlay (which technically is no longer an overlay as nothing goes behind it).

The beauty of this is when you get to the very bottom of the page everything will be visible, with an overlay you may end up with something partially obscured if you don't give it enough margin / padding at the bottom.

HTML tab interface using only CSS

It is possible with html and css for most modern browsers using the border-radius property (not supported by internet explorer 8 and below).

css

li {-moz-border-radius: 12px 12px 0 0; /* FF1+ */
-webkit-border-radius: 12px 12px 0 0; /* Saf3-4 */
border-radius: 12px 12px 0 0; /* Opera 10.5, IE9, Saf5, Chrome */
border:1px solid black;
display:inline;
list-style-type:none;
padding:5px;
}
li:hover {background:black;}
li a {text-decoration:none; color:black;}
li a:hover {color:white;}

html

<ul>
<li><a href="#tab1">tab 1</a></li>
<li><a href="#tab2">tab 2</a></li>
<li><a href="#tab3">tab 3</a></li>
</ul>

To support internet explorer you can use css3pie but you have to keep in mind that it uses javascript.

You can find more information about border-radius at: http://www.w3.org/TR/css3-background/#the-border-radius

Example: http://jsbin.com/idiza5/2

Enable :focus only on keyboard use (or tab press)

Update: This issue may no longer be relevant

Some other posters have mentioned the :focus-visible pseudo class - which now has decent browser support...

I would like to add, that based on the spec which covers the :focus-visible pseudo class, browsers should now only indicate focus when it is helpful to the user - such as in cases where the user interacts with the page via a keyboard or some other non-pointing device

This basically means that the original issue is no longer relevant, because now, when a user clicks/taps a button (or another focusable element), the User Agent won't show the focus ring anymore - even though the button is focused - because in this case the focus ring isn't helpful to the user.

From the spec:

While the :focus pseudo-class always matches the currently-focused
element, UAs only sometimes visibly indicate focus (such as by
drawing a “focus ring”), instead using a variety of heuristics to
visibly indicate the focus only when it would be most helpful to the
user. The :focus-visible pseudo-class matches a focused element in
these situations only...

Indeed, as of version 90, Chromium’s User Agent stylesheet switched from :focus to :focus-visible, and as a result of this change, button clicks and taps no longer invoke focus rings

Also, as of version 87, Firefox also uses :focus-visible on their User Agent style.

All that being said, if custom focus styles are needed, since focus styles have now shifted from :focus to :focus-visible, when overriding the default styles with custom focus styles - the :focus-visible pseudo class should be used.

Something like this:

button:focus-visible {
/* remove default focus style */
outline: none;
/* custom focus styles */
box-shadow: 0 0 2px 2px #51a7e8;
color: lime;
}

Backwards Compatibility:

The possible problem with using :focus-visible like this, is that browsers which don't support :focus-visible, will show the default focus ring, which may not be clear or visible - depending on the design.

Šime Vidas, in this article, describes a viable strategy to currently use the :focus-visible pseudo class - which would work even in browsers which don't yet support :focus-visible -

A good way to start using :focus-visible today is to define the focus
styles in a :focus rule and then immediately undo these same styles in
a :focus:not(:focus-visible) rule. This is admittedly not the most
elegant and intuitive pattern, but it works well in all browsers:

Browsers that don’t support :focus-visible use the focus styles
defined in the :focus rule and ignore the second style rule completely
(because :focus-visible is unknown to them).

In browsers that do support :focus-visible, the second style rule
reverts the focus styles defined in the :focus rule if the
:focus-visible state isn’t active as well. In other words, the focus
styles defined in the :focus rule are only in effect when
:focus-visible is also active.

button:focus {
outline: none;
background: #ffdd00; /* gold */
}

button:focus:not(:focus-visible) {
background: white; /* undo gold */
}

Original Answer:

This excellent article by Roman Komarov poses a viable solution for achieving keyboard-only focus styles for buttons, links and other container elements such as spans or divs (which are artificially made focusable with the tabindex attribute)

The Solution:

button {
-moz-appearance: none;
-webkit-appearance: none;
background: none;
border: none;
outline: none;
font-size: inherit;
}

.btn {
all: initial;
margin: 1em;
display: inline-block;
}

.btn__content {
background: orange;
padding: 1em;
cursor: pointer;
display: inline-block;
}

/* Fixing the Safari bug for `<button>`s overflow */
.btn__content {
position: relative;
}

/* All the states on the inner element */
.btn:hover > .btn__content {
background: salmon;
}

.btn:active > .btn__content {
background: darkorange;
}

.btn:focus > .btn__content {
box-shadow: 0 0 2px 2px #51a7e8;
color: lime;
}

/* Removing default outline only after we've added our custom one */
.btn:focus,
.btn__content:focus {
outline: none;
}
<h2>Keyboard-only focus styles</h2>

<button id="btn" class="btn" type="button">
<span class="btn__content" tabindex="-1">
I'm a button!
</span>
</button>

<a class="btn" href="#x">
<span class="btn__content" tabindex="-1">
I'm a link!
</span>
</a>

<span class="btn" tabindex="0">
<span class="btn__content" tabindex="-1">
I'm a span!
</span>
</span>

<p>Try clicking any of the the 3 focusable elements above - no focus styles will show</p>
<p>Now try tabbing - behold - focus styles</p>

How to css target Tab state of the input?

The css selector you want is :focus, like input:focus { ... } but not everything can be tabbed to like this. Things like anchors, inputs and buttons can be tabbed to in this way. I do not know any more off the top of my head but things like div, span, h1/2/3/4/5/6 cannot be tabbed to.

Also to note, tabbing into an item is not the only way to bring it to focus. An anchor for example, you can hold left mouse button down over it and then move the cursor away before letting go. The item will be focused by doing this (it might aid your dev time if you want to style something near the end of the page.)

Making a clickable div accessible through tab structure?

I note the question is tagged WCAG and "accessibility".

The answer to your question is therefore "don't do that." The other answers on this page will all work fine, for everyone except the people who need it to work, i.e. those using screenreaders or other assistive technology. None of the javascript-based solutions here are WCAG compliant.

What you want is a <button>. That gives you your tabindex and keyboard control for free.

You can also force a <div> to work like a <button> by adding ARIA markup (though it's better and easier to just use the tag that already does what you need it to do.)

If absolutely necessary, a good introduction to using ARIA for pseudo-buttons is here:
https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_button_role

The gist of it is, you need to add the role="button" attribute, and manage the state of the aria-pressed attribute manually (by capturing key events and clicks in javascript; other answers have covered this pretty thoroughly so I won't repeat the details)

Making my drop-down menus accessible when only using the keyboard

Looking at the tutorial, it is not really a good starting point to make fully keyboard accessible. Unfortunately it isn't just a matter of showing the drop-downs when pressing tab.

Once you get past a couple of items in each drop-down it it not a good thing for keyboard users to have to tab through every option.

There are two main ways to make this type of menu accessible:

  1. Traditional method: Make each top level link go through to a landing page that includes the links from the menu, and hide the drop-downs from keyboard use.

  2. HTML5 (new) method: Use WAI-ARIA markup and lots of scripting to enable full keyboard accessibility.

The traditional method if fairly easy to do, you just need to properly hide the menus that are off-screen using display: none; on the classes starting .dropdown_1column. The means keyboard users won't tab through the sub-menus.

And then use display: block to show the sub-menus on hover (not focus), where the CSS starts:

#menu li:hover .dropdown_1column

To achieve the HTML5/new method I would start with Adobe's open-sourced "Accessible Mega Menu". It would be a huge amount of work to do this from scratch, and unless you know how to test with a wide range of user-agents, it is unlikely to work as well as Adobe's.



Related Topics



Leave a reply



Submit