Java Script (JS) is fine if you know how to use it, but ideally web design (not to be confused with web tech. development) could be done entirely with HTML and CSS; no JS. This page provides the example code for a responsive tabbed container that was written with the goal of avoiding the use of JS as much as possible. The code here differs from other known examples of tabbed containers found on the internet in a number of ways which are also discussed below. Furthermore, this page contains some discussion and documentation for the code itself.
This code is free to use and distribute, but please just include the comment at the top of the HTML as a reference back to this landing page.
HTML and CSS developers (in browser development generally speaking) are slowly approaching the goal of providing a platform in which designers can simply apply styling attributes to commonly used functional containers without them having to create a lot of additional new functions using JS. But it is 2017 and there are still lots of things, such as a tabbed container, that have not yet been modularized as independent HTML elements.
Why is that? Well, JS developers have already provided a relatively simple solution for such a tabbed container, as well as for many other common HTML elements, so the priority for HTML Developers is probably just relatively low compared to making other existing HTML elements work better.
So, why not just use tabbed containers that implement JS? Well, the more JS that must be used, the more difficult code can be to maintain, and the more complicated it can be to customize in the first place. Using the least amount of custom code to implement a functional block should always be the preferred choice; thus efforts to create a tabbed container using only HTML and CSS (no JS) will continue to be revisited by many folks until it is finally fully proven.
The code provided here is a great progression from what has been done in years past in that it offers a fully responsive container in which the content accordions down (instead of covering up subsequent content), but depending on the requirements of its usage, it still must use some JS if there is a need to un-select all tabs. Further discussion of this, and when all JS may be deleted, is addressed in the Documentation section below.
W3Schools provides a lot of great tutorials for web development, but in some regards, their examples are not always the most cutting edge. To their credit, they provide a great tutorial here, and another here, but as of today (3/9/2017) both examples of tabbed containers implement JS to handle the tab switching functionality, and both examples requires the method of closing the content to be in the manner of an additional “x” button on the inside of the content section; clicking directly on a selected tab label itself does not close the content. (If a new HTML element were created for a tabbed container like this, its labels ought to have an attribute associated that specifies whether or not those labels may be un-selectable.)
Other sources on the web seem to have been propagating a concept of a tabbed section created with only CSS and HTML, no JS, such as this (2014), and this (2012), (I saw others too, but I’ve lost their references) but each of those are very similar, and have some very major flaws, the biggest of which is that they are non-responsive. The other major flaw with these examples is that none of the examples provides a method of closing the content containers once one has been opened. So while these efforts should be applauded, they need to be accompanied with these caveats.
Taking what I had learned from those examples, I expanded upon them and came up with a couple different intermediate solutions that were responsive and were implemented using buttons for one, and radio buttons for another. Neither solution used JS, but both solutions had issues when it came to closing the content section. After trying to push CSS selectors to their limits, a realization that JS would be necessary if closing the content section was a requirement of the tabbed container. Henceforth I created a Stack Overflow question here to try to determine the best way to close this content section. After 40 people read the question and came up with no other solutions, I ended up answering my own question with a little JS and for the time being am going with that as the best solution to that problem. Perhaps someday someone will post a better JS solution there though. Note that this JS is not necessary if the content does not ever need to roll up (close) entirely (because a lot of tabbed containers don’t).
I could have stopped there, but then I also came across a great article here that proved a way to create nicer looking tabs using only CSS. Someone else (Tom) read that article, propagated the concept further, and provided his progression here. Unfortunately the HTML structure of my solution was quite a bit different and I had to do quite a bit of work to adapt Tom’s concept, and the CSS to work for my structure. In the end, I realized that Tom’s solution, although pretty, was not responsive, so I spent some additional time working to make it so, which for the most part, it now is.
This solution contains three parts of code, one for each HTML, CSS, and JS; but not all parts are necessary, nor are all sections of each part necessary. This Documentation attempts to explain in greater detail each of these sections and under which circumstances they need to be included. Furthermore it describes in moderate detail, the concepts of the components of each section in order to aid in customizing those sections.
Most everything in the HTML is necessary. Maintaining the structure of the HTML is also necessary in order for many of the important CSS selectors to work properly. That is perhaps the biggest disadvantage of this solution. On the other hand, the structure is very simple and quite intuitive. The only portion of the HTML that may be omitted in certain circumstances are the div tags inside the label tags; those should only be eliminated if the Pretty-Tabs section of CSS is NOT used. They are used to attach :before and :after geometry to the labels that help add curvature and lines to the extremity of the tab labels. Note, the comments are necessary to remove space between the labels which are displayed using inline-block. Flex wouldn’t work because we can’t change the structure of the HTML to put the labels into their own flex container separate from the content. Note also that the tab that is selected by default can easily be changed by switching the “checked” attribute to a different input.
The JS is entirely unnecessary unless you wish to incorporate the ability to close the content by clicking on the label of the opened tab. It uses a JQuery library and was tested with both versions 1.9 and 3.1.1. The algorithm is such that, first, we get the id of the initial checked radio (if one has been identified) in the HTML. Then, we determine which button was selected, we compare it to the previous value, and then we un-check it if they match. It seems like there may be a better way, but this is simple enough and works for now.
There are two large sections surrounded with comments identifying them as: 1. “ESSENTIALS” , and 2. “Pretty-Tabs”.
The ESSENTIALS section is necessary in all cases, though many of the attributes, like colors and borders, may easily be customized. When this section stands alone, the tab labels appear as simple rectangles, and the selected tab appears white among the non-selected grey tabs. This is about as basic as it can get!
Note that if more tabs are added in the HTML, then selectors for those labels that are referenced by ID must be duplicated for the new tabs in the CSS.
The Pretty-Tabs section is provided because the tabs formatted by the ESSENTIALS section are rather plain, and most people generally recognize tab containers, and their functionality, by the trapezoidal shape of their labels.
This section is quite customizable if you know what you are doing, henceforth, this documentation is provided to help you get started.
This section itself can be broken up into sub-sections, which generally may be eliminated from the bottom up, particularly in cases where the tab labels are expected to be viewed very close up (by zooming way in).
Basically, each label (and its divs) has :before and :after geometry applied that attach to the left and right sides of the label. Each of those blocks are skewed and have one edge and one rounded corner as borders, and the blocks are then positioned such that those borders are lined up to make the familiar trapezoidal shape you would expect to see of a tab label.
The first portion of the CSS sets attributes for the rectangular sections of the tabbed container (the content and the rectangle portion of the labels). There is also a special selector to manage the checked tab.
The second portion sets up large skewed rhombozoids on the left and right side of the label to create the primary diagonal lines and curves at the top of the tab labels. They stretch to the height of the label responsively and look and work pretty good on their own. There is also a special selector to manage the colors of these attributes for the checked tab.
The next section sets up smaller rhombozoids at the bottom edges of the label to complete the curves at the bottom. These are the most problematic in some zoomed circumstances and can quite easily be removed to leave behind a decently shaped trapezoidal tab if those circumstances arise. Alternatively, their widths, heights, and positions could be adjusted by calculated amounts using JS, but, at this point, I didn’t feel that was worth it, not to mention, it was a goal of this project to avoid using JS as much as possible. There is also a special selector to manage the colors of these attributes for the checked tab.
Towards the bottom, is a section that attaches an underline to the right side of the tab labels. This underline is simply for looks, to help distinguish parallelity of tab labels when the screen is squished small and when some tabs have to scoot to the next line. These lines are not necessary and can easily be removed. Additionally, if the tabs are aligned center or left, then this section will need to be customized appropriately (The :right selector would have to change to :left etc..) Note: Making those lines go full width across the screen seemed to be a difficult challenge in which I didn’t feel the need to pursue.
At the very end is a very short section that defines some hover effects on the tabs. Although unnecessary, they are nice to have, but can be customized quite extremely, thus they are left at the end.
It is also worth mentioning that dimensions for all of these sections have been carefully calculated. In this example, all of the tabbed components are based on them being 24px high. If you choose to enlarge them, by adding padding to the labels, or some other method, by say 0.5em, then you should locate all references to height for those components by the increased size. So, for example, everywhere you saw “24px”, you would then replace that with “calc(1em + 24px)”.
Note also that there are several places where “box-sizing:border-box;” was used to prevent the height of the containers from expanding by their border width. This seemed to be the best method of attempting to help keep the “absolute” elements (those that are “raised”, or “lifted”, or re-positioned somewhere on top of their anchored relative elements) lined up closely with their anchor elements of the same height.
Unfortunately, after analyzing various browser behavior more closely, a strange phenomenon seems to be occurring when zooming in, where thin lines seem to appear unexpectedly at the edges of some of the containers. The explanation of those lines that appear is this: When browsers zoom in on a page, they adjust the border widths of containers by a zoom factor. So for example, when you zoom in to a page at 120%, a 1px border then gets reduced to a 0.8px border and then the whole image is scaled up. This is fine in most cases, but the problem occurs with how browsers anchor absolute containers to parent relative containers. An absolute container (the one that is raised) is positioned with respect to the parent container, and in that case the border is always included in the height. The outside edge of the border of that absolute container is anchored to the inside corner of the parent border (or the outside edge of the content, more precisely). So you can re-position the absolute container by 1px to account for the with of the border of the parent container, but that only works when zoom levels equal 100%. At 125%, the borders of both containers shrink to .8px, but the offset of the absolute container remains at 1px and suddenly the content of these containers do not overlap exactly, and the renderer displays lines.
This is a browser problem, and applies to all absolute elements. Until the browser developers and CSS consortium recognize this as a problem and offer some way of dealing with this, the only real solution would be to apply some complicated JS to the absolute elements to position them appropriately according to the zoom factor, but honestly, I’m not so sure that is worth it right now.
An alternative solution would be to just not use any borders at all and remove all adjustments to the positioning of the absolute containers that account for border widths.
Created @ Green Dingle™ by Damian Green, © 2017