CSS Only Tabs

There is a tendency of web developers to prefer javascript over other tools of the trade. A lot of this is based in the need to support older browsers or - even more often - the percieved need to support old browsers. Just as often however, it is due to fear and ignorance of how the tools of the internet are meant to work.

A great example is ui tabbing systems. I’ve participated in half-hour long hoarse-yelled “discussions” on which tabbing widget is the best. Which is ironic, because the true answer is: you probably don’t need a javascript tabbing widget at all. If you understand CSS, you can assemble your own easily enough. Let’s do that now.

So we want a fully functioning tab system with url navigation. Lets figure out how to do that.

Observation #1

The :target pseudo-selector seems useful. You can use it to highlight bits of html that are hash-linked

Hash-linked element selection

Go ahead and check out the full-screen view to really get the effect. So we learn that you can apply styles to any element that is currently selected by the url hash.

Observation #2

You can select elements that follow :target

Selecting following elements

And even elements that eventually follow it

Selecting eventually following elements

So, maybe if we can arrange our html like this we can take advantage of things. We’ll start with some really simple html and some basic styling:

Just the facts straightforward HTML and CSS

Of course tabs need to go first, so lets re-arrange things with flexbox’s order property.

Position tabs first with flexbox order

Note that we start ordering at 1, not 0…this will come up later. Also note the technique we’ve undertaken to ennsure the content does not flow up to the tab line - we make all articles take up 100% of their container and allow the flex direction to wrap using flex-wrap. This is a somewhat awkward but necessary feature for working with this particular scheme.

With that being figured out, we already know how to show only the targetted tab content. Go ahead and click the tabs below.

Show only the the selected tab content

We even know how to style the selected tab itself using the + selector. Click around.

Style the selected tab

And so we have a working CSS-only tabs implementation! But not really. Because nothing is targetted on initial page load, no content is initally loaded. We could of course solve this with javascript:

		window.location.hash = document.querySelector('main > article').id

But that’s cheating. We want a fully no-javascript implementation.

Observation #3

You can style the last element as if it was targetted. Then, if another element is targetted, override that styling on all subsequent elements.

Style last element in an overridable manner

Note that we can only override following items, not preceeding ones. This means the element selected on first page load has to be the last in the list.

But knowing this, we can have article content show when the page is first loaded.

Article content visible when page first loaded

We’re so close! But it’s still wierd. Usually, when a page loads the first tab is selected. This is something we can solve with css ordering!

A fully working CSS tabs implementation

And we’re done! We even have hash linking.

While other implementations of css tabs exist, this is the only one I’ve seen that will work without having to hard-code ids into your css.

So before we finish up, there’s a few pros and cons to this approach that ought to be mentioned.

  • Pros:
    • No javascript at all. No javascript libraries, not even any glue code. This is the lowest overhead possible.
    • Nice, declarative system. No procedural javascript. Just plain ol’ CSS. There is no chance of some sequence of events not firing a handler and things not working
    • Naturally flows with how HTML and CSS are originally meant to work.
  • Cons:
    • This only shows/hides what is already on the page. There are no transition hooks, no hooks to load data, no cancelling navigation, all data is on the page at the same time.
    • flex-wrap is necessary but weird. There is no way to actually tell the content to “take up the rest of the page” as often is with flexbox. This is the one thing you kind of have to hack around if that is the behavior you want.
    • If you are using a js framework that highjacks url hashes this will screw it up

So don’t forget. Javascript might not be necessary. Just take the time to understand the tools that the designers of the web made available to us.

blog comments powered by Disqus