Tabs on desktop, accordion on mobile became, for some reason, by far the most popular Pen I’ve ever done in CodePen. It was an experiment on how to deal with tabbed content on smaller screens, where there is not enough space for the horizontal tabs. My suggestion was to turn it to an accordion, as this seemed to be the most logical and user-friendly approach. Given that the Pen was created a few years ago, I thought I’d revisit it and make some improvements.

Eventually, the goal is to build is something that can adapt smartly, depending on the context and not on manually set media queries. On the GIF below, watch how the first component changes view automatically, while the second stays the same, as there is still enough space:

Challenges

Although implementing the general idea can be achieved in many ways, there were a few challenges that needed to be taken into consideration:

  1. Keep a clean markup. Probably the most obvious approach was to duplicate the accordion titles as tabs, toggling their visibility. Having duplicate elements would create a messy markup, though, and I’m not a big fan of it unless it is absolutely necessary.
  2. Dealing with unknown tab widths. The original Pen uses a specific breakpoint, assigned by hand, for the transformation from tabs to accordion and vice versa. The problem with that, though, is that in a production environment you can’t know how many tabs you will have and how long each one will be. Therefore, you can’t have one breakpoint for every occasion, but you need to adjust it every time, which is obviously not practical.
  3. Avoid jQuery. The original Pen used jQuery for the JavaScript parts, but there is no need for it anymore, as we can handle it with vanilla JavaScript – at least as long as we don’t care about legacy browsers (spoiler: I don’t, at least for this project).
  4. Replicating jQuery slideToggle() with CSS. The original Pen focused solely on the functionality, keeping very minimal transition effects. This works fine for tabs, but having an accordion without the slide up/down effect seemed lacking.

Implementation

Normally, I’d go mobile-first, building the component around the accordion’s structure, as that’s the mode that appears on smaller screens. Tabbed content’s markup needs to be more specific, though, so we will have to use its structure instead. Then, as we go down, we can use a data attribute to get the tab’s title with CSS attr() and display it for the accordion.

Besides that, Tabs is the primary view of the component, as the accordion might never appear even on smaller screens, if the tab size is small enough to fit.

So, our base markup should look like this:

<article class="ootb-tabcordion">
  <div class="ootb-tabcordion--tabs">
    <button class="tab is-active" id="tab1">Tab title</button>
  </div>
  <section id="tab1-tab" class="ootb-tabcordion--entry is-active" data-title="Tab title">
    <div class="ootb-tabcordion--entry-container">
      <div class="ootb-tabcordion--entry-content">
        <p>Tab content</p>
      </div>
    </div>
  </section>
</article>

And the title on smaller screens could be captured with that:

.ootb-tabcordion--entry::before {
  content: attr(data-title);
}

Then, we use some JavaScript to measure the tabs’ total width and compare it to the container’s total width. Depending on that comparison, a .has-tabs¬† class gets toggled accordingly, to indicate the mode we are on. This allows the CSS to know when to display the correct mode.

To identify when the containers’ dimensions change, we can use the ResizeObserver interface. This is much more efficient than using an onresize¬†event, as it avoids infinite callback loops and cyclic dependencies that are often created when resizing via a callback function.

Lastly, mimicking jQuery’s slide() effect with pure CSS can be done with some negative margin-top and transitions, giving us the final result that you can see below. If you are on desktop, try to resize the screen and watch the tabs transforming to an accordion when they run out of space.

 

See the Pen
Tabs on desktop, accordion on mobile
by Giorgos (@gsarig)
on CodePen.

Accessibility

As pointed out correctly by a few people on Twitter, the initial markup falls short on accessibility, as it doesn’t allow you to navigate with your keyboard. W3.org includes guides and best practices for both Tabpanel and Accordion. Of course, they are two different components with different markup, so I had to make a choice. As mentioned already, the component is first and foremost a Tabpanel, as the Accordion might even never appear under some circumstances. Therefore, I worked with the tabbed version first, adjusting the markup accordingly, adding the necessary WAI-ARIA Roles and Properties:

<article class="ootb-tabcordion">
  <div class="ootb-tabcordion--tabs" role="tablist" aria-label="Demo">
    <button class="tab is-active" role="tab" aria-selected="true" aria-controls="tab1-tab" id="tab1">Tab 1</button>
    <button class="tab" role="tab" aria-selected="false" aria-controls="tab2-tab" id="tab2" tabindex="-1">Tab 2</button>
  </div>
  <section id="tab1-tab" class="ootb-tabcordion--entry is-active" data-title="Tab 1" tabindex="0" role="tabpanel" aria-labelledby="tab1">
    <div class="ootb-tabcordion--entry-container">
      <div class="ootb-tabcordion--entry-content">
        <p>Tab 1 content</p>
      </div>
    </div>
  </section>
  <section id="tab2-tab" class="ootb-tabcordion--entry" data-title="Tab 2" tabindex="-1" role="tabpanel" aria-labelledby="tab2">
    <div class="ootb-tabcordion--entry-container">
      <div class="ootb-tabcordion--entry-content">
        <p>Tab 2 content.</p>
      </div>
    </div>
  </section>
</article>

Then, studying the JavaScript of the official example, a keyboardSupport() function has been created, to allow us to enable keyboard navigation. With those adjustments, when you are on Tabbed view you can navigate with your left/right keys between the tabs and select them pressing Space or Enter. When you are on the Accordion view, the left and right buttons become inactive, as now you can navigate through the panels using the up/down keys. On both views, pressing Home will take you to the first tab/panel, and End will take you to the last. Here’s a demo with navigating through the tabs without a mouse, using only the keyboard:

So, how do you like it? Do you see any drawback to that approach or would it be OK for you to use it on production? Some future weekend’s project might be turning this into a Gutenberg Block.

One thought on “Tabs on desktop, accordion on mobile

Leave a Reply

Your email address will not be published. Required fields are marked *