The most modern Carousel component using CSS Scroll Snap and Vue.js
Last week I went to Codemotion Madrid with my beloved friend Laura Bonmati. It's one of the biggest conferences about coding in Spain.
One of our favourite talks was "The latest features in CSS" (in Spanish) by Sonia Ruiz, and this tip comes from some ideas I took from her talk, so let's give her a shoutout on twitter for the inspiration!
Among the features she showed, I was amazed by scroll-snap
and how easy is to create a carousel by using it, so I thought... let's make it a component!
First, create a Carousel.vue
component with the following structure:
<template>
<div class="carousel-wrapper">
<div class="carousel" :style="{ width, height }">
<slot/>
</div>
</div>
</template>
<script>
export default {
props: ["width", "height"]
};
</script>
<style scoped>
.carousel {
display: flex;
overflow: scroll;
scroll-snap-type: x mandatory;
}
.carousel > * {
flex: 1 0 100%;
scroll-snap-align: start;
}
</style>
The important parts here are the use of scroll-snap-type
. Using it we're forcing the scroll to snap to every item inside the .carousel
element, in the x axis.
Then you need to make every item in the carousel to take the full space of the container. You do that using flex: 1 0 100%
and defining the items alignment with scroll-snap-align: start
.
Notice that I'm also passing by a width
and height
properties so you can easily set the carousel dimensions from outside the component.
By the way, if you're not sure about how <slot/>
work, you can check the articles Using scoped slots in Vue.js and New v-slot directive in Vue.js 2.6.0 and know more about them (including scoped slots).
Just like that, you have a carousel component that works smoothly using the mouse scroll!
But hey... let's add some functionality right? What if you want to change the carousel slides manually?
As you might have thought, the carousel "sliding" works according to the scroll, so you must play with the scroll position.
In fact, each slide scroll position is exactly the width of the carousel multiplied per the slide position in the carousel.
Let's add a couple of buttons that allow us to go back and forth in the carousel, so we can navigate through every item:
<template>
<div class="carousel-wrapper">
<div class="carousel" :style="{ width, height }">
<slot/>
</div>
<button @click="changeSlide(-1)">Prev Slide</button>
<button @click="changeSlide(1)">Next Slide</button>
</div>
</template>
<script>
export default {
props: ["width", "height"],
methods: {
changeSlide(delta) {
const carousel = this.$el.querySelector(".carousel");
const width = carousel.offsetWidth;
carousel.scrollTo(carousel.scrollLeft + width * delta, 0);
}
}
};
</script>
These buttons call the changeSlide
method passing by the number of positions you want to navigate back or forth.
The method basically takes the carousel current width, and calculates where should scroll by checking the current scroll position and adding the amount of pixels where it should navigate. That calculation is defined by carousel.scrollLeft + width * delta
.
If you try the component now, you'll see that it works as expected, but the scroll is not smooth. Instead, it changes the slide immediately.
Luckily, modern CSS comes to the rescue again!
You can use the property scroll-behavior: smooth
on the carousel element and the scrolling will be smooth again!
.carousel {
display: flex;
overflow: scroll;
scroll-behavior: smooth;
scroll-snap-type: x mandatory;
}
Finally, the way to use the component is as simple as this:
<template>
<Carousel width="400px" height="250px">
<div class="blue">Blue</div>
<div class="green">Green</div>
<div class="red">Red</div>
</Carousel>
</template>
Do you realize how easy was to build this component? Modern CSS is rad!
I'm pretty sure you're now thinking on more ways to improve it: change slides automatically every X seconds, loop scrolling, go to a specific slide... please, tell me what you come up with and show it to me! I'll be more than happy to see what you accomplish!
Of course, keep in mind that this component uses very new CSS properties, so it's only supported in the most modern browsers.
But hey, you must be building the Carousel of the future!
Do you want to see this carousel working? Go to this CodeSandbox!
Related Articles
Creating UI components based on a Design System in Vue.js
Don't you know the benefits of using a Design System? Not sure how to structure your Vue.js components in an app? Read the article and find it out.
Jul 21, 2020
How to structure a Vue.js app using Atomic Design and TailwindCSS
Unsure about how to organize your Vue.js components? Learn in this tutorial how to do it using Atomic Design methodology. Examples and demos included!
Jun 16, 2020
The most modern Pie Chart component using CSS Conic Gradient and Vue.js
Build a Pie Chart component using one of the modern CSS features Conic Gradient
Oct 21, 2019
Sponsors
VueDose is proudly supported by its sponsors. If you enjoy it, consider supporting it to ensure the project maintainability.