Carousel
A Carousel is an interactive component that allows users to scroll through a collection of items that would otherwise not fit within the available space. Additional items are able to be accessed by scrolling, swiping or using the inbuilt controls provided with the component. You should consider all other options carefully before using a Carousel, as they can have significant accessibility and usability implications. Displaying all items in a grid or list is often a better option.
Our Carousel component is built on top of Embla Carousel, a lightweight carousel library with great accessibility support and a small footprint. All embla options and plugins are supported via the options and plugins props on our Carousel component.
Installation
npm install @nib-components/carousel
Note: You will also need to install the following peerDependencies:
- @nib-components/button
- @nib-components/copy
- @nib-components/theme
- @nib/icons
- @nib/layout
- embla-carousel
- embla-carousel-react
Usage
import {Carousel, CarouselControls, CarouselContent, CarouselItem} from '@nib-components/carousel';
Interactive demo
<Carousel gap={3}> <CarouselControls /> <CarouselContent itemWidth={{xs: '90%', md: 'min(23rem, 100%)'}} hinting="none"> <CarouselItem> <Card> <CardContent title="Card 1"> <Copy>Enabling teams to create high-quality products and experiences faster for our members.</Copy> </CardContent> </Card> </CarouselItem> <CarouselItem> <Card> <CardContent title="Card 2"> <Copy>Enabling teams to create high-quality products and experiences faster for our members.</Copy> </CardContent> </Card> </CarouselItem> <CarouselItem> <Card> <CardContent title="Card 3"> <Copy>Enabling teams to create high-quality products and experiences faster for our members.</Copy> </CardContent> </Card> </CarouselItem> </CarouselContent> </Carousel>
Props
Carousel
| Prop | Type | Default | Description |
|---|---|---|---|
children (required) | node | Must include a <CarouselContent> with <CarouselItem> components. Can also include <CarouselControls>, and any other elements. | |
options | EmblaOptionsType | Options for the Embla carousel. We use embla's defaults here except for {align: 'start', inViewThreshold: 0.5, skipSnaps: true} | |
plugins | EmblaPluginType[] | Plugins to be used with the Embla carousel. | |
gap | ResponsiveSpaceProp | The gap between carousel items. A size from our spacing scale. Can be made responsive by passing an object of breakpoints. Value(s) must be one of 1...10 values. | |
verticalSpace | ResponsiveSpaceProp | {xs: 4, xl: 6} | The vertical space between the children of the Carousel. A size from our spacing scale. Can be made responsive by passing an object of breakpoints. Value(s) must be one of 1...10 values. |
setApi | (api: EmblaCarouselType) => void | Callback to receive the Embla carousel API. | |
slideName | {singular: string; plural: string;} | {singular: 'item', plural: 'items'} | The name of the slide to use in the label and accessibility attributes. |
CarouselControls
| Prop | Type | Default | Description |
|---|---|---|---|
withSlidesInViewLabel | boolean | Whether to show the slides in view label alongside the controls. |
CarouselContent
| Prop | Type | Default | Description |
|---|---|---|---|
itemWidth (required) | CSSProperties['width'] | Partial<Record<Breakpoint, CSSProperties['width']>> | The width of each CarouselItem. All items must be the same size. | |
children (required) | node | The content of the carousel. Must include one or more <CarouselItem> components. | |
overflow | CSSProperties['overflow'] | 'clip' | The overflow behavior of the carousel content. |
hinting | 'none' | 'start' | 'end' | 'both' | 'none' | Whether to show the hint of more carousel items. |
hintingPosition | 'inside' | 'outside' | 'inside' | Whether the visual affordance gradients are positioned inside or outside the carousel viewport. |
floatingControls | boolean | Partial<Record<Breakpoint, boolean>> | false | Whether to show floating prev/next navigation arrows. Can be made responsive by passing an object of breakpoints. |
floatingControlsTopOffset | string | Partial<Record<Breakpoint, string>> | '3rem' | The top position of the desktop floating prev/next navigation arrows. Can be made responsive by passing an object of breakpoints. |
CarouselItem
CarouselItem has no custom props, but will accept any valid HTML attributes for a div.
Configuration and considerations
The Carousel component is a powerful and flexible component that comes with a number of configuration options and considerations to ensure the best possible user experience.
Carousel item width
A width should be set on the CarouselContent for all CarouselItems to use. CarouselItems all must have the same width. This can be any valid CSS length, but commonly will be either a percentage (e.g. 50%), a fixed value (e.g. 15rem) or a responsive object (e.g. { xs: '100%', md: '50%' }).
Percentage-based width
Good for fitting a deliberate number of items within the carousel viewport, for a particular screen size. For example if you wish to have one card in view on small screens, two cards in view on medium screens and three cards in view on large screens, you would set the width to { xs: '100%', lg: '50%', xxl: '33.33%' }.
Fixed width
Useful for when the items within your carousel simply have a width that they need to be. Items will be shown according to how many can fit within the carousel viewport, and the rest will be accessible by scrolling. For example, if your carousel items are 20rem wide, and the carousel viewport is 60rem wide, three items will be fully in view at a time.
Make a claim
View my cover
View extras usage
Show digital card
Find a provider
Book telehealth
Use your points
View extras usage
Show digital card
Find a provider
Book telehealth
Use your points
View all
Hinting at more content
Hinting to the user that there are more items available to scroll to is important for usability. The Carousel has a number of props to help with this.
By default, the Carousel component will fill the space with the carousel viewport. Optionally, via the hinting prop on CarouselContent, you can enable hinting and show a gradient fade-out on either side of the carousel content to indicate that there are more items available to scroll to.
If your carousel is aligned with other content on the page, this indentation to accommodate the visual affordance may be unsuitable. To help in this situation, hinting can be shifted outside the carousel viewport by setting the hintingPosition prop to outside.
Hinting is most effective when the carousel is the primary element on the page, and the items are using percentage-based widths.
With hinting
Hinting can be enabled on one or both sides of the carousel viewport via the hinting prop on CarouselContent. The hinting can be positioned either inside or outside the carousel viewport via the hintingPosition prop.
<Carousel gap={3}> <CarouselControls /> <CarouselContent itemWidth={{xs: '100%', md: '50%'}} hinting="both" hintingPosition="inside"> {Array.from({length: 4}, (_, i) => ( <CarouselItem key={i}> <Card> <CardContent title={`Card ${i + 1}`}> <Copy>Enabling teams to create high-quality products and experiences faster for our members.</Copy> </CardContent> </Card> </CarouselItem> ))} </CarouselContent> </Carousel>
Without hinting
If your carousel is aligned with other content on the page, you may wish to disable the hinting affordance by setting the hinting prop on CarouselContent to none.
<Carousel gap={3}> <CarouselControls /> <CarouselContent itemWidth={{xs: '100%', md: '50%'}} hinting="none"> {Array.from({length: 4}, (_, i) => ( <CarouselItem key={i}> <Card> <CardContent title={`Card ${i + 1}`}> <Copy>Enabling teams to create high-quality products and experiences faster for our members.</Copy> </CardContent> </Card> </CarouselItem> ))} </CarouselContent> </Carousel>
You can still hint to the user that there are more items by setting the width of the items to sum to slightly less than the viewport size. For example, if you want two items in view at a time, you could set the width to 45% instead of 50%.
<Carousel gap={3}> <CarouselControls /> <CarouselContent itemWidth={{xs: '90%', md: '45%'}} hinting="none"> {Array.from({length: 4}, (_, i) => ( <CarouselItem key={i}> <Card> <CardContent title={`Card ${i + 1}`}> <Copy>Enabling teams to create high-quality products and experiences faster for our members.</Copy> </CardContent> </Card> </CarouselItem> ))} </CarouselContent> </Carousel>
Previous/next controls
The Carousel component provides a set of controls for navigating forward and backwards through the items. These are implemented in the CarouselControls component, which is placed top-right above the carousel viewport.
Optionally, via the withSlidesInViewLabel prop, you can enable a label that shows the number of slides currently in view.
<Carousel gap={3} slideName={{singular: 'card', plural: 'cards'}}> <CarouselControls withSlidesInViewLabel /> <CarouselContent itemWidth="50%"> {Array.from({length: 4}, (_, i) => ( <CarouselItem key={i}> <Card> <CardContent title={`Card ${i + 1}`}> <Copy>Enabling teams to create high-quality products and experiences faster for our members.</Copy> </CardContent> </Card> </CarouselItem> ))} </CarouselContent> </Carousel>
Floating controls
For extra prominent carousels, like within a quote funnel, we also have a floatingControls prop for larger floating previous/next controls that are positioned over the carousel content. These controls are sticky and will scroll with the content. This is useful when the carousel items are taller than the viewport as it keeps the controls visible. By default these controls are positioned vertically centred, but you can adjust this via the floatingControlsBottomPosition prop.
<Carousel gap={3} options={{startIndex: 2}}> <CarouselContent itemWidth={{xs: '100%', md: '50%', lg: '33.33%'}} hinting="both" floatingControls floatingControlsTopOffset="5rem"> {Array.from({length: 6}, (_, i) => ( <CarouselItem key={i}> <Box mode={i % 2 ? 'warm' : 'sage'} padding={6} borderRadius="standard" background="default" foreground="default" height="80vh"> Item {i + 1} </Box> </CarouselItem> ))} </CarouselContent> </Carousel>
The floatingControls and floatingControlsTopOffset props can also be made responsive by passing an object of breakpoints to tailor their positioning for different screen sizes.
Responsively activating/deactivating the Carousel
Sometimes you may wish to deactivate the Carousel functionality on larger screens, where all items can fit within the viewport. You can do this via the options prop on the Carousel utilising the breakpoints option from Embla.
<Carousel options={{ breakpoints: { xl: { active: false } } }} gap={2} > <CarouselControls /> <CarouselContent itemWidth={{xs: '100%', md: '50%', xl: '25%'}}> {Array.from({length: 4}, (_, i) => ( <CarouselItem key={i}> <Card> <CardContent title={`Card ${i + 1}`}> <Copy>Enabling teams to create high-quality products and experiences faster for our members.</Copy> </CardContent> </Card> </CarouselItem> ))} </CarouselContent> </Carousel>
We have extended embla's options.breakpoints to support using our named breakpoints as well as any valid media query:
<Carouseloptions={{breakpoints: {xxxl: {active: false},'(min-width: 1234px)': {loop: true}}}}>
When the carousel is not active, controls are automatically hidden and the carousel is no longer scrollable. You should take care to ensure that the itemWidths are set such that all items fit within the viewport when the carousel is deactivated.