Further explanation and more about CSS tabs:
Here at CSS-Tricks, we’ve been playing with the idea of CSS tabs for years. Half a decade ago we explored seven different techniques (All of them pretty bad, don’t use those). We revisited it four years ago and solved some of the shortcomings there, but introduced some new problems.
These days, through techniques like The Checkbox Hack, you can get a lot closer to a good tabbed UI experience with just HTML and CSS. Check out Art’s post about how to do that. And his final demo:
See the Pen Radio-Controlled Tabs (ARIA) by Art Lawry (@artlawry) on CodePen.
As ever on the web, there is always new possibilities to explore. I believe the jury is still out on whether form controls are acceptable for accessibility for toggling the visibility of content when
display: none is being used to hide that content. Perhaps other hiding methods could be explored.
Stephan Barthel has explored this concept using flexbox which is pretty interesting in itself.
There are rumors of CSS helping with arbitrary state-based design as well, so certainly the future is interesting for this kind of thing.
Why not use css :target? Wouldn’t that be nicer over using checkboxes? =\
That certainly does work! At the same time, it can cause some odd unnecessary page-jumping junk on click: https://css-tricks.com/functional-css-tabs-revisited/
“unnecessary page-jumping junk on click”
It can be avoided
More info [es-es blog]
Yeah, definitely not saying it doesn’t work–just nice to not have to think about that side effect. :)
I’m always excited about what css can do, but I’m a little concerned about that (more or less).
But yeah, for a very simple page, or a small and not critical part of our web app, I think that this is really powerful, easy to do and very clean.
But again, it’s just my opinion.
With radio buttons and checkboxes styled as links, keyboard users expect that pressing enter on those links would produce the desired results; however, if I remember correctly, checkboxes and radio buttons can only be selected using space bar: another not so cool fact regarding this approach.
Comment may be slightly off as I have not recently tested the differences between the behaviour of input based elements and anchor elements.
@Irfan adding one event listener on the container of radio buttons you could have enter selected the radio button that has focus (event bubbling is a beautiful thing).
the last tab (dessert) has a broken active state.
Darn those missing dots…thanks and fixed.
Can you explain why you need to use:
and not just
I can’t see how that results in any difference?
That’s the general sibling selector and it’s what makes this magic happen.
Your version says we want the #breakfast-tab element which is inside .tabs, which is inside #breakfast (and #breakfast has focus). But since #breakfast is a radio input, we can’t put other elements inside it.
Instead, we say we want the #breakfast-tab that is inside .tabs, which is a sibling with #breakfast (and #breakfast has focus). We couldn’t put our tabs inside the radio input, but we can put them after it. So this allows us to style things that come after a radio (or checkbox) input based on its state.
Hey, nice! Thanks for remembering me!
Well now my comment doesn’t make sense, but I was mentioned in the earlier revision of this article. :-/
This is pretty cool! Thanks for sharing.
FWIW, jQuery UI Tabs have this: https://jqueryui.com/tabs/
Standalone demo: https://jqueryui.com/resources/demos/tabs/default.html#tabs-2
I also feel that a solution like this one, albeit pretty damn clever, is kind of ‘hacky’. Using
label‘s for tabs feels unsemantic to me :/
Now, here are a few questions for everyone:
On another note and in the spirit of naming things correctly, here is a suggestion:
White space is not inherently a bad thing, white space is actually quite the opposite:
1- It gives elements on the layout room to “breathe” and not feel cramped.
2- It enhances content hierarchy.
3- It improves legibility.
More here: https://boagworld.com/design/why-whitespace-matters/
As it is, the phrase is misleading because it labels white space as a bad thing. I recommend changing the term white space to one of these: bottom gap; unused space; empty space;
I’ll leave this here too:
Should have the accessibility issues solved + has the friendly syntax of having each tab defined as a standalone block instead of splitting it into three parts (the traditional radios first, labels next and then the panels).
The sample in the link includes extra CSS and JS to make it work in IE8, IE7 and IE6, in easy-to-remove format. Without JS the support begins from around the IE9, Opera Presto and Safari 5 age of browsers.
And it goes to accordion mode in mobile.
Many thanks for great article!
Really like this – excellent stuff – but wouldn’t work for me 1st time around until I spotted a bug in main text CSS
#breakfast:checked ~ tab-links #breakfast-tab,
#dinner:checked ~ tab-links #dinner-tab,
#dessert:checked ~ tab-links #dessert-tab
should be …
#breakfast:checked ~ .tab-links #breakfast-tab,
#dinner:checked ~ .tab-links #dinner-tab,
#dessert:checked ~ .tab-links #dessert-tab
A tabbed interface without JS is all well and good as a concept, but why is accessibility always an afterthought?
It should be baked in to our dev process.
Where’s the basic keyboard support?
And once that’s fixed, screen reader users still have no chance with the content being hidden with “display:none;”
Credit to Geoff for refining an old article & method, but I agree with @basher — this should never be considered a best practice. It is indeed a “CSS trick” — yet it slams the door on the visually impaired.
This article feels out of place on CSS Tricks. I have tremendous respect for the work that went in to this article, and for this site — and will continue to — but I find this article disappointing. I want to learn how to build the web the best that I can — for everyone!
Yeah, my thoughts exactly. This is a toy, maybe for quick demos, but shouldn’t be used on serious projects. Even if it wasn’t inaccessible, using form elements for their interactive properties alone just FEELS wrong. Too bad
<dl>s don’t work the same on all browsers; I use those most for Q&A type togglers. Using the
:targetedmethod makes a lot more sense for tabs, and I might play with @Kseso’s idea to prevent the jumpiness. One downside of that is having to back/forward through multiple anchors is unexpected on a tabs page… if only there were a good no-js way to leave a page and just come back to the last tab that was open rather than going through the entire history of clicks made.
Thanks for revisiting, life saver.
You could use max-height: 0 (with overflow: hidden) instead of display: none.
* Can be coupled with transition to animate between tabs
* Requires an explicit max height for the open state
* It’s kinda janky
Actually, on second thoughts, maybe don’t do that…
Two more cons:
Hiding elements only visually like that still allows screen reader users to find and interact with them.
Placing a bunch of text on the screen that users can’t see is a black hat tactic and can damage SEO.
display: none; is important. :)
The SEO concern is also non-existing. Google has had no problem indexing and has given no penalty despite having non-
And I have another (8 way?) of creating pure css tabs, without radio buttons, or target, or focus selector, its still no perfect as checked radio’s but it can be done using infinitive transition delay, interested? :>
@basher, @kyle, and @philtune: I delve more into the state of accessibility for this technique in the link now mentioned at the top. It still might not get you to the point where you’re comfortable with the technique as a “best practice” but it definitely goes beyond the original article as it was posted here.
I also talk a bit about using checkboxes/radios to store page state which may address @philtune’s concerns (then again, I find people fall down hard on one side of the line or another).
Over all, I’m just really happy that people are exploring the strengths and weaknesses of this technique. Hopefully others continue to refine it and challenge it so that a stronger solution emerges.