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 .
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!