Create the accordion effect using CSS3

Recently I have been playing around with CSS transitions and animations as implemented in webkit based browsers such as Safari and Chrome. They have been submitted to the W3C for consideration in the CSS3 spec so hopefully we should see more browsers support this soon, Firefox 3.5 supports CSS transforms which was developed by the webkit people to work alongside CSS animations & transitions.

To continue my effort to accomplish tasks in CSS that are usually reserved for JavaScript, such as my Futurebox and CSS based iPhone orientation detection. I have developed a CSS based version of the popular “accordion effect” that utilises the webkit CSS transitions. Like the Futurebox demo I’m utilising the CSS3 :target pseudo class to know which item to show based the URI fragment identifier (the # in the url).

It should be noted that this works best in a webkit based browser such as Safari 3+, Chrome or iPhone. Other browsers that support the :target pseudo class will still function on the core level but won’t animate the showing and hiding. The following browsers have been tested and work with this demo.

  • Firefox 1.5+
  • Opera 9.6+
  • Safari 3+
  • Chrome 1+
  • IE6+ – IE solution

The xhtml

<dl>
	<dt><a href="#Section1">Section 1</a></dt>
	<dd id="Section1">
		<p>
			Lorem ipsum dolor sit amet...
		</p>
	</dd>
	<dt><a href="#Section2">Section 2</a></dt>
	<dd id="Section2">
		<p>
			Lorem ipsum dolor sit amet...
	</dd>
	...
</dl>

We setup the accordion using a definition list to create the foundation so we can show and hide the definition data (dd) tag when the user clicks the anchor link inside the definition title (dt) tag.

The CSS

dl
{
	padding: 10px;
	min-width: 960px;
}
	dl dt
	{
		-webkit-border-radius: 5px;
		-moz-border-radius: 5px;
		border: 1px solid #cccccc;
		margin: 0;
	}
		dl dt a
		{
			color: #ffffff;
			font-weight: bold;
			text-decoration: none;
			padding: 10px;
			display: block;
		}
	dl dd
	{
		margin: 0;
		height: 0;
		overflow: hidden;
		-webkit-transition: height 1s ease;
	}
		dl dd p
		{
			padding: 10px;
			margin: 0;
		}
	dl dd:target
	{
		height: auto;
	}
 
@media (-webkit-transition) {
	dl dd:target
	{
		height: 6.667em;
	}
}

Pretty simple CSS involved, the dd tag is hidden by setting the height to 0 and the overflow to hidden.

-webkit-transition: height 1s ease;

This property on the dd tag lets webkit browsers know we wish to transition the height value over 1 second period using the ease timing function this transition will only happen when the height of the dd tag is changed. We can also express this in a long hand version.

-webkit-transition-property: height;
-webkit-transition-duration: 1s;
-webkit-transition-timing-function: ease;

To change the height we use the :target pseudo class to set the height of the dd tag to auto so the right content will show based the URI fragment identifier. For webkit browsers it’s a little different.

Webkit media queries

In webkit browsers there are additional media queries available so we can target browsers that support the extended features such as transitions and not affect other browsers. In this demo I use the @media (transition) media query.

Webkit implements this feature by using their -webkit vendor extension so the media query looks like the following

@media (-webkit-transition) {
	dl dd:target
	{
		height: 6.667em;
	}
}

Unfortunately setting the height of the dd tag to auto will not make it animate although this would be ideal and much more capable of catering for different sized content it’s not possible at the moment. For now we have to set the height to an actual value, to keep the height in line with any text resizing I set the height using em based value so if the user has larger text the height will adjust and won’t cut of any content. The height is 80px we divide by the base font size, which is 12, and we get 6.667em.

What about IE

Unfortunately IE doesn’t support the :target pseudo class and won’t work as describe above, but that didn’t stop me! Take a look at working example that functions in IE6 and up.

This is quite hacky and involves a bit of IE conditional comments.

IE xhtml

<dl>
	<!--[if IE]>
		<a href="#Section1" class="ie"><div>
	<![endif]-->
	<dt>
		<!--[if !IE]>-->
			<a href="#Section1">
				<!--<![endif]-->Section 1<!--[if !IE]>-->
			</a>
		<!--<![endif]-->
	</dt>
	<dd id="Section1">
		<p>
			Lorem ipsum dolor sit amet...
		</p>
	</dd>
	<!--[if IE]>
		</div></a>
	<![endif]-->
	...
</dl>

As you can see there is conditional comments so I can wrap the dt and dd tag in an anchor so we can get it functioning in IE using the following CSS. I also use the conditional comments to hide the anchor that appears in the dt tag only for IE browsers. IE6 was not functioning with just the anchor around the dd & dt so I added a div inside the anchor. In IE6 the first anchor would surround all the items, the div fixes that. Demo, demo files and example code has been updated to reflect that.

IE CSS

dl a.ie { text-decoration: none; }
	dl a.ie dd { display: none; }
 
/* Fix IE6 hover bug */
dl a.ie:hover { background-color: #606061 !important; }
 
dl a.ie dt
{
	color: #ffffff;
	font-weight: bold;
	text-decoration: none;
	padding: 10px;
	display: block;
}
 
dl a.ie:hover dd,
dl a.ie:active dd,
dl a.ie:focus dd
{
	height: auto;
	color: #cccccc !important;
	display: block;
}

Pretty simple stuff, set the text-decoration so the content isn’t underlined. We need to hide the dd tag as it causes issues in IE7 and below when trying to hover over any items below the first section. Next a background-color is applied to the :hover pseudo class of the surrounding anchor to fix an issue in IE6 that won’t trigger a hover unless something like a background-color is applied it. To make it work in IE we utilise the :hover, :focus and :active pseudo classes. That way when the user hovers in IE the content gets revealed, we also simulate a “click” by using the :active pseudo class. The :focus pseudo class allows us to make it work by using keyboard navigation, tabbing to the anchor will reveal the content. All the mark-up is XHTML 1.0 Strict complaint.

I think this is a pretty good attempt and best of all it works in all major browser so it can be potentially be used in a production environment.

Post filed under: css.

Skip to comment form.

  1. Noclegi says:

    Thank you very much this will help moving forward.

  2. Sweet says:

    It’s great effect. I love it:)

  3. take flight says:

    Is there a way to use this modify this code to make it work horizontally?

  4. Ryan Seddon says:

    @take flight

    Of course! If you adjust the CSS you can make it show horizontally and the rather than do height: auto; you use width and floats.

  5. Bram de Haan says:

    Beside the point of this great tutorial, but anyway one could include a pure CSS dropdown-icon:

    <a href="#Section1" rel="nofollow">Section 1</a>
    /* Dropdown-icon */
    .dropdown-icon { 
      border-color: #EEE transparent transparent; 
      border-right: 5px dashed transparent; 
      border-style: solid dashed dashed; 
      border-width: 5px 5px 0; 
      display: inline-block; 
      font-size: 0; 
      height: 0; 
      left: 6px; 
      line-height: 0; 
      position: relative; 
      top: 0; 
      width: 0; 
    }
    
  6. Shawn says:

    I was curious if besides reloading the page, there is a way to load all pleats of the accordion closed. My thing is that I need to have the accordion start off closed and then I need the pleats to close when folks click other pleats, but also, I was hoping to have this set up so that it can close if someone clicks the pleat again. I think that someone else asked a similar question, but I didn’t fully follow the response.

    Thanks!
    Shawn

  7. Micha says:

    Hi Ryan,

    sorry for my English I’m from Germany1

    very Nice Tutorial but i have a little problem. I created a list which is larger than my display. If i click on one of the sections my page skips down to the section and this is not very nice.

    I hope you can help me! THANKS A LOT

  8. Ryan Seddon says:

    @Shawn

    Unfortunately due to the nature of anchor links clicking it again won’t remove the anchor from the address bar which the :target pseudo-class relies upon. That sort of functionality would require javascript.

  9. Fabiola Singh says:

    hello, I implement your accordion in my page, looks good but i have one concern, for some reason the firsts links is hidden, if you try to click on the 2nd o 3rd section i have to move the mouse and scrool up to find the text.
    https://globalcallforwarding.com/GlobalCallForwarding-web/FAQS.htm

    Thanks for your help.

  10. daniel says:

    I’m having a crack at html5/css3. every example for an accordian results is the page jumping around each time the transition is started,doesn’t work with height:auto; and the extras required for ie are a joke.

    has anyone found a solution for this?

    this is a good tutorial thanks for the efforts, but a terrible solution. html5 is definitely not the flash killer it claims to be.

  11. Very great implementation . You’ve created great tutorials.

    Thanks for sharing!

  12. #nycmixing says:

    Great code!

    I tested your first code in IE9 and it works fine, but all of the windows expand in IE7 versus your second code which only opens windows when you hover over them. Do you have a fix for IE7 for your first code. I’m trying to integrate a CSS accordion that will keep a window open even when the mouse is no longer hovering, but will close when another category is clicked.

    Thank you.

  13. Gilberto Cocchi says:

    Nice, but works only when already defined the height you wanted, that’s bad.
    Mine version of accordion works with variable height, waiting from browser to calculate that. Also handle orientation change etc.
    Works well on webkit browsers, also implemented for browsers that does not support CSS3.
    Example: m.staples.com

  14. Ali says:

    Very nice accordion example. Thanks for tutorial.

  15. Michael says:

    Hey, I was wondering how can I set individual heights for each drop down?

  16. Ryan Seddon says:

    @Michael – You could add a class name to each revealed container, or use the css3 :nth-child() selector

  17. Olivia says:

    Hi Ryan, this is great.
    How would you do that with the nth-child() sector? or the class name. Like Michael I am in the need to set individual heights…

  18. Ryan Seddon says:

    @Olivia – Simplest way would be to add class names to set individual heights on the content.

    dl dd:target.container-1 {
        height: 100px
    }
    

    You’d do that for each one that requires it’s own height.

    Alternatively I’d recommend looking at my other implementation of bootstrap plugins in CSS. The accordion (collapse) version can handle varying heights in content without having to set individual classes.

  19. Tom says:

    I am trying to make the section title background color change when it is targeted. I have tried varying combinations when dl dd is target and i am able to change the background of each section but not the section title. Could someone please help? I am trying to go from #2d2d2d to #d72130 when it is clicked. THANK YOU!

  20. Ryan Seddon says:

    @Tom – Not possible using :target unfortunately.

    However take a look at my bootleg.css project this has a accordion using checkboxes instead http://labs.thecssninja.com/bootleg/#collapse which is easy to style on state change.