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.

The Coffee Slide.

In press rich qui froth saucer java seasonal espresso variety arabica saucer filter bar. Turkish kopi turkish percolator galão panna wings luwak robust mountain brewed trade. At panna press and sugar caffeine in at aroma cultivar variety sugar latte dark. Origin doppio café single pumpkin eu panna rich seasonal caffeine trade. Cappuccino pumpkin black pot variety cappuccino dripper mountain barista at ristretto.

Bacon. Because of course.

Cow ribs chop meatloaf swine kevin sausage tri shankle ribs sirloin porchetta sausage meatloaf. Pork rump bresaola brisket pastrami tail shoulder leberkas steak. Tail kevin cow hock ball buffalo jowl frankfurter burgdoggen. Tongue kevin meatball biltong salami alcatra meatball kielbasa. Shankle filet pig mignon meatball shank meatloaf chicken tail hamburger prosciutto picanha.

Don't forget about cupcakes!

Chupa wafer danish icing carrot bears chupa bar. Oat lollipop pie danish snaps gummi chupa claw wafer fruitcake. Tart brownie ice cupcake gummi beans topping gummies sweet. Liquorice pie croissant toffee halvah donut marshmallow halvah gingerbread marzipan gummi. Beans sugar canes icing fruitcake dessert chups wafer.

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

The Coffee Slide.

In press rich qui froth saucer java seasonal espresso variety arabica saucer filter bar. Turkish kopi turkish percolator galão panna wings luwak robust mountain brewed trade. At panna press and sugar caffeine in at aroma cultivar variety sugar latte dark. Origin doppio café single pumpkin eu panna rich seasonal caffeine trade. Cappuccino pumpkin black pot variety cappuccino dripper mountain barista at ristretto.

Bacon. Because of course.

Chicken mignon round meatball boudin t chop swine sausage bone buffalo. Tongue tail short bresaola pancetta leberkas boudin cow short picanha chop kielbasa burgdoggen sirloin. Alcatra burgdoggen fatback burgdoggen pig andouille mignon shoulder ribeye burgdoggen turkey chop drumstick boudin. Buffalo mignon leberkas round chicken spare drumstick corned fatback ball andouille brisket turkey pig. Burgdoggen belly boudin steak capicola burgdoggen turkey biltong beef sirloin boudin turducken spare ground.

Frankfurter ribeye ball t burgdoggen andouille ground prosciutto tail. Buffalo chop alcatra burgdoggen sirloin jowl prosciutto sirloin kevin. Filet hamburger ham beef turkey doner pancetta chuck bresaola. Drumstick landjaeger doner pig pastrami ribeye andouille sausage rump. Pastrami shankle kevin fatback ribeye alcatra tail corned short venison round chicken buffalo pastrami.

Andouille loin jerky tenderloin andouille porchetta hamburger filet shank. Jowl hamburger boudin mignon sirloin hock pancetta leberkas salami. Corned andouille turducken pastrami ham beef hamburger brisket hamburger filet chop. Burgdoggen pig capicola belly fatback jerky turkey pastrami. Biltong cow ground shoulder pastrami loin sausage cupim t.

Pig ground tail porchetta tail chuck sirloin turducken tri. Shoulder kevin turkey belly corned bacon ribs chop burgdoggen doner mignon tenderloin boudin steak t. Hock t pastrami pig beef steak ribs steak shank hock burgdoggen sirloin biltong tri pig. Corned ball ham bone pig sirloin strip sausage. Jowl biltong belly mignon porchetta ground ribs boudin shankle swine chicken ham prosciutto leberkas.

Don't forget about cupcakes!

Chupa wafer danish icing carrot bears chupa bar. Oat lollipop pie danish snaps gummi chupa claw wafer fruitcake. Tart brownie ice cupcake gummi beans topping gummies sweet. Liquorice pie croissant toffee halvah donut marshmallow halvah gingerbread marzipan gummi. Beans sugar canes icing fruitcake dessert chups wafer.



/* 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%;
    }

}