Playing with Sibling-Selectors (and Pseudo-Elements): CSS Slider.
Peter Adomokai
Senior Software Engineer (Fullstack Developer) and Interaction Designer
So a few days ago, I came across CSS Sliders and immediately had a few ideas about how to use it to create custom scrollspy effects and a simple 2 page slider with slick animations without the use of Javascript like below.
Unpopular opinion, I love JS but if you can achieve something without the use of JS. I think it's advisable to do so so even users with JS disabled would still get an optimum experience.
So let me introduce to the star of the show and the reason I went down the rabbit hole. The tilde (~) sign enables you to change the behaviour of an element based on the behaviour of the previous sibling.
<div class="first-sibling"></div>
<div class="second-sibling"></div>
<div class="third-sibling"></div>
In CSS, you have two kinds of sibling selectors; + and ~. The + selector allows the first-sibling to affect the second-sibling but the ~ selector allows the first-sibling to affect the second and third-sibling.
Setting the stage
<input type="checkbox" class="check">
<div class="absolute"></div>
<div class="absolute"></div>
So in our example here, we want the checkbox to act as a button. Since CSS has a :checked pseudo-class we can use this with the sibling-selector to change the behaviour of the absolute divs.
In the absolute divs, we set up the content we want to show. I'll provide the HTML and CSS I used in my example for reference although you can set it up anyway you want.
<div class="absolute"><div class="row"><div class="col-60"><div class="text"><p>Captivating Header in Bold</p><p>Lorem ipsum dolor sit amet consectetur adipisicing elit.
Facilis quia ad dignissimos odio aspernatur nesciunt autem
architecto magnam nam ipsam? Quod culpa at, quam officia
quasi natus quae quia asperiores!</p></div></div><div class="col-40"><img height="700" alt="image" src="image.png"></div></div>
</div>
<div class="absolute"><div class="row"><div class="grid"><div><img alt="img" src="cost.png" width="80" height="auto"><p>Lorem ipsum dolor, sit amet consectetur adipisicing elit.
Repellat laboriosam debitis id delectus? Dicta accusantium
ullam aliquam ad expedita itaque. Fugit explicabo earum
mollitia placeat eos minima, maiores iure nihil?</p></div><div><img alt="img" src="innovative.png" width="80" height="auto"><p>Lorem ipsum dolor, sit amet consectetur adipisicing elit.
Repellat laboriosam debitis id delectus? Dicta accusantium
ullam aliquam ad expedita itaque. Fugit explicabo earum
mollitia placeat eos minima, maiores iure nihil?</p></div><div><img alt="img" src="productivity.png" width="80" height="auto"><p>Lorem ipsum dolor, sit amet consectetur adipisicing elit.
Repellat laboriosam debitis id delectus? Dicta accusantium
ullam aliquam ad expedita itaque. Fugit explicabo earum
mollitia placeat eos minima, maiores iure nihil?</p></div><div><img alt="img" src="secure.png" width="80" height="auto"><p>Lorem ipsum dolor, sit amet consectetur adipisicing elit.
Repellat laboriosam debitis id delectus? Dicta accusantium
ullam aliquam ad expedita itaque. Fugit explicabo earum
mollitia placeat eos minima, maiores iure nihil?</p></div></div></div>
</div>
Now for the CSS.
.row {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
width: 100%;
height: 100vh;
}
.col-40, .col-60 {padding: 0 5%; transition: 0.5s all ease}
.col-60{
width: 60%;
height: 100%;
display: flex;
flex-direction: column;
justify-content: center;
}
.col-40{
width: 40%;
text-align: center;
}
.red{
background-color: red;
}
.text{
height: 50%;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.text > p:first-of-type{
font-size: 40pt;
font-weight: bolder;
}
.text > p:nth-of-type(2){
font-size: 26pt;
line-height: 1.3;
}
.grid{
display: grid;
grid-gap: 50px;
grid-template-columns: repeat(auto-fill , minmax(250px, 1fr));
font-size: 16pt;
line-height: 1.1;
text-align: justify;
margin: 0 10%;
grid-auto-rows: auto;
grid-auto-flow: dense;
}
.grid > div > img {
margin-bottom: 20px;
}
.absolute{
position: absolute;
transition: 0.5s all ease;
left: 0;
top: 0;
}
So there's nothing here needed for the effect to work except the positioning. We set the absolute divs to position absolute (inside a relative container) so we can position them on top of themselves and position the checkbox.
At this point, the document looks like this.
So we want to place the checkbox (and style it) on the screen and use the :checked state to switch between both views.
.check{
z-index: 100;
position: absolute;
top: 72%;
left: 5%;
transition: 0.6s all ease;
}
.check:after {
content: "Learn More";
display: block;
cursor: pointer;
user-select: none;
font-size: 1.6em;
color: #222;
background: #fff;
padding: 20px 0;
transition: 0.5s all ease;
width: 300px;
text-align: center;
position: absolute;
}
.check:checked:after {
background: #1A69DB;
color: #fff;
content: "Go Back";
width: 269.7px;
}
So what's happening here. First of all, we place the checkbox above the other two divs by manipulating its z-index and we use the position attribute it to place it properly.
After that we use the :after state to style the checkbox to our preferred style. Throughout this series I've mostly used the :after state with the content set to empty but this time we have a legitimate reason to fill the content; switching between "Learn More" in the first view and "Go Back" in the second view.
The CSS
Now we get to where the magic is happening. This is the styling for the example in the video above. Drum roll please...
/*This targets the text-content and moves it vertically off the screen */
.check:checked ~ .absolute:first-of-type > .row .col-60 {
transform: translateY(-100vh)
}
/*This targets the image and moves it horizontally off the screen */
.check:checked ~ .absolute:first-of-type > .row .col-40 {
transform: translateX(-100vw)
}
/*This hides the second div. We could have set the first div to have a
higher Z-index but because we can animate the opacity*/
.absolute:nth-of-type(2) {
opacity: 0;
}
/*This moves the checkbox to position it properly in the second slide*/
.check:checked{
left: 10%;
top: 80%;
}
/*This brings the second div into focus*/
.check:checked ~ .absolute:nth-of-type(2) {
opacity: 1;
}
And that's pretty much it. You can combine this with animations to create a more complicated transition as in the example below;
You could also do something a little more traditional with one div sliding over the other.
Viola!
Hopefully, this article was educative or useful to you. As usual, let me know if; there's something I missed, you have a neater way of doing this or you have something you'd like me to cover in another article.