How to Create a CSS3-Only Horizontal Accordion Using the :target Selector

The CSS3 :target selector is incredibly powerful and allows us to create attractive widgets which would have required ninja-like JavaScript skills a few years ago. We’ve already built a tab control and a vertical accordion. In this post, we’ll create a horizontal accordion using the same HTML5 markup.

View the demonstration page…

The solution works in IE9, Chrome, Firefox, Safari and Opera and doesn’t require JavaScript or images. Again, it fails miserably in IE6, 7 and 8 so you either need a JavaScript shim such as selectivizr or some other fallback.

The HTML

Here’s a reminder of the HTML5 code. It’s identical to our vertical accordion: there are a number of section elements with clickable headings contained in the initial h2 tag:


<article class="accordion">

	<section id="acc1">
		<h2><a href="#acc1">Title One</a></h2>
		<p>This content appears on page 1.</p>
	</section>
	
	<section id="acc2">
		<h2><a href="#acc2">Title Two</a></h2>
		<p>This content appears on page 2.</p>
	</section>
	
	<section id="acc3">
		<h2><a href="#acc3">Title Three</a></h2>
		<p>This content appears on page 3.</p>
	</section>
	
	<section id="acc4">
		<h2><a href="#acc4">Title Four</a></h2>
		<p>This content appears on page 4.</p>
	</section>
	
	<section id="acc5">
		<h2><a href="#acc5">Title Five</a></h2>
		<p>This content appears on page 5.</p>
	</section>

</article>

The CSS

The CSS is a little more complex but it’s worth it. First we style our outer article container:


article.accordion
{
	display: block;
	width: 43em;
	margin: 0 auto;
	background-color: #666;
	overflow: auto;
	border-radius: 5px;
	box-shadow: 0 3px 3px rgba(0,0,0,0.3);
}

Each section starts in its closed state; they’re floated to the left and have a width of 2em. Overflow is set to hidden and we’re also making the text color the same as the background so they effectively look like solid blocks without content:


article.accordion section
{
	position: relative;
	display: block;
	float: left;
	width: 2em;
	height: 12em;
	margin: 0.5em 0 0.5em 0.5em;
	color: #333;
	background-color: #333;
	overflow: hidden;
	border-radius: 3px;
}

Now for some nasty prefixed CSS3! Each h2 title is rotated 90° counter-clockwise using a transform and absolutely positioned over the closed section:


article.accordion section h2
{
	position: absolute;
	font-size: 1em;
	font-weight: bold;
	width: 12em;
	height: 2em;
	top: 12em;
	left: 0;
	text-indent: 1em;
	padding: 0;
	margin: 0;
	color: #ddd;
	-webkit-transform-origin: 0 0;
	-moz-transform-origin: 0 0;
	-ms-transform-origin: 0 0;
	-o-transform-origin: 0 0;
	transform-origin: 0 0;
	-webkit-transform: rotate(-90deg);
	-moz-transform: rotate(-90deg);
	-ms-transform: rotate(-90deg);
	-o-transform: rotate(-90deg);
	transform: rotate(-90deg);
}

article.accordion section h2 a
{
	display: block;
	width: 100%;
	line-height: 2em;
	text-decoration: none;
	color: inherit;
	outline: 0 none;
}

We can now ‘open’ the active section using the :target selector. The section width and colors are changed and the title is moved back to the top:


article.accordion section:target
{
	width: 30em;
	padding: 0 1em;
	color: #333;
	background-color: #fff;
}

article.accordion section:target h2
{
	position: static;
	font-size: 1.3em;
	text-indent: 0;
	color: #333;
	-webkit-transform: rotate(0deg);
	-moz-transform: rotate(0deg);
	-ms-transform: rotate(0deg);
	-o-transform: rotate(0deg);
	transform: rotate(0deg);
}

That’s fine, but a CSS3 transition makes it look fantastic:


article.accordion section,
article.accordion section h2
{
	-webkit-transition: all 1s ease;
	-moz-transition: all 1s ease;
	-ms-transition: all 1s ease;
	-o-transition: all 1s ease;
	transition: all 1s ease;
}

View the demonstration page…

It’s a shame IE6, 7 and 8 users can’t use the widget. IE9 users won’t see animation either. But you’d need a lot of time and patience to achieve the same effect using JavaScript! Have fun with it.

Win an Annual Membership to Learnable,

SitePoint's Learning Platform

  • Matt

    A major drawback to this technique: when following a link to a fragment identifier, the browser will scroll to where the element is on the page. This can be observed if you squish the demonstration page into a tiny window so there is a vertical scroll bar; now clicking the according causes it to scroll to the top of the viewport. The same is true if you use this technique for tabs.

    “Not such a big deal” I hear you cry! If this widget is ever placed on a page that scrolls it pretty much makes the technique unusable. A fix would be to use preventDefault with JavaScript – but now, if we have JavaScript available, we might as well use JavaScript to switch out the tabs/according sections too… I’d really like to use this technique, but it’s just not (currently) viable.

    • http://www.optimalworks.net/ Craig Buckler

      Thanks Matt. It will jump to the correct part of the page, but how is that different to using a #fragment in any link? I agree that a little JS would make it better, though.

  • sathish

    Good

  • http://www.itmitica.com itmitică

    Three things, and I’m done with this “should be javascript not CSS”.

    1. The fact that it relies on fragments, it makes for a frustrated user. We’ve been over this one and I didn’t get a logical reason as to why it is desirable for the user to look at this one as a feature. For me it’s a clear drawback, and even more, it’s inconsistent behavior if you don’t provide TOCs for fragments in every one of your pages.

    2. A quick web search for “jquery plugin horizontal accordion” reveals at least a few quality results that stand against your “But you’d need a lot of time and patience to achieve the same effect using JavaScript!”

    3. I have no strong opinion about the use of this CSS and :target solution by anyone that wants to. I have a strong opinion when it come to categorizing this working compromise as a natural evolution. This programmatic behavior belongs in javascript while CSS should do presentation better. The link hover/active example you gave is not applicable by similarity since in that case CSS doesn’t decide what the link does, it only decides how it looks like.

    Done done and done :)

    • http://www.optimalworks.net/ Craig Buckler

      Hi again!

      Remember you can, if desired, add TOCs or links anywhere on the page to a section within the accordion. I agree that the HTML is coded differently — and perhaps less semantically — than you’d normally see for a tab-like control. Whether that’s a problem will depend on your use-case.

      Do the JavaScript versions you discovered use CSS3? You’d have a tough time implementing rotations and other effects without them.

      I don’t think we’ll ever determine whether this is or isn’t programmatic behavior. But I will leave you with this: it is a far simpler solution.

      • http://www.itmitica.com/en/ itmitică

        It appears to me that in the heat of discussion I forgot to acknowledge your hard work in putting together these CSS3 techniques. Sorry for that :) Nice work!

      • http://www.optimalworks.net/ Craig Buckler

        You’re welcome, itmitica.

  • RidleyO

    Fun experiment but the current state is not usable in production, as it doesn’t degrade in ie8. (Unless your audience is sure not to use that browser….)

    • http://www.optimalworks.net/ Craig Buckler

      It’ll work if you add a JavaScript shim such as selectivizr.

  • noname

    Amazing css3 goodness, looking forward of using these techniques
    Thank you

  • FAIK SAID KHAMIS ZNZ

    nice clas