This Gatsby post was written for Gatsby 3 and is no longer maintained. If you would like to try out SvelteKit or Astro, check out the maintained posts which are based on those. Astro lets you continue to use React but also with partial hydration which can be used to provide an enhanced user experience. SvelteKit offers server side rendering as well as static site generation. See the post on how performance improved switching from Gatsby to Astro for more background.
Let's build a CSS only Carousel in Gatsby. We are going to add a slider which stops at discrete positions (snap feature). As well as that, we will give it a little flip animation. We will do all of that using CSS. On top we take the new Gatsby Image Plugin out for a spin.
Why build a CSS only Carousel in Gatsby? I thought this might be something interesting for anyone new to Gatsby and React, but with some web development experience with other technologies. Although there is nothing to fear in React, this guide will help you ease into React. However even if you are already a Gatsby pro and can code this all up using advanced React features, you might appreciate the opportunity to step back and simplify your Gatsby logic, letting CSS do some of the heavy lifting and hopefully make the page more efficient.
The carousel is such a useful pattern to add to your toolkit. It's probably best known for its use on online stores, letting you flip through a catalogue of products. It is not only useful for e-commerce sites though. Even if a package like flickity is your current first choice for an image gallery slider. A CSS only carousel is an alternative which doesn't need extra dependencies. Basically the pattern can be used anywhere where you have a number of items you want to present using a limit screen footprint.
We will use a carousel to showcase recent projects on the home page of an MDX photography blog. The smooth-sliding
carousel serves up a hero image for each project. On hover (or tap for mobile), a nice flip animation
reveals some project details. We do all of that in CSS, using gatsby-image-plugin for the responsive
images. Here's a short video of our CSS only carousel in action:
- Gatsby v3 : to make the site blazing fast,
- gatsby-plugin-image : optimal image sizing, plus automatically generated AVIF and WebP images,
- gatsby-starter-climate : this gives us a minimal Gatsby v3 MDX blog site to add the carousel to. It also saves us having to write a bit of boilerplate code.
To speed things up we'll use a Gatsby 3 MDX Blog starter . To begin, clone the repo from GitHub and fire up Gatsby from the terminal:
Have a quick look through the folders just to get used to the setup. Once you have a feel for it,
we'll carry on. OK let's do this! We'll warm up with some Gatsby before getting into the CSS.
Create a new
Projects component in
This is a basic shell for the component. We need to define a few bits and pieces before we wire it
up. First, though, let's have a quick look at the GraphQL queries. In lines
110 we define a fragment which we can use
on any page we place the component on. We'll see that later. You'll note it includes another fragment
ProjectCarouselFragment — no fewer than three times!
This illustrates one practical use of fragments. You can see that fragment's code above. It's body
is eight lines long. Defining the fragment once and then referencing it when we need it saves us writing
those eight lines over and over. On top if we need to change anything in the query we just change it
once, so the code is more robust using fragments. You can learn more about Gatsby GraphQL Fragments in the Gatsby docs
ProjectCarouselFragment uses the new Gatsby Image Plugin API. This
creates an image object from the file. In the component body (lines 97–103) we will render the
image. Note the inclusion of
height attributes. Generally this is best practice as it gives the user's browser an earlier indication
of the image aspect ratio. Including these can help reduce cumulative layout shift
as the page loads. You will need to download three images (camera-restoration-project.jpg
) and save them to a new folder:
I downsized the images and optimised them locally before uploading so they are ready for you to use. Even though gatsby-plugin-image can optimise images, it is best practice to do some of the work yourself. This reduces the size of your repo and also speeds up site build. The images are 1344 pixels wide, this is double the widest display width needed. I use double because of the way Retina works. Some stock images are huge so be sure to downsize before using them.
There are some defaults for gatsby-pluin-image already set in
113). This saves us having to repeat them in each GraphQL query.
To be able to access the images in the GraphQL query, we need to update our
Next, lets start an SCSS file for the Project component:
Next, lets start an SCSS file for the Project component:
Then finally we add some missing icons to
src/components/Icons.jsx. Add these to the existing icons:
We should now be able to get the component rendered to the home page, once we wire it up. Edit
There's nothing fancy here, I think it's self explanatory, but let me know if it is not. Just note
we have used the
ProjectsFragment defined earlier. It was not at all
necessary to use a fragment. I just wanted to show that fragments can be used outside of the files
they are defined in. As an aside, with Gatsby v3 you can now use fragments in GraphiQL.
If you have Gatsby running in develop mode, you should now have the component rendered on the home page. The images aren't in a carousel yet, but that will come next.
This is supposed to be a CSS only carousel in Gatsby so let's break out the CSS. Just before that, we will update the Projects component. We'll put all the CSS classes in now so we can see the page blossom as we crank out the CSS.
I have added a bit of new code here so let's run through some of the logic. I have based the
implementation off a Chris Coyier post written to show how far HTML and CSS can take you
. You might have noticed before that each of the images was given an
id tag on their parent element. Interestingly, even if the images are in a carousel, we can jump to
any image and have it show, front and centre in the carousel using default browser behaviour. See the
post on using Scroll to Anchor for some background on how id's and hashes work together.
The logic in lines
100 gets the current hash from the browser window loacation. We need this because we are not using React
to track the carousel's state. We have a check that the code is running in a browser (and not on the
server as it does during build). Without this check we will run the site fine in develop mode, but
get errors when we try to build. As this check is something that might be needed in other components,
we will define it in a utilities file later.
There are a few accessibility attributes to improve user experience. The
tabIndex in line 109 allows users to focus the carousel by tabbing to it. Once focussed, users can scroll
through carousel slides using the keyboard. I learned about
aria-current (line 135) from the Universty of Twitter via a Wes Bos question answered by Scott Vinkle. It is
used here on the dots below the carousel. There is a visual indication of which slide is selected (red,
instead of blue dot),
aria-current is used to communicate that message to assistive technology
Just before we go on to flesh out the CSS, we should define our
isBrowser utility function, which we mentioned previously. Create a new
utilities folder in
/src and add a
utilities.js file to it:
Finally it's time to put the CSS into our CSS only carousel in Gatsby. We will do this in three phases:
- carousel slider with animation,
- flip animation,
- add responsive support.
If you are short on time, just paste the three main chunks of highlighted code below, one after the other into you editor (ignore the short alternative implementation block).
Add the code below to
src/components/Projects.module.scss to add
the carousel animations.
Now you should have something which looks more like a carousel. If you click a blue dot below the image you should trigger a slide transition. Look in you browser address bar and you will see a hash has been tacked on the end of the address.
A few things to note in the CSS. We set smooth scrolling in line 3. This improves the UX. Motion
is slow so it's probably not necessary to consider coding up a
prefers-reduced-motion alternative. The
scroll-padding-top setting in the next line helps
make sure the entire slide is visible when the user clicks one of the dots. There is an alternative,
which was not working in this case in Chrome or Safari, but did work in Firefox:
You might try it elsewhere, depending on your use case. It has the advantage of not having to apply the style to the entire html section of the DOM.
scroll-snap-type: x mandatory in line 43 ensures the slider stops
at discrete positions which line up with the slides. This is the behaviour in Chrome and Firefox.
I would say the best way to understand what some of the other lines of CSS do is to play around with
them. There is a lot of customisation possible. When applying this code to your own site be careful
with the contrast levels of the overlay text against the background. To maintain accessibility, you
might want to add linear gradient over the image. I am playing around with some Rust code to check overlay text / image contrast
but this is still in its infancy.
If you're ready, let's move on to look at the flip animation.
Next we add a flip animation to the carousel. Each project has a little summary detail, already
included in the React component (but not yet visibly rendered). When we hover over a slide, it
will flip, revealing the project details. Add the code below to the end of
src/components/Projects.module.scss to add the carousel animations.
Let me know if there is anything in there I should add an explanation for. This flip animation
is not rendering correctly in Safari at the moment, so I have included some CSS to disable the
flip on hover (lines
144), just for Safari. The block at the
end is kind of related to the carousel styling, it hides the dots where scroll-snap-type is not
Last bit is probably the most important. We add a little CSS to make sure the code is responsive
and mobile users have fantastic UX too. Update the code in
src/components/Projects.module.scss to match this:
That's it! What do you think of the CSS only carousel in Gatsby? I think this is the first component I have coded where there are more lines of CSS code than JSX 😀. It started out as a much shorter post but I wanted to flesh it out to a fully featured, real-world example so it would have more practical use. Thanks for following all the way through to the end 🙏🏽.
Share your own CSS only carousel in Gatsby implementation on Twitter. Use the
#askRodney hashtag so you can find other coders' work too.
How did you find this guide? What extensions do you want to implement on your CSS only carousel in Gatsby? Let me know if I should do a follow up post on using React to mirror the carousel state. If you do this, you can synchronise elements outside of the slider. As an example, scroll down to the bottom of this page to the “Similar Posts” block. You will see that if you scroll, the orange dot indicator tracks the current slide (this only works in reverse i.e. click dot and slide changes in the CSS only version). On top the post title shortcuts are synchronised. I will write a post on how to build that extra level of interaction if you are interested.
Have you found this post useful? I do hope it was easy enough to follow. Please let me know in any case. I would love to know your thoughts on the starter. Did it save you a bit of time by not having, firstly, to code a whole website from scratch to try out this code? Was it easy to download and start with Gatsby v3? Also get in touch if you have ideas for posts showing other new Gatsby v3 features. If you have found this post useful and can afford even a small contribution, please consider supporting me through Buy me a Coffee.
Finally, feel free to share the post on your social media accounts for all your followers who will find it useful. You can get in touch via @askRodney on Twitter and also askRodney on Telegram . Also, see further ways to get in touch with Rodney Lab. We post regularly on online privacy and security as well as Gatsby v3. Also subscribe to the newsletter to keep up-to-date with our latest projects.