Flexy respondy tabs

Foundation 4 had a sections module that would be an accordion menu at narrow widths and a tab pane at wider widths.

This did horrible things with javascript - absolutely positioning the tabs using style attributes, after doing maths on them - making the tabs difficult to style differently than the supposed blank-slate foundation provides, which is probably why this is gone from later versions of foundation.

I heavily modified the module to copy the heading links into a separate div so I could use normal styling rather than javascript maths, but it was still very unsatisfying.

I have set out to recreate this using CSS alone - based mostly on the wonderful order flexbox property.

The rules:

  1. Don’t duplicate content.
  2. Use no unnecessary HTML elements
  3. Have a sensible/coherent source order
  4. No JavaScript!
  5. Flexible about the amount of content.
  6. Don’t worry about browser support - polyfills are a wonder.

The HTML I ended up with initially was very simple:

<div class="sections">
  <a href="#one" id="one" class="tab">One</a>
  <section><p>The first section</p></section>

  <a href="#two" id="two" class="tab">Two</a>
  <section><p>The second section</p></section>

  <a href="#three" id="three" class="tab">Three</a>
  <section><p>The third section</p></section>
</div>

The tabs target themselves so we can use the target pseudo-class .tab:target to select to the currently highlighted tab, and the sibling selector .tab:target + section to select its section.

But as I wrote this page and it got longer and became scrollabe the target was getting scrolled to the top. So, I’ll switch to using input[type="radio"] and :checked. woo.

<div class="sections">
  <input type="radio" id="one" checked>
  <label class="tab" for="one">One</label>
  <section><p>The first section</p></section>

  <input type="radio" id="two">
  <label class="tab" for="two">Two</label>
  <section><p>The second section</p></section>

  <input type="radio" id="three">
  <label class="tab" for="three">Three</label>
  <section><p>The third section</p></section>
</div>

Accordion styles

The css of the accordion menu is very straghtforward.

.sections .tab {
  position: relative;
  display: block;
}
.sections :checked + .tab {
  # highlighted styles
}

The sections are shown and hid using the animable max-height (as height:auto can’t be animated to.)

.sections section {
  transition: max-height 0.2s ease-out;
  max-height: 0;
  overflow: hidden;
}
.sections :checked + .tab + section {
  transition-delay: 0.15s;
  transition-duration: 0.5s;
  max-height: 100%;
  overflow: auto;
}

with a bit of extra visual style:

Tab styles

For the tabs we set the container to be display:flex, then we are able to pull all the tabs to the top of the container using order:1 for the tabs and order:2 for the content.

.sections {
  display: flex;
  flex-flow: wrap;
}
.sections .tab {
  order: 1;
  flex-grow: 1;
}
.sections :checked + .tab {
  # highlighted styles
}

We push the sections to the overlap using width:100%; margin-left:-100%;.

.sections section {
  order: 2;
  z-index: 1;
  position: relative;
  width :100%;
  margin-left: -100%;
  background: white;
}

The selected tab’s content gets stacked on top. Because of the wonder of flexbox all the tab panes are flexibly the same height, and so the first can be simply overlapped.

.sections :checked + .tab + section {
  z-index: 2;
}

And with all of this in place, we can slot everything into media queries and everything is wonderful, and easily add all the styles we could want in each case, without fighting with any js applied styles.