Home » Web » CSS » Functional CSS Tabs Re-Revisited

Functional CSS Tabs Re-Revisited

A tabbed UI is pretty common to most of us: click a tab area to see new content, generally without a page refresh. It’s a handy technique in situations where content needs to be broken up into contained blocks and digested one at a time.

Tabs have a long history that is deeply rooted in JavaScript as the method for handling the functionality of hiding and revealing content on click. Approaching tabs purely with CSS is a much shorter history, but we’ve seen it as far back as 2008 when Brad Kemper made it work with the :checked pseudo selector.

A Tale of Tabs Past

We took a stab at pure CSS tabs using the :checked method a few years back and wrote it up as a tutorial. This was a solid approach, for the most part, but the bummer about it was the need to specify an explicit min-height on the tabbed content container. This often led to situations where tabs with less content than others were left with awkward white space, and those with more content spilled past the container:

See the Pen CSS Tabs with min-height by CSS-Tricks (@css-tricks) on CodePen.

What we want is a set of similarly functional tabs, but that can support content of any length or size. They should grow or shrink based on the content in the currently selected tab. Let’s do that.

HTML Structure

We start with the tab wrapper, followed by a row of radio inputs (for the functionality), then the panels themselves.

<div class="tabs">

  <input class="state" type="radio" title="Breakfast" name="houses-state" id="breakfast" checked="">
  <input class="state" type="radio" title="Dinner" name="houses-state" id="dinner">
  <input class="state" type="radio" title="Dessert" name="houses-state" id="dessert">

  <div class="tab-links">
    <label for="breakfast" id="breakfast-tab" class="tab">Breakfast</label>
    <label for="dinner" id="dinner-tab" class="tab">Dinner</label>
    <label for="dessert" id="dessert-tab" class="tab">Dessert</label>

  <div class="panels">
    <div id="breakfast-panel" class="panel active">
      Tab Content
    <div id="dinner-panel" class="panel">
      Tab Content
    <div id="dessert-panel" class="panel">
      Tab Content


CSS Layout

Skipping over the purely presentational parts of the CSS, we’re basically:

  1. Hiding the radio inputs (using absolute positioning)
  2. Positioning panels using inline-block
  3. Hiding panels when a radio input is in an :unchecked state
  4. Revealing panel when a radio input is in a :checked state
.tabs .state {
  display: none;

#breakfast:focus ~ .tabs #breakfast-tab,
#dinner:focus ~ .tabs #dinner-tab,
#dessert:focus ~ .tabs #dessert-tab {
  box-shadow: 0 0 3px 3px rgba(0,127,255,.5);

.tabs .tab {
  display: inline-block;
  padding: 10px;
  vertical-align: top;
  background-color: #eee;
  cursor: hand;
  cursor: pointer;

.tabs .tab:hover {
   background-color: #fff;

#breakfast:checked ~ tab-links #breakfast-tab,
#dinner:checked ~ tab-links #dinner-tab,
#dessert:checked ~ tab-links #dessert-tab {
  background-color: #fff;
  border-bottom: 5px solid #fff;
  cursor: default;

.tabs .panels {
  background-color: #fff;
  padding: 10px;

.tabs .panel {
  display: none;

#breakfast:checked ~ .panels #breakfast-panel,
#dinner:checked ~ .panels #dinner-panel,
#dessert:checked ~ .panels #dessert-panel {
  display: inline-block;

So, what’s the big difference here from our previous attempt? It’s getting rid of position: absolute on the tab containers and choosing instead to use display: inline-block when revealing the content for the currently checked tab. This allows the container to ditch that explicit min-height that was a requirement for position: absolute.


Psych! There is no JavaScript. Pretty awesome, right? We’re exploiting the The Checkbox Hack to manage state.

The Result

See the Pen Radio-Controlled Tabs by CSS-Tricks (@css-tricks) on CodePen.

Why This is More Awesome

  • No more explicit heights! The panels grow and shrink according to the content it contains.
  • The support is a little deeper (and more current) than before.

Why This is (Still) Not Perfectly Awesome

  • It’s not totally accessible. The content is hidden with display: none which isn’t ideal here because there is no anchor link or other obvious control to reveal that hidden content. We could possibly remedy that by using opacity instead, but there are costs and trade-offs with that as well.
  • The browser support is good, but not bullet-proof. Everything is great in modern browsers, but starts to fall apart in IE 9 and below. JavaScript is still the best method if deeper browser support is needed. And the JavaScript needed here would be very light. Just a click handler on the tabs that toggles class names on the tabs and panels as needed. Plus then the HTML structure would be completely up to you.

Flexbox to the Rescue?

Stephen Barthel has a nice example of tabs using Flexbox as an alternative. Still has some of the same accessibility and browser support concerns as the technique in this post, but is nice to see nonetheless.


Functional CSS Tabs Re-Revisited is a post from CSS-Tricks

Feed Source: CSS-Tricks
Article Source: Functional CSS Tabs Re-Revisited

About Admin

Powered by WP Robot