Pure CSS Gallery

Galleries of pictures are nothing new. News sites, photography portfolios, screenshots of applications - you've no doubt seen one recently.

I found myself with the task of implementing such a gallery recently, and inspired by CSSBox I decided to implement my own - purely in CSS without any Javascript at all as a challenge (also because the website upon which I wanted to put it doesn't yet have any Javascript on it, and I don't want to add any unless I have to). I started with a basic HTML structure:

<section class="gallerybox">

    <section class="gallerybox-gallery">

        <figure class="gallerybox-item" id="picture1">
                <img src="" alt="" decoding="async" />

            <figcaption>Caption A</figcaption>

            <a class="gallerybox-prev" href="#picture3">❰</a>
            <a class="gallerybox-next" href="#picture2">❱</a>
        <figure class="gallerybox-item" id="picture2">
                <img src="" alt="" decoding="async" />

            <figcaption>Caption B</figcaption>

            <a class="gallerybox-prev" href="#picture1">❰</a>
            <a class="gallerybox-next" href="#picture3">❱</a>
        <figure class="gallerybox-item" id="picture3">
                <img src="" alt="" decoding="async" />

            <figcaption>Caption C</figcaption>

            <a class="gallerybox-prev" href="#picture2">❰</a>
            <a class="gallerybox-next" href="#picture1">❱</a>


I've omitted the <html><head></head><body><main></main></body></html> wrapper bit to keep this short, and I'm using {placekitten} for nice placeholder images (my placeholder image service is cool, but doesn't generate images that work well in a gallery).

Each gallery item is a <figure> element and has it's own unique / previous buttons are implement with <a> elements linking between ids. By using the :target element, we can apply CSS rules to the element whose id matches that of the anchor element that we've linked to. For example, if at the end of the URL in the address bar there was #picture2, then the <figure> element with the id picture2 would be targeted by the :target pseudo-selector.

The key challenge in a gallery is to display only the image that's currently being viewed, without showing any of the other images. This is trivial with Javascript, but slightly more challenging with CSS only.

To solve this problem, at first I jumped almost immediately to :target-within and did something like this:

.gallerybox-gallery:target-within > .gallerybox-item:target {
    display: block;
.gallerybox-gallery:target-within > .gallerybox-item:not(:target) {
    display: none;

Unfortunately though, as of the time of typing it's not supported by any browser:

(Click to see the latest Can I Use support table - by the time you're reading this support may have been implemented)

Annoyingly, :focus-within is already supported, but not :target-within. Not to be deterred, I came up with another plan. By using a CSS Grid with only 1 element, we can make all the images occupy the same space on the page, and then use position: relative and z-index to control which one is displayed at the top of the stack to the viewer:

.gallerybox-gallery {
    display: grid;
    grid-template-columns: auto;
    grid-template-rows: auto;
    grid-template-areas: "main";

.gallerybox-item {
    grid-area: main;
    z-index: 2;

.gallerybox-item:target {
    z-index: 100;

It took me a bit of fiddling around to come up with, but it's a wonderful hack that solves the problem. In the future, I'll revise the code behind this to use :target-within, because it's probably much more performant.

With this, we have the basics of a gallery! With a of styling of the captions and next / previous arrows, I got this:

See the Pen gallerybox: Pure CSS Gallery by Starbeamrainbowlabs (@sbrl) on CodePen.

(Can't see the above? Try this direct link.)

The other thing of note here are is the way I vertically aligned the next / previous arrows:

.gallerybox-item {
    position: relative;
:where(.gallerybox-prev, .gallerybox-next) {
    display: flex;
    align-items: center;
    height: 100%;
    position: absolute; top: 0;

I use a flexbox here along with the align-items directive (which aligns things perpendicular to the main axis of the flexbox) to vertically align the text inside the <a> elements. To make the <a> elements the right height, I use the fact that position: abolute is relative to any ancestor elements that are also positioned with a height: 100% (100% of the containing element's height) to make them the right height.

This post was mostly just to quickly show off the some of the techniques I've used here and this gallery script. It does have the flaw that if used in a long page it causes the user's browser to jump and put the top of the gallery at the top of the page (which wouldn't be an issue if I used Javascript), but that's an acceptable issue given the constraints of the challenge.

If anyone found this interesting, I'd be happy to talk more in depth about CSS in a future post (though CSS Tricks does this very well already).

Tag Cloud

3d 3d printing account algorithms android announcement architecture archives arduino artificial intelligence artix assembly async audio automation backups bash batch blog bookmarklet booting bug hunting c sharp c++ challenge chrome os cluster code codepen coding conundrums coding conundrums evolved command line compilers compiling compression containerisation css dailyprogrammer data analysis debugging demystification distributed computing docker documentation downtime electronics email embedded systems encryption es6 features ethics event experiment external first impressions freeside future game github github gist gitlab graphics hardware hardware meetup holiday holidays html html5 html5 canvas infrastructure interfaces internet interoperability io.js jabber jam javascript js bin labs learning library linux lora low level lua maintenance manjaro minetest network networking nibriboard node.js operating systems own your code pepperminty wiki performance phd photos php pixelbot portable privacy problem solving programming problems project projects prolog protocol protocols pseudo 3d python reddit redis reference release releases rendering resource review rust searching secrets security series list server software sorting source code control statistics storage svg talks technical terminal textures thoughts three thing game three.js tool tutorial tutorials twitter ubuntu university update updates upgrade version control virtual reality virtualisation visual web website windows windows 10 xmpp xslt


Art by Mythdael