CSS-only Slideshow
A standard slideshow with CSS Grid and no JavaScript
This technique expands on the tabbed interface technique. Again, using css grid to place the items, and radio buttons for the triggers.
This is the end result. Again, there's zero JavaScript controlling this. It's all 100% pure CSS and HTML.
Here's the basic code. If you have emmet.io installed, you can expand this code snippet to create a basic html framework to follow along
.slides>(input:r.slide-control#slide_$[name="slides-1" role="presentation"]+label[for="slide_$"].slide-label>{Slide $}^div.slide>lorem100)*3
<div class="slides">
<input type="radio" name="slides-1" id="slide_1" class="slide-control" role="presentation" checked>
<label for="slide_1" class="slide-label">Slide 1</label>
<div class="slide"> -- Slide 1 content -- </div>
<input type="radio" name="slides-1" id="slide_2" class="slide-control" role="presentation">
<label for="slide_2" class="slide-label">Slide 2</label>
<div class="slide"> -- Slide 2 content -- </div>
<input type="radio" name="slides-1" id="slide_3" class="slide-control" role="presentation">
<label for="slide_3" class="slide-label">Slide 3</label>
<div class="slide"> -- Slide 3 content -- </div>
</div>
.slides {
display: grid;
grid-template-rows: 1fr 2em;
grid-template-columns: repeat(3, 1fr) 3fr;
grid-row-gap: 1em;
overflow: hidden;
border: 1px solid #ccc;
}
.slide-label {
grid-row: 2 / 3;
}
.slide {
grid-row: 1 / 2;
grid-column: 1 / 5;
position: relative;
left: -100%;
transition: left .5s;
padding: 1.2rem;
}
.slide-control:checked + .slide-label {
color: #C95B36;
}
.slide-control:checked + .slide-label + .slide {
left: 0;
}
.slide-control:checked ~ .slide {
left: 100%;
}
This works because clicking on a label that's associated with a radio button (or a checkbox) will check or uncheck that element. CSS will let you select an item based on it's :checked state.
Lines 1 through 21 are just for setting up the CSS grid. We are setting 2 rows, with the bottom row being 2em high, and the top row being as large as needed for the content. I have 3 slides, each with basically the same amount of content, so this will work just great.
The real magic happens because of how CSS handles ~, known as the general sibling combinator. When used, it will only select items in the DOM that come after the selected item. Whenever a label is clicked, it sets the checked state of the radio button, which moves the corresponding slide in to view, and by default every slide before the checked slide in the DOM moves off screen to the left, and every slide after the checked one moves off the screen to the right.
The above code does have a couple issues, however. Unless you use a solid background on each slide, when skipping over slides all the text will show on top of each other for a moment. One simple way around that is to use a small opacity fade in/out on the active slides.
Another problem is that the slide labels are left-aligned. So to make them centered, add a column before and after, have the first 'cell' stretch over two widths, and right-align the text. This will give the impression that they are all centered.
One of the benefits of using CSS Grid is that each slide will be the height of the tallest content in the slides. One of the drawbacks of CSS Grid is that all the slides will be the same height as the tallest slide's content.
To compensate for this, add a max-height and an overflow-y: auto to add scrollbars as needed.
And what about browsers that don't support CSS Grid? Putting everything behind an @supports (display:grid) query takes care of that. To any users on IE, the slides will display as regular blocks of content on the page, which the label acting as the header of the section. And to people with CSS grid, all the magic happens.
Heres the final version, with centered labels and fades and scrollbars, and the final CSS code
/* for browsers that don't support css-grid */
.slide-control { display: none }
.slide-label { font-weight: bold}
.slide { margin-bottom: 2em}
@supports (display:grid) {
/* reverse the previous */
.slide-label { font-weight: inherit}
.slide { margin-bottom: 0}
.slides {
display: grid;
grid-template-rows: 1fr 2em;
grid-template-columns: repeat(5, 1fr);
grid-row-gap: 1em;
overflow: hidden;
border: 1px solid #ccc;
}
.slide-label {
grid-row: 2 / 3;
text-align: center;
}
.slide-label:first-of-type {
grid-column: 1 / 3;
text-align: right;
}
.slide-label:last-of-type {
text-align: left;
}
.slide {
grid-row: 1 / 2;
grid-column: 1 / 6;
position: relative;
left: -100%;
opacity: 0;
max-height: 30rem;
overflow-y: auto;
transition: left .5s, opacity .7s;
padding: 1.2rem;
}
.slide-control:checked + .slide-label {
color: #C95B36;
}
.slide-control:checked + .slide-label + .slide {
opacity: 1;
left: 0;
}
.slide-control:checked ~ .slide {
left: 100%;
}
}