Image Accordion with CSS3

In this tutorial we will create an image accordion that will expand an item on click. Using the sibling combinators and a nested structure we can control the opening of the items/slides with radio buttons.

Image Accordion with CSS3

Today we are going to create a CSS-only image accordion. We’ll use a nested structure and apply a technique of radio buttons and the sibling combinator in order to control the slides. The idea is to make each item, or slide, “clickable” by overlaying the radio button over the entire slide, and change the position of a child element when clicking on it.

The images used in the demo are from the “L’aquarelle” project by Andrey & Lili: L’aquarelle on Behance. It is licensed under the Creative Commons Attribution-NonCommercial 3.0 Unported License .

Please note: the result of this tutorial will only work as intended in browsers that support the respective CSS properties.

The Markup

The main idea is to create a nested structure that will allow us to simply move the respective accordion slides to one side, i.e. moving one slide will move all its inner slides (the nested elements). Each of the figures will have the image, a radio button for controlling the “opening” of a slide, and a caption.

<div class="ia-container">
				
	<figure>
		<img src="images/1.jpg" alt="image01" />
		<input type="radio" name="radio-set" checked="checked" />
		<figcaption><span>True Colors</span></figcaption>
		
		<figure>
			<img src="images/2.jpg" alt="image02" />
			<input type="radio" name="radio-set" />
			<figcaption><span>Honest Light</span></figcaption>
			
			<figure>
				<!-- ... -->
				
				<figure>
					<!-- ... -->
					
					<figure>
						<!-- ... -->
						
						<figure>
							<!-- ... -->
				
							<figure>
								<!-- ... -->										

								<figure>
									<!-- ... -->
								</figure>
								
							</figure>
				
						</figure>	
							
					</figure>	
						
				</figure>
					
			</figure>
			
		</figure>
		
	</figure>
	
</div>

You could also use different kind of content here, what is important is the nested structure and the radio input.

The CSS

We’ll give the slider a width value and hide the excess part with overflow hidden.

.ia-container {
	width: 685px;
	margin: 20px auto;
	overflow: hidden;
	box-shadow: 1px 1px 4px rgba(0,0,0,0.08);
	border: 7px solid rgba(255,255,255,0.6);
}

The width is calculated as follows:

( (Number of images - 1) * 50px ) + 335px

where 50 is the width of the visible piece of an image and 335 is the width of the image. In our case, that gives us ( (8 – 1) * 50px ) + 335px = 350px + 335px = 685px.

Each of the figures will have a left value of 50 pixel (visible piece). That should be their position relatively to their parent. This makes the slider look like an accordion. The width of a figure is 335 pixel, just like the image inside. The figure will also have a transition for a smooth movement:

.ia-container figure {
    position: absolute;
	top: 0;
	left: 50px; /* width of visible piece */
	width: 335px;
    box-shadow: 0 0 0 1px rgba(255,255,255,0.6);
    transition: all 0.3s ease-in-out;
}

The first figure will be positioned completely to the left and we set the !important because we will use media queries later and we always want the left to be 0 for this figure. The position will be set to “relative” in order to give the slider a height:

.ia-container > figure {
    position: relative;
	left: 0 !important;
}

The image will have a width of 100% so that it fills the figure:

.ia-container img {
	display: block;
	width: 100%;
}

Each of the radio buttons will cover the visible part of a slide. We set the opacity to 0 so that we can’t see it but still be able to click it. The z-index should be high, so that it covers everything else:

.ia-container input {
	position: absolute;
	top: 0;
	left: 0;
	width: 50px; /* just cover visible part */
	height: 100%;
	cursor: pointer;
	border: 0;
	padding: 0;
    opacity: 0;
	z-index: 100;
	-webkit-appearance: none;
	-moz-appearance: none;
	appearance: none;
}

When an input is selected or clicked, we want the input to disappear. Actually, we could say something like width: 0px but because of some kind of strange behavior in the Chrome browser we still need it at the right side (the next item won’t trigger the hover if we haven’t hovered over the current input). We also want the sibling figures to move to the left with a transition:

.ia-container input:checked{
	width: 5px;
	left: auto;
	right: 0px;
}
.ia-container input:checked ~ figure {
    left: 335px;
    transition: all 0.7s ease-in-out;
}

Giving two different transition durations (one in the “default” state, one when we check an input) will give us the “shuffle” effect. Playing with these values will give you different effects.

The caption of the figure will cover all the element with a transparent dark overlay and the span will be positioned over the upper half of the figure initially:

.ia-container figcaption {
	width: 100%;
	height: 100%;
	background: rgba(87, 73, 81, 0.1);
	position: absolute;
	top: 0px;
    transition: all 0.2s linear;
}

