About Version 4
Splide v4
I never expected current exponential growth of my library when I created it, but now Splide has over 300 million hits/month on jsDelivr✨. Surprisingly, it's more than the number of Swiper that is the one of the most popular carousel libraries. I know it's not the only way to distribute a package, but I'd like to thank all Splide users, and much appreciate all sponsors!
Now new version has come, improving accessibility and usability. Here is the summary of v4 updates:
- Revise accessibility with following W3C Carousel Design Pattern
- Install a Live Region for screen reader users
- Reduce non-essential motions when detecting the
prefers-reduced-motion
media feature - Support a toggle (combined play-pause) button for autoplay
- Make breakpoints behave like CSS media query, ensuring the same carousel between window resize and page load
- Implement the Drag Free Snap mode
- Make the
direction
option responsive - Add options to control the wheel sensitivity for inertia scroll
- Patch
:focus-visible
for IE and Safari, and add focus styles to themes - Some bug fixes
A bad new: code size increased (27Kb→29Kb).
Breaking Changes
If these changes may affect your carousel, modify your code by following Migrating from v3 to v4.
- Change default behaviour of
keyboard
,waitForTransition
andslideFocus
- Drop support for separated play/pause buttons for autoplay, and support a toggle button (combined play-pause) instead
- Drop support for
splide__slider
element, removingarrows: 'slider'
andpagination: 'slider'
options - Change some methods in Components
Also, update following items for new features:
- Translate strings newly added to i18n
- Set
aria-label
oraria-labelledby
to the root element (if necessary)
If you are using React, Vue or Svelte Splide, follow each instruction:
New Features
Robust Accessibility
To make Splide accessibility robuster and more reliable, I've revised roles and attributes so that the carousel complies with W3C Carousel Design Pattern, and installed a Live Region that is very helpful for screen reader users.
With a live region, screen readers are able to announce contents of the newly visible slide every time when a carousel moves. You can see how NVDA which is a famous screen reader reads contents in this video:
Additionally, I've implemented "reduced motion" mode that automatically disables transition effects while prefers-reduced-motion
media feature is reduced
for those who experience distraction or nausea from animation (WCAG 2.3.3).
If you are interested in these accessibility improvements, see this long document.
Breakpoints
Old Splide updated current options by options associated with matched breakpoint,
hence you might get different result between window resize and page refresh.
For example, think you have these options with breakpoints
:
{
perPage
:
3
,
gap
:
'1rem'
,
breakpoints
:
{
1200
:
{
perPage
:
2
,
gap
:
'1rem'
}
,
640
:
{
gap
:
0
}
,
}
,
}
JavaScript
{ perPage : 3, gap : '1rem', breakpoints: { 1200: { perPage: 2, gap: '1rem' }, 640 : { gap: 0 }, }, }
When you open the page on a desktop (> 1200px), the carousel has 3 slides in a view.
And then, if you resize down the window to 640px or below,
the carousel will have 2 slides because the window passes through 1200: { perPage: 2 }
.
On the other hand, when you open the page on a mobile (≤ 640px), the carousel has 3 slides instead of 2.
To get the desired result, you had to add all options that you want to update into both base and breakpoints:
{
perPage
:
3
,
gap
:
'1rem'
,
breakpoints
:
{
1200
:
{
perPage
:
2
,
gap
:
'1rem'
}
,
640
:
{
perPage
:
2
,
gap
:
0
}
,
}
,
}
JavaScript
{ perPage : 3, gap : '1rem', breakpoints: { 1200: { perPage: 2, gap: '1rem' }, 640 : { perPage: 2, gap: 0 }, }, }
In v4, I've made breakpoints behave as like media queries because:
- Some people were confused by the difference with CSS
- It can ensure the same result between window resize and page refresh
- It is more understandable what options will be used on the specific point
By this change, Splide merges all options in breakpoints that matches the current width into base options. For example, when you open the page with following options on a mobile(≤ 640px):
{
arrows
:
true
,
perPage
:
3
,
breakpoints
:
{
1200
:
{
arrows
:
false
}
,
800
:
{
perPage
:
2
}
,
640
:
{
}
,
}
,
}
JavaScript
{ arrows : true, perPage: 3, breakpoints: { 1200: { arrows: false }, 800 : { perPage: 2 }, 640 : {}, }, }
...your carousel has 2 slides and no arrows since 1200
and 800
also match the device width.
Besides, values will be reset to default if base options do not contain their keys. For example, if you specify the width of slides on a specific breakpoint:
{
breakpoints
:
{
1200
:
{
fixedWidth
:
200
}
,
}
,
}
JavaScript
{ breakpoints: { 1200: { fixedWidth: 200 }, }, }
...the width will be reset to the default value (undefined
) when you widen the window over 1200px.
Although this change won't break your breakpoints since you defined them in the stricter way than the new one, I recommend checking your carousel just in case.
Migrating from v3 to v4
Options
Default values of some options have been changed. Update your options if necessary.
keyboard
The default value has been changed from true
to false
.
Also, old Splide assigned tabindex="0"
to a root element when the value is 'focused'
, but the new one won't
because adding the attribute to non-interactive elements is discouraged.
If necessary, you will have to insert it manually.
true | Enables shortcuts only when a carousel contains focus |
---|---|
false | Disables shortcuts |
'global' | Enables shortcuts globally, listening to the window |
'focused' | Same with |
See this section for more details.
waitForTransition
The default value has been changed from true
to false
.
If two synced carousels had different transition speed, ignoring interaction while transitioning might cause desynchronization issue.
slideFocus
The default value was true
, but now it is:
false
for normal carouselstrue
whenisNavitation
istrue
As a result of other accessibility improvements, slides in a normal carousel do not need to be focusable (probably, I'm not 100% sure),
whereas slides in a thumbnail carousel with isNavigation
still needs to, since clickable elements must be focusable.
arrows
The 'slider'
value is no longer available. Change the value to true
and follow this instruction.
pagination
The 'slider'
value is no longer available. Change the value to true
and follow this instruction.
i18n
New keys —carousel
, slide
, and slideLabel
— are added. See this page for more details.
Play/Pause Button
According to feedback, users prefer a combined play-pause button for autoplay rather than separated buttons. So, I've decided to quit supporting individual buttons and to include the single toggle button.
----++++<
div
class
=
"splide"
>
...
<
div
class
=
"splide__autoplay"
>
<
button
class
=
"splide__play"
>
Play
<
/
button
>
<
button
class
=
"splide__pause"
>
Pause
<
/
button
>
<
/
div
>
<
button
class
=
"splide__toggle"
type
=
"button"
>
<
span
class
=
"splide__toggle__play"
>
Play
<
/
span
>
<
span
class
=
"splide__toggle__pause"
>
Pause
<
/
span
>
<
/
button
>
<
/
div
>
HTML
<div class="splide"> ... - <div class="splide__autoplay"> - <button class="splide__play">Play</button> - <button class="splide__pause">Pause</button> - </div> + <button class="splide__toggle" type="button"> + <span class="splide__toggle__play">Play</span> + <span class="splide__toggle__pause">Pause</span> + </button> </div>
You can keep using separated buttons by the following snippet:
var
splide
=
new
Splide
(
'.splide'
,
{
autoplay
:
true
}
)
.
mount
(
)
;
var
Autoplay
=
splide
.
Components
.
Autoplay
;
var
play
=
splide
.
root
.
querySelector
(
'.splide__play'
)
;
var
pause
=
splide
.
root
.
querySelector
(
'.splide__pause'
)
;
if
(
play
)
{
play
.
addEventListener
(
'click'
,
function
(
)
{
Autoplay
.
play
(
)
;
}
)
;
}
if
(
pause
)
{
pause
.
addEventListener
(
'click'
,
function
(
)
{
Autoplay
.
pause
(
)
;
}
)
;
}
JavaScript
var splide = new Splide( '.splide', { autoplay: true } ).mount(); var Autoplay = splide.Components.Autoplay; var play = splide.root.querySelector( '.splide__play' ); var pause = splide.root.querySelector( '.splide__pause' ); if ( play ) { play.addEventListener( 'click', function () { Autoplay.play(); } ); } if ( pause ) { pause.addEventListener( 'click', function () { Autoplay.pause(); } ); }
Slider Element
The purpose of using <div class="splide__slider">
was to wrap a track with a relative element.
In the new version, it's no longer necessary since I've relaxed restriction of the HTML structure.
You can get the same result by following steps:
- Change
arrows
and/orpagination
options from'slider'
totrue
, or just remove them since it's the default value.-+-+{
arrows
:
'slider'
,
arrows
:
true
,
// or remove this
pagination
:
'slider'
,
pagination
:
true
,
// or remove this
}
JavaScript{ - arrows: 'slider', + arrows: true, // or remove this - pagination: 'slider', + pagination: true, // or remove this }
- Replace the slider element with
<div style="position: relative">
, or assign a class that hasposition: rerative
.<
div
class
=
"splide"
>
<
div
style
=
"position: relative"
>
<
div
class
=
"splide__track"
>
<
div
class
=
"splide__list"
>
...
<
/
div
>
<
/
div
>
<
/
div
>
<
/
div
>
HTML<div class="splide"> <div style="position: relative"> <div class="splide__track"> <div class="splide__list">...</div> </div> </div> </div>
Alternatively, you can keep the slider element by adding the CSS below:
.splide__slider
{
position
:
relative
;
}
CSS
.splide__slider { position: relative; }
Components
Move#isBusy()
Moved to Controller#isBusy()
.
Controller#scroll()
Parameters have been changed. See Controller for more details.
Scroll#scroll()
Parameters have been changed. See Scroll for more details.