First off big thanks to Chris Coyer for posts like: Functional CSS Tabs Revisited. I read a ton of articles figuring this out, so if I forgot to credit someone, then here's a thumbs up to you!

The goal: Create a tabbed interface that is responsive, degrades 'gracefully' on older browsers, is accessible/seo-friendly and that uses minimal (no) javascript.

Here's where I ended up.

Here's how I got there.

First, using articles like the afformentioned Functional CSS Tabs Revisited I found that the :checked radio button tab method fit an accordion-style interface perfectly. The beauty of an accordion is that, since each header directly precedes it's content, there's no need for position:absolute like Chris' example and the layout can easily be made responsive.

We also need our content to be accessible/seo-friendly. By following the principles at Places It’s Tempting To Use Display: None; But Don’t we refuse to display:none, opacity:0 or height:0 our content. Instead we overflow:hidden and height: <small value> to visually hide it. This is another place the accordion works in our favor, since we don't want to hide the section headers we set the content block to the height of the header, hide overflow, and we're done.

Another important concept is having it degrade gracefully. For my purposes that means having all the content visible and organized. The article at points out that by using the :not(:target) we can point our rules at only the browsers that support the needed CSS Selectors. That same principle can be applied to the :checked method. So by using :not(:checked) to collapse unfocused tabs and :checked to style the focused tab we've got our fallback. Now old browsers will simply not collapse any sections and we end up with all our content. That turns out looking something like this:

But this leaves us with an accordion, not a tab. So now we use progressive enhancement to turn our accordion into tabs for screens wide enough to show them. But how? Following the example at 4 Methods CSS3 Tabbed Content (fourth example) if we make our inputs siblings with our tab content and throw in a second set of labels we can now, with some complicating of the css (more on that momentarily), have a tabbed interface, that can also become an accordion interface when the tabs no longer fit, and still degrades 'gracefully'.

Purely for the sake of repetition here's the final product again:

The catch:

The CSS for our tabbed-with-fallback interface is considerably stickier than our accordion. In the accordion each input was siblings with it's label, and both were children of the content blocks. In the tabbed method the tabs and the inputs are siblings with the content blocks, and not in a useful order. To solve this problem we have options. One way is to use the :nth-of-type() selector to pair the first input with it's first sibling label and first sibling content, etc, etc. For my example I chose instead to use IDs/Classnames that I've set to follow a convention of my choosing. For me this left shorter rules. Whether using nth-of-type or id/class you have to pick a convention and stick to it. I prefer the method that isn't source-order dependent.

The other unfortunate side-effect of this method is that we have to make a reasonable assumption about the maximum number of tabs we'll have and then make some rather lengthy rules to list all the possible input-label-content pairings. This is unfortunate, but to me seemed worth the highly usable end result.

I'm still in search of a way to generalize my CSS more so I don't have to explicitly pair the input with its label and content. Of course any comments for improvement are highly appreciated.

2,880 1 15