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. |
floatingArrows | boolean | Whether to show floating prev/next navigation arrows. | |
floatingArrowsBottomPosition | string | '50%' | The bottom position of the floating prev/next navigation arrows. |
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. |
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 arrows
For extra prominent carousels, like within a quote funnel, we also have a floatingArrows
prop for larger floating previous/next controls that are positioned over the carousel content. These arrows 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 arrows are positioned vertically centred, but you can adjust this via the floatingArrowsBottomPosition
prop.
<Carousel gap={3} options={{startIndex: 2}} floatingArrows floatingArrowsBottomPosition="40%"> <CarouselContent itemWidth={{xs: '100%', md: '50%', lg: '33.33%'}} hinting="both"> {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>