7 January 2013
HTML, CSS and some knowledge of new CSS3 features.
In this article, I run through some of these techniques and discuss the concepts involved. This should help you get a good overall understanding of how these tricks work, and may give you insight into creating something with pure CSS for one of your future web projects.
The fact is, the techniques mentioned in this article may not be good options because of the semantic problems and accessibility challenges that arise when using form elements in a way not intended.
So although this article has definite experimental and educational benefits for those learning CSS, take into consideration any drawbacks that might occur before using these techniques extensively in any project. Of course, if you're able to ensure you can use these without accessibility problems, then by all means do so. But please be forewarned that they could be problematic.
Many websites feature a "tab switcher". In brief, this is a single unit, or widget, sometimes appearing in the sidebar of a website, that has two or more content areas that the user can choose from. Only one of these content areas is visible at one time, and the options are displayed in the form of tabs at the top of the widget.
Most older tutorials and demos use list items, div elements, and jQuery to create this functionality. But in the past couple of years, a number of scripts and tutorials have begun to use radio buttons and some new CSS3 pseudo-classes to assist with the functionality of the tabs.
Let's run through how this might be done. Our markup can be divided into multiple sections. Each section might look something like the following markup:
<section class="tab"> <input type="radio" id="tab-one" name="tab-set" checked> <label for="tab-one">Tab #1</label> <div class="tab-content"> <p>First tab content goes here.</p> </div><!-- tab-content --> </section><!-- .tab (1) -->
This might be repeated three or four times. Then, we could have a wrapper with a class of
tab-switcher holding all these sections together. The key parts of the markup are the input elements with type
radio and their associated label elements.
Here is the example in JS Bin. Select CSS to see that code.
Although this example is using radio buttons, we don't actually see any radio buttons on the page. This is because the radio buttons are set to
display: none in our CSS. But if the radio buttons aren't visible on the page, how do they function?
Well, notice the markup also includes a label element that's associated with each radio button using IDs and for attributes. Although the radio buttons are not visible or clickable, the associated label elements are. And if you are familiar with label elements, you know that browsers allow the labels to be clicked to select their associated form elements.
Thus, all we have to do is style our labels to look like tabs (I used rounded corners and a few other styles) and then target them using the
:checked pseudo-class along with the general sibling selector (the tilde character, ~).
The general sibling selector lets us target both the content section of the selected tab, as well as the label element. Using
:checked in combination with the sibling selector lets us target only the parts of the tab switcher that are currently selected, or active.
With some absolute positioning along with z-index, we then can make the chosen section visible. In the demo, I also added some CSS3 transitions to prevent the switching from occurring too quickly.
To summarize this technique, here are the concepts to keep in mind:
:checkedpseudo-class in combination with the general sibling selector lets us target elements associated with the selected form element.
For our markup, similar to the tab switcher, we divide each accordion section using HTML5 section elements. We have a wrapper with a class of
accordion-wrap enclosing all five sections. Each section looks something like the following markup:
<section class="accordion-item"> <input type="checkbox" id="accordion-one" name="accordion-group" checked> <label for="accordion-one">Accordion Section #1</label> <div class="accordion-content"> <p>content here... </p> </div><!-- .accordion-content --> </section><!-- .accordion-item -->
The actual example has five of these, but I'm showing you just one for brevity. Again, the key parts of the code are the checkbox input element and its associated label element.
Our CSS is similar to the previous example, and once again we utilize the
:checked pseudo-class along with the general sibling selector. Here is the example in JS Bin. Select CSS to see that code.
This example uses checkboxes, which, unlike radio buttons, allow multiple choices to be selected. This means the different items in the accordion can all appear expanded at the same time.
The tabs example used a combination of opacity and z-index to show the selected item. But this time, we're using the max-height property. Any unchecked item starts out with a max-height value of 0, along with
overflow: hidden, so it's not visible. When an item is checked, we set the max-height to any large number that exceeds the height value of the largest item.
We use max-height and not the height property, because although height can be animated via CSS3, you cannot animate from 0 to auto. So we have to choose an arbitrary
max-height value, to ensure the height of the element doesn't extend far past the actual content it holds. (Credit to Lea Verou for this technique.)
Concepts to Remember
Here are the main points to remember in this example:
max-height: 0to a large max-height value that exceeds the tallest item.
This final technique is simply a form button that, when clicked, animates to reveal a text input element. Let's look at the markup first, which is quite simple:
<input type="text" class="txt" id="textfield"> <label for="textfield">Click to Type</label>
In this example, we're using nothing but a single text field with an associated label element, as in the following JS Bin example. Select CSS to see the related code.
In this example, we're using the
:focus pseudo-class to make the form field visible. The label element is styled to look like a button. It's positioned to appear on top of the text field, so the text field is not immediately visible.
We then use our super-handy sibling selector to animate the left property on the label element. This reveals the text field by moving the label element out of the way.
Take a look at this code in action at this JS Bin demo.
To summarize what we did in this last example:
:focuspseudo-class is used to detect if the user has clicked the label element, thus adding focus to the text field.
leftproperty to move the label out of the way when the user is ready to type.
As already mentioned, these techniques should be considered carefully before being used. First of all, older versions of WebKit had a bug that didn't allow the
:checked pseudo-class to work in conjunction with a sibling selector. Fortunately, not many people are using older versions of WebKit so this is less of a problem today.
The other problem occurs in WebKit on iOS5 or below, which doesn't allow a checkbox's state to change by touching a label element. A workaround for this is to add an empty inline
onclick handler to all label elements, as I did in the demos for this post. Additionally, iOS has slow reaction to label clicks that trigger input state changes.
A more significant problem, however, is the fact that the markup for these elements could cause problems with assistive technology devices like screen readers. This accessibility issue should be taken into consideration before implementing a method like these.
There has been discussion about the possibility of adding this type of behavior natively to elements with pure CSS, to prevent these bugs and accessibility problems, but that would be much further down the road and would not be implemented in all browsers for a very long time.
To deal with some of the cross-browser problems, you can attempt to use a selector polyfill like Selectivizr to fill in any gaps, but you might end up with buggy or unpredictable results, and, unless you provide an animation polyfill, transitions are missing.
Much of the credit and inspiration for this post is due to work done by a number of developers. Be sure to check out the articles and demos shown below if you want to delve more into this topic.
This work is licensed under a Creative Commons Attribution-Noncommercial-Share Alike 3.0 Unported License. Permissions beyond the scope of this license, pertaining to the examples of code included within this work are available at Adobe.