.ia-container figcaption span {
	position: absolute;
	top: 40%;
	margin-top: -30px;
	right: 20px;
	left: 20px;
	overflow: hidden;
	text-align: center;
	background: rgba(87, 73, 81, 0.3);
	line-height: 20px;
	font-size: 18px;
    opacity: 0;
	text-transform: uppercase;
	letter-spacing: 4px;
	font-weight: 700;
	padding: 20px;
	color: #fff;
	text-shadow: 1px 1px 1px rgba(0,0,0,0.1);
} 

When a slide gets selected, we will remove the overlay by setting the opacity value of the RGBA to 0:

.ia-container input:checked + figcaption,
.ia-container input:checked:hover + figcaption{
	background: rgba(87, 73, 81, 0);
}

The span will fade in and move from the top with a delay (the slide should open first):

.ia-container input:checked + figcaption span {
    transition: all 0.4s ease-in-out 0.5s;
	opacity: 1;
	top: 50%;
}

Since the last slide does not have any other slides coming from the right when we select it, the delay does not need to be as high (we’ve given the last radio input an id ia-selector-last):

.ia-container #ia-selector-last:checked + figcaption span {
	transition-delay: 0.3s;
}

When we hover over the visible piece of a closed slide, we want a slight hover effect. But because the radio input is covering this part (it’s on top of all the other elements) we will use the adjacent sibling combinator (this requires that the input is directly followed by the figcaption element):

.ia-container input:hover + figcaption {
	background: rgba(87, 73, 81, 0.03);
}

All the following siblings of a checked input should get a low z-index, again to prevent some unwanted behavior in Chrome:

.ia-container input:checked ~ figure input{
    z-index: 1;
}

And finally, we’ll add some media queries to make the slider responsive:

@media screen and (max-width: 720px) {
    .ia-container { 
		width: 540px; 
	}
	
	.ia-container figure { 
		left: 40px; 
		width: 260px; 
	}
	
	.ia-container input { 
		width: 40px; 
	}
	
	.ia-container input:checked ~ figure { 
		left: 260px; 
	}
	
	.ia-container figcaption span { 
		font-size: 16px; 
	}
}

@media screen and (max-width: 520px) {
    .ia-container { 
		width: 320px; 
	}
	
	.ia-container figure { 
		left: 20px; 
		width: 180px; 
	}
	
	.ia-container input { 
		width: 20px; 
	}
	
	.ia-container input:checked ~ figure { 
		left: 180px; 
	}
	
	.ia-container figcaption span { 
		font-size: 12px; 
		letter-spacing: 2px; 
		padding: 10px; 
		margin-top: -20px; 
	} 

}

That’s all, hope you like it!

Tagged with:

Rey Wang

Rey is a digital product designer based in Beijing, China. Contact & collaboration will be welcome.

Stay up to date with the latest web design and development news and relevant updates from Codrops.

Feedback 22

Comments are closed.
  1. INCREDIBLE!! Wow nice work… Now if only IE6-9 could die, we could use this everytime 🙁

  2. All nice.. but the problem is IE, until IE has a market share of 30-40% this is all experimental :'(

    • It was warned in the beginning of the article that was the case. IE10 will be able to utilize these features and their new OS will urge that version be what most current IE users upgrade more than they ever have.

      Highly suggest a reasonable fallback solution with Modernizr…

  3. This is awesome really good work Ring Wing, like the way remembered to include media queries in this to make it responsive this is a step which is often forgotten on accordions like this.

  4. nice one! just a question. is it possible to make it work for mouse hover, not click? how does the trigger work? wit the :checked?
    thank you!

    • Hi Bogdon, did you ever figure it out to make it hover and not clicked? I would like to use the script for an iPad mag and i need to show the pic on click down or hover (i think)…

  5. new question 🙂 how to change the animation? just to simple slide? thank youu

  6. Really amazed to read this article that CSS3 transistion property can also work in this way.
    Nice explanation
    Thanks !

  7. I am new to CSS.. but could someone tell me how I could create a link for each image.. tried <a> around the img tag but I don’t think that’s supposed to work..;-)..

    • I created a link for the labels of each picture. Like this..
      <a href="YOUR URL">True Colors</a>

  8. While I’m aware you can nest figures inside a parent figure tag, why must we do it here; why figures siblings to each other (figure+figure or ) would not be a recommended approach here?

  9. Wow this looks amazing. Any chance of a jQuery version that we can use until IE doesnt exist?

  10. It´s great! I´m trying to add a link at the bottom of teh caption or make it clickable…anybody did it already?

    Thanks for this.

  11. Is possible for each individual image be a link, i have tried wrapping the anchor tag (<a>) around the figure and the figcaption, but its not working. Also, anytime i click on the image it goes straight to the top of the page. since i embedded the accordion in a lower part of my website. any help please.

  12. In the live demo I can see al 8 images, but when I download the code and run the index.html file I can see only 4

    I am using Edge / Win10 all up to date 18/06/2018