In this tutorial, we’re going to take a look at how to build a horizontal accordion slider using CSS 2D transforms and a bit of jQuery.
Typically, the trickiest part about this kind of horizontal accordion is getting the text on the accordion “spines” to display vertically, so people usually use images to display the text. It’s not simple, but using some CSS 2D transforms, we can actually make the text display vertically without using images.
2D transforms work in most modern browsers, but for those that don’t, we’re going to have our accordion degrade gracefully, without resorting to images.
Caveat: This isn’t a WordPress tutorial… yet. Wait for it, one step at a time!
Step 1: Photoshop
So let’s take a look at our design. It’s not going to win any awards, but it’s clean and straight forward. There are a few challenges related to there being borders between each spine, but we’ll address those with the CSS. For now, let’s just slice out the images.
Slicing The Image
When we slice out the spines, we’re going to take the borders on each side. The easiest way to do this is to just slice out one of the spines, and then save it for the web 4 times, changing the number each time.
If you’re following along with my PSD, the images are pre-sliced. We’ll also need the background and the main images.
A CSS sprite would be more efficient, but I’m trying to keep things simple.
While we’re going through the HTML and CSS, I’m actually not going to use the images at first, I’m just going to use regular CSS backgrounds, to keep things simple.
Step 2: HTML
The markup for our accordion is pretty simple. The whole thing is one unordered list, where each list item is a slide. Inside each list item, we have an anchor tag, which will be the trigger for the slide, and the slide content. Give the first slide a class of “active” so that we can style it differently if we like.
<div id="accordion"> <ul id="slides"> <li class="slide open active" id="slide-1"> <a class="slidebutton" href="javascript:void(0);">Jellyfishes of the Deep</a> <div class="slide-content"> <img src="images/image1.jpg" alt="image1" width="590" height="290" /> </div><!-- slide content --> </li><!-- slide 1 --> <li class="slide open" id="slide-2"> <a class="slidebutton" href="javascript:void(0);">Shark Week</a> <div class="slide-content"> <img src="images/image2.jpg" alt="image2" width="590" height="290" /> </div><!-- slide content --> </li><!-- slide 2 --> <li class="slide open" id="slide-3"> <a class="slidebutton" href="javascript:void(0);">Swimming with the Fishes</a> <div class="slide-content"> <img src="images/image3.jpg" alt="image3" width="590" height="290" /> </div><!-- slide content --> </li><!-- slide 3 --> <li class="slide open" id="slide-4"> <a class="slidebutton" href="javascript:void(0);">Dolphins & Friends</a> <div class="slide-content"> <img src="images/image4.jpg" alt="image4" width="590" height="290" /> </div><!-- slide content --> </li><!-- slide 4 --> </ul> </div><!-- accordion -->
Step 3: CSS
First, we’re just going to do a basic reset and get some of the structure set. The only really important thing here is the width of the list items: it should be the width of a slide, including the trigger, but not including the leftmost border.
The slide content width is just the width of the content images. The 50px margin refers to the width of each trigger.
html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section { margin: 0; padding: 0; border: 0; font-weight: inherit; font-style: inherit; font-size: 100%; font-family: inherit; vertical-align: baseline; } #accordion { position: relative; width: 795px; height: 290px; margin: 60px auto; } #slides { list-style: none; width: 795px; overflow: hidden; height:290px } #slides li.slide { overflow: hidden; clear: both; width: 640px; background: #00202f; height: 290px; } #slides .slide-content { width: 590px; height: 290px; color: #444; margin-left: 50px; padding: 5px 0; }
Right now we just have a semi-styled list of images with room for our trigger spines.
Next we need to get all those slides to pile on top of eachother, instead of coming one after the other. To do this, we’re going to use some clever absolute positioning and z-index values. Well, not that clever. But handy. The left values for each slide are just multiples of the 50px triggers.
#slide-1 { position: absolute; top: 0; left: 150px; z-index: 4; } #slide-2 { position: absolute; top: 0; left: 100px; z-index: 3; } #slide-3 { position: absolute; top: 0; left: 50px; z-index: 2; } #slide-4 { position: absolute; top: 0; left: 0px; z-index: 1;}
Now comes the tricky part: styling the triggers with vertical text. Let’s take a look at the code then go over the tricky bits:
#slides a.slidebutton { display: block; -webkit-transform: rotate(270deg); -moz-transform: rotate(270deg); -webkit-transform-origin: 120px 120px; -moz-transform-origin: 130px 130px; width: 300px; height: 50px; position: absolute; top: 70px; text-align: right; }
Okay, so after we set the display to block (because anchors are inline), we’re rotating the text around 270 degrees (3/4 of a rotation). Because we’ve rotated the element, height is now width and width is now height, so we set the height to 50 and the width to 300. Similarly, the padding-top actually refers to padding to the left padding on the text.
I’m going to admit, I’m not really sure how the transform-origin property works, all I know is that I tried adding it on a whim, it turned out to be the secret ingredient that makes this all work. I messed around with the values for a while until I found one that worked.
If you’re using different sized elements than me, you’re going to have to adjust both the -transform-origin values and the top value. I suggest using [Firebug for Firefox] or just the webkit Inspector to view your changes real-time in the browser.
Anyways, so now we’ve got what’s starting to look like an accordion slider. Time to animate this puppy!
Step 4: jQuery
Now, I can’t take credit for the javascript in this tutorial, I found it and modified it from [this website] ages ago when I was building an old version of my portfolio.
$(document).ready(function() { $('.slide') .bind('open', function(){ if(! $(this).hasClass('open')){ $(this).next().trigger('open'); $(this).addClass('open'); $(this).animate({left: "-=590px"}); } else{ $(this).prev().trigger('close'); } $(this).siblings().removeClass('active'); $(this).addClass('active'); }) .bind('close', function(){ if($(this).hasClass('open')){ $(this).removeClass('open'); $(this).animate({left: "+=590px"}); $(this).prev().trigger('close'); } }); $('.slidebutton') .click(function(){ $(this).parent().trigger('open'); $('#content-' + $(this).parent().attr('id')).trigger('show'); }); });
You can more or less copy and paste this code into your own project, the only value that needs to be adjusted is the animation, which should be equal to the width of your slide content.
Step 5: Make it pretty
Now that we have our ugly, but functional, slider working, let’s add in those images from before to make it pretty.
We’re going to add in the spine background images to each slide. Make sure to remove the background colour from the list elements.
#slide-1 { position: absolute; top: 0; left: 150px; z-index: 4; background: url(images/spine1.jpg) no-repeat left; }
Next we add a bit more styling to the text:
#slides a.slidebutton { display: block; width: 290px; height: 55px; position: absolute; -webkit-transform: rotate(270deg); -moz-transform: rotate(270deg); -webkit-transform-origin: 120px 120px; -moz-transform-origin: 130px 130px; top: 70px; padding-top: 15px; text-align: right; color: #fff; text-decoration: none; font-family: Helvetica, Arial, sans-serif; font-size: 18px; text-shadow: 1px 2px #077691; }
And finally add in the background:
body {background: #012d42;} #accordion { position: relative; width: 795px; height: 290px; margin: 60px auto; background: url(images/bg.jpg) no-repeat; padding: 5px 0; }
The finished product
Download the PSD
And there we have it, a fully functional horizontal accordion with vertical text, sans images. Stay tuned for part 2, where I discuss cross-browser application (this only works properly in Firefox, Chrome & Safari) and graceful degradation, and part 3: WordPress!
Edit: I still haven’t been able to get my Windows virtual machine up and running since my hard drive crash, so perhaps I’ll do the WordPress first. Or you’ll just have to wait… I can’t find my Windows install disks
i have added background image with spin.jpg
but nothing happens??huh