primitives icon indicating copy to clipboard operation
primitives copied to clipboard

[New Primitive] `Carousel`

Open chaance opened this issue 4 years ago • 52 comments

See https://github.com/radix-ui/primitives/discussions/375

chaance avatar Jan 08 '21 22:01 chaance

Carousel Research

Real world examples

Google cast

image

  • Scrollable
  • Drag to scroll (mouse and touch)
  • Scrolls 100% carousel width per page
  • Prev/next page buttons

Netflix

image

  • Prev/next page buttons
  • Drag to scroll on touch devices only
  • Scrolls 100% carousel width per page
  • Indicator (top right - doesn't click to jump)

Instagram

image

  • Prev/next buttons
  • Autoplay
  • Progress bar for each slide
  • One slide per page

Squarespace

image

  • Prev/next page buttons (bottom right)
  • Hovering left or right of carousel changes cursor to prev/next arrow. Clicking when this happens behaves the same as prev/next buttons
  • Slides one slide at a time
  • Indicator (bottom right - numbers)
  • Loops

Usain Bolt

image

  • Prev/next page buttons
  • Autoplay
  • Thumbnails (also acts as navigation)
  • Slides one slide at a time
  • Loops

Mercedes Benz

image

  • Prev/next page buttons
  • Autoplay
  • Slides one slide at a time
  • Pauses timer when mousing over the carousel
  • Drag to slide (mouse and touch)
  • Loops

Toyota

image

  • Prev/next page buttons
  • Autoplay
  • Slides one slide at a time
  • Pauses timer when mousing over the carousel
  • Indicator
  • Swipe to slide (mouse and touch)
  • Progress bar that indicates when timer is about to run out
  • Loops

Lib examples

Bootstrap

https://react-bootstrap.github.io/components/carousel/

  • Prev/next buttons
  • Indicator (acts as indicator and navigation)
  • Autoplay
  • Interval per slide
  • Pause
  • Left/Right keys to slide
  • Caption per slide
  • Slides one slide at a time
  • Loops

React carousel

https://brainhubeu.github.io/react-carousel/

  • Prev/next buttons
  • Multiple slides per page
  • Drag to slide (mouse and touch)
  • Loops
  • Autoplay
  • Indicator (also navigates)
  • Thumbnails (also navigates)

Slick

https://kenwheeler.github.io/slick/

  • Prev/next buttons
  • Multiple slides per page
  • Drag to slide (mouse and touch)
  • Loops
  • Centre mode (active slide in centre when multiple slides per page)
  • Autoplay
  • Pause on hover when autoplaying
  • Indicator (also acts as nav)
  • Keyboard navigation but via tab stops (no arrow keys)

Flickity

https://flickity.metafizzy.co/

  • Prev/next buttons
  • Indicator (and nav)
  • Drag to slide (mouse and touch)
  • Keyboard navigation
  • Loops
  • Autoplay
  • Pause on hover when autoplaying
  • Single/Multiple slides per page

React Responsive Carousel

https://react-responsive-carousel.js.org/

  • Prev/next buttons
  • Indicator (acts as nav)
  • Thumbnails (acts as nav)
  • Drag to slide (mouse and touch)
  • Claims to have keyboard nav but it's buggy
  • Autoplay
  • Slides one slide at a time
  • Loops

Comprehensive features

Looking at the above, the spectrum of available features appears to be the following. I've made these checkboxes so we can agree which we would like to support.

  • [x] Prev/next buttons
  • [x] Indicator (non-interactive)
  • [x] Indicator (interactive jump to slide)
  • [ ] Thumbnails
  • [x] Drag to scroll (mouse)
  • [x] Drag to scroll (touch)
  • [ ] Drag to slide (mouse)
  • [ ] Drag to slide (touch)
  • [x] Ability to slide a whole page
  • [x] Ability to slide a specified number of slides
  • [x] Keyboard navigation with arrow keys
  • [x] Keyboard navigation via tab stop to next/prev buttons or indicator
  • [ ] Autoplay
  • [ ] Autoplay timer progress bar
  • [ ] Custom interval per slide when autoplaying
  • [ ] Pause autoplay on interaction
  • [x] Loops
  • [x] Scrollable

Please feel free to and add anything not covered here to the list! Once agreed on these, I'll propose an API 👍

Questions

  1. Most carousels appear to contain images. Are we happy to say that slides are an open box for content or would we expose something like SlideImage, SlideDescription to restrict the types of content? @chaance raises an interesting point below about draggable functionality if the carousel is an open box with draggable things inside it.

jjenzz avatar Jan 12 '21 20:01 jjenzz

Great start. Can't believe there's not a slick-carousel on the list here since that's been the one I historically have seen most indie devs reach for despite being unmaintained for years 😅

My biggest concern with drag-to-slide is how it works when composed with other draggable/slideable components composed within the carousel. Are nested carousels supported in any case? What about Slider or audio/video element controls inside of a Carousel? I'm sure we'll get into some funky edge cases here, just something I might consider early if it helps.

chaance avatar Jan 12 '21 20:01 chaance

In the Usain Bolt example, I imagine these slide previews might overflow at certain widths, and they too would become independently draggable. A carousel inside a carousel. 🙃

image

chaance avatar Jan 12 '21 20:01 chaance

Probably worth adding https://flickity.metafizzy.co/ to the list as well. That's arguably one of the best out there I think.

benoitgrelard avatar Jan 12 '21 20:01 benoitgrelard

Can't believe there's not a slick-carousel on the list here

Your wish is my command 😛

My biggest concern with drag-to-slide is how it works when composed with other draggable/slideable components composed within the carousel

Yeah I was also wondering what sort of media we think should be allowed in these. Are we happy to say they are an open box or would we expose something like SlideImage, SlideDescription to limit the content? 🤔

jjenzz avatar Jan 12 '21 20:01 jjenzz

Yeah I was also wondering what sort of media we think should be allowed in these. Are we happy to say they are an open box or would we expose something like SlideImage, SlideDescription to limit the content? 🤔

My thought is that it probably has to be a bit of an open box, but it's potentially problematic even with non-interactive content in ways I haven't considered fully. I'd probably want to still be able to select text with a mouse, for example. I wonder if it would be worth trying to detect the intent of a particular action -- is the user clicking or touching to pressing something, or are they intending to drag? I'd be interested to see how/if React Aria handles this when usePress and useMove are both triggered.

chaance avatar Jan 12 '21 20:01 chaance

Some notes after chatting through the discovery with @jjenzz.

The feature list posted above looks solid.

Two things that require further thinking:

  • We are thinking we could potentially omit the autoplay stuff and loop back around layer to add it on a second pass.
  • We could maybe have Carousel and a separate GalleryCarousel which re-uses Carousel under the hood but has the more image specific features.

Some notes to clarify particular features:

Prev/Next Buttons

  • These should be simple, just two buttons.
  • A somewhat common use case is prev/next buttons which are invisible, but each cover a large area of either side of the carousel (often 25%). The cursor is commonly changed to arrows on hover. This is all just CSS, no need for us to do anything other than provide next/prev buttons.
  • Another use case is two invisible hover areas, which are not clickable. On hover, they display the button nested inside. This doesn't need to be a component we provide part, the consumer can just add their own random div.

Thumbails Thumbnails would just be content inside the indicator button. The component would render a button by default, and the consumer just adds content inside.

Decouple events from scrolling You can set the amount of slides per pane, which are scrolled when you click a next/prev button. However, the touch/swipe/trackpad interaction should not necessarily be tied to this setting.

It works well for image gallery carousels, the kind you see when you click an image in an image grid on Twitter or FB. But there are many cases where you want the touch/swipe/trackpad interactions to function as a normal scroll area, with only the next/prev and indicator buttons sliding a whole pane.

Some research is required here to figure out exactly how best to manage this.

Option to disable swiping on pointer devices This is something to consider. Instagram and Twitter don't support swiping in their image gallery components. It's arguably a mistake, and I'd vote that it is a mistake.

Indicators We have identified three different types of indicator element.

  1. Indicator Button would render a <button> by default, and could optionally be rendered as a <div>. Sometimes they indicate the total panes count, others indicate the total slides count. This should be verified by research though, I'm now thinking they only indicate slides when they're progress bars, which would mean we may not need to support both behaviours on this component.
  2. Indicator Count would show the current slide number and the total slides count. Perhaps it should also be able to show the current pane number and the total panes count. Although perhaps it should just work with panes, as per my previous point.
  3. Indicator Progress is commonly seen on social media stories, where each slide has a timer and a progress bar. There is always only one progress bar per slide.

isScrolling state Expose the state, so consumers can animate/highlight something (like an indicator or scrollbar) while the carousel is scrolling.

Autoplay

  • Indicator progress bars are dependent on autoplay. If autoplay is not on, progress bars should not render.
  • We should provide a global interval, for simple carousels.
  • We also need a custom interval per slide, for story-style carousels.
  • Pause autoplay on interaction.

Cursor key navigation Cursor keys should navigate slides, even if there is no focusable content inside the slides. Like an image gallery carousel.

Many carousels have focusable content in each slide. For example, focusable cards on Deliveroo, focusable cards on Netflix, tabs in a carousel on Youtube etc. So we need to ensure that, for example, cursor key navigation still works on tabs, even though each tab would be inside an element like <CarouselItem>.

Nested carousels

  • For lightbox style carousels, the thumbnails (just indicator buttons) would need to be wrapped in a carousel themselves.

colmtuite avatar Jan 13 '21 17:01 colmtuite

I really really like where this is going! ♥️

You can set the amount of slides per pane

There is something interesting related to this in Flickity which I haven't seen many libs support and we haven't mentioned here: variable width cells (ie. images of differents widths/ratios for example).

This is kinda important I would say, and does change the way things will have to be calculated probably (especially around swiping, etc).

Indicators We have identified three different types of indicator element.

Indicator Button would render a

benoitgrelard avatar Jan 13 '21 19:01 benoitgrelard

variable width cells

I didn't think of this, so we should probably look into it a little. I see some carousel libs support it, but off the top of my head, I can't think of any valid use cases or any existing examples in the wild. It seems like it would always be a bad design.

but this seems to mention other stuff like number of panes, number of slides,

On Netflix in the screenshot posted above, the indicators reflect the number of panes/pages available, not the number of total slides.

In other carousels, like social media stories (Fleets, IG Stories, FB Stories etc.), the indicators reflect the total number of slides/items.

In both of the cases I've just mentioned, the indicators are not interactive. But often, you would want interactive indicators like the little button dots you mentioned.

colmtuite avatar Jan 14 '21 12:01 colmtuite

variable width cells

I didn't think of this, so we should probably look into it a little. I see some carousel libs support it, but off the top of my head, I can't think of any valid use cases or any existing examples in the wild. It seems like it would always be a bad design.

Here's one of the flickity examples: https://codepen.io/desandro/pen/GgQREP I can definitely see at least this use case for image galleries as you have portrait vs. landscape.

but this seems to mention other stuff like number of panes, number of slides,

On Netflix in the screenshot posted above, the indicators reflect the number of panes/pages available, not the number of total slides.

In other carousels, like social media stories (Fleets, IG Stories, FB Stories etc.), the indicators reflect the total number of slides/items.

In both of the cases I've just mentioned, the indicators are not interactive. But often, you would want interactive indicators like the little button dots you mentioned.

Ah ok I see, I wasn't clear on whether you meant the numbers should actually appear on the page (like 3/5). I see what you're saying now, meaning how many indicators based on if they're indicating slides or panes, gotcha.

benoitgrelard avatar Jan 14 '21 13:01 benoitgrelard

I can definitely see at least this use case for image galleries as you have portrait vs. landscape.

I would file this under "bad design". It's passable in this exact case (the full-width carousel), but still bad. The image should be centered horizontally and vertically inside a fixed-width container.

In a typical image gallery, where you're viewing a single image at a time (the kind on Twitter/IG/FB etc.), this would be very bad. You never want the UI (next/prev buttons etc.) around the image to shift. So the container should always be the same size, and the image is centered inside it.

So at this point, I'd still file this under "not important at all", and potentially even "we should actively discourage this".

colmtuite avatar Jan 14 '21 13:01 colmtuite

🤔 I don't see anything shifting on this example. But anyway, just making sure we capture all the things out there.

benoitgrelard avatar Jan 14 '21 13:01 benoitgrelard

This is still very much in DRAFT, but wanted to jot down progress so far in case there are any opinions on this direction so far:

Anatomy

See bullets below for detailed explanation of various bits.

<Carousel  
  as="div"                      // default `div`
  slidesPerPage={2}             // default `auto`
  loop={false}                  // default `true`?
  page={1}                      // default `undefined`
  defaultPage={1}               // default `undefined`
  onPageChange={page => {}}     // default `undefined`
  onScroll={event => {}}        // default `undefined`
  onDragScroll={event => {}}    // default `undefined`
>
  <CarouselSlide as="div" />
  <CarouselSlide as="div" />
  <CarouselSlide as="div" />
  <CarouselSlide as="div" />
  <CarouselSlide as="div" />
  <CarouselSlide as="div" />
  <CarouselSlide 
    as="div" 
    autoFocus                   // Scrolls into view
  />

  {/* non-interactive indicator */}
  <CarouselIndicator as="ol">
    <CarouselIndicatorItem as="li" />
  </CarouselIndicator>

  {/* interactive indicator */}
  <CarouselIndicator as="ol">
    <CarouselIndicatorItem as="li">
      <CarouselIndicatorButton as="a" />
    </CarouselIndicatorItem>
  </CarouselIndicator>

  <CarouselButtonPrevious as="button" />
  <CarouselButtonNext as="button" />
</Carousel>
  • CarouselIndicatorItem and CarouselIndicatorButton - My thinking here was that the user would render one of these and we would clone it for each page and if they wanted an indicator per slide then they would do slidesPerPage={1}. However, this doesn't cover the case where users might want to put their own content inside. I need to think about this part some more (hence DRAFT). I've been thinking about this (but not hugely keen):

    / 1. When there is one slide per page (for thumbnail case usually)
    CarouselIndicator as="ol">
    <CarouselIndicatorItem as="li"><img alt="thumb" /></CarouselIndicatorItem>
    <CarouselIndicatorItem as="li"><img alt="thumb" /></CarouselIndicatorItem>
    /CarouselIndicator>
    
    / 2. When you want indicator to clone for you
    CarouselIndicator as="ol">
    	<CarouselIndicatorItem as="li" />
    	</CarouselIndicator>
    
    / 3. When there are multiple slides per page and you want custom content 
    /    (this is the one I hate). Do we need this?
    CarouselIndicator as="ol">
    	{({ page }) => (
    	<CarouselIndicatorItem as="li">{page}</CarouselIndicatorItem>
    )}
    /CarouselIndicator>
    

    Perhaps we could stick with 2 for now and then 1 would be part of ImageCarousel (and 3. can do one 😅 )?

  • CarouselIndicatorButton - Using an anchor tag by default as I plan to get this working without JS as one of our first progressively enhanced SSR components and potentially use aria-current="step" here.

  • CarouselButtonPrevious and CarouselButtonNext - Prev/next page. If they want to navigate per slide then they would do slidesPerPage={1}. These would only render client-side. Users can scroll until JS kicks in.

  • slidesPerPage - If auto, a page is considered 100% width of the carousel. To page one slide at a time they can make the carousel the same width as a slide or make slides 100% width and set slidesPerPage to 1.

  • page - If set to a page that doesn't exist, will show closest page or page % total if looped.

  • autoFocus - The furthest slide in DOM with this attribute will scroll into view.

  • onPageChange - Still fires when dragging/scrolling when the page bounds have changed.

  • onScroll - Any kind of scroll, trackpad or dragging.

  • onDragScroll - Drag scroll only.

jjenzz avatar Jan 14 '21 19:01 jjenzz

A couple of initial thoughts, but otherwise I love the proposed API!

  • I think it might actually be kind of important to support autoplay. In a11y audits I've seen some implementations where someone used a lib without proper autoplay support and then hacked it by simulating click events, which is ... not great! It's not a super simple thing for a consumer to add later and do it well. Would it really be that much more complexity to include it out of the gate?
  • Does autoFocus imply that as slides change automatically that the new slide receives focus as it comes into view?
  • CarouselIndicatorItem and CarouselIndicatorButton - My thinking here was that the user would render one of these and we would clone it for each page and if they wanted an indicator per slide then they would do slidesPerPage={1}. However, this doesn't cover the case where users might want to put their own content inside. I need to think about this part some more (hence DRAFT). I've been thinking about this (but not hugely keen):

Didn't see this as an option in your post, but what about:

<CarouselIndicator as="ol">
  {({ pages }) => pages.map((page) => (
    <CarouselIndicatorItem as="li" key={page.id}>
      <CarouselIndicatorButton as="button" aria-label={page.title}>
        <img alt="" />
      </CarouselIndicatorButton>
    </CarouselIndicatorItem>
  ))}
</CarouselIndicator>

chaance avatar Jan 15 '21 02:01 chaance

I think it might actually be kind of important to support autoplay

Yep, I think we're all agreed we need this but as Colm touched on above, we're thinking that could be the second release of this (which we could work on right away if we wanted to). There are features like autoplay that appear to be more suited to image carousels. So, we might just build a separate Slideshow component that would re-compose Carousel and include things like autoplay and arrow key navigation.

When we come to do that work, perhaps we will realise it should just be part of Carousel, which is fine. We're just omitting it now to help save time on those discussions (and the added complexity of doing it well like you say) so we can get an MVP version out there sooner.

Does autoFocus imply that as slides change automatically that the new slide receives focus as it comes into view?

Nope. I'm imagining it much like autoFocus on an input. So, when the Carousel mounts/loads, if a CarouselSlide has autoFocus it will gain focus and scrollIntoView. I added this because the consumer won't necessarily know what page a particular slide is on so they can't just do defaultPage={3}. Pages could be dependent on viewport dimensions. That's why I also added this:

onPageChange - Still fires when dragging/scrolling when the page bounds have changed.

So, if they have page={2} but also have autoFocus on a slide that isn't on page 2, it would scroll into view and call the onPageChange callback :+1:

Didn't see this as an option in your post, but what about

That is the same as my proposal I believe except I meant we would do the map under the hood for them. I think I confused the situation by rendering page in the item. That wasn't meant to be a component, it's just the page number. The focus of the proposal was the function as child thing tho like yours:

<CarouselIndicator>
	{({ page }: { page: number }) => (
		<CarouselIndicatorItem>
      		<CarouselIndicatorButton>
				Page {page}
			</CarouselIndicatorButton>
		</CarouselIndicatorItem>
	)}
</CarouselIndicator>

There is no page data object here, only what page number the indicator is trying to render an item for.

I'm just not sure if this pattern is worth it for our first release. This is the sort of thing I'd prefer to pass on personally until we understand how people are using it and it is specifically requested for something that cannot be achieved without it. I'm struggling to think of genuine use-cases that 1 and 2 don't already cover. Can anyone else?

jjenzz avatar Jan 15 '21 11:01 jjenzz

When we come to do that work, perhaps we will realise it should just be part of Carousel, which is fine. We're just omitting it now to help save time on those discussions (and the added complexity of doing it well like you say) so we can get an MVP version out there sooner.

👍

There is no page data object here, only what page number the indicator is trying to render an item for.

Right, I think the difference is that I'm thinking of an API where we register pages in context as slides are rendered. This let's consumers map over that data if they want, or they just render the indicators one at a time, whatever they want. I think I get what you're saying about cloning them so they only have to render once, but I generally like the APIs to be as explicit as possible. But I think generally I agree that:

This is the sort of thing I'd prefer to pass on personally until we understand how people are using it and it is specifically requested for something that cannot be achieved without it.

chaance avatar Jan 15 '21 17:01 chaance

This let's consumers map over that data if they want

What data are you referring to here? A page doesn't have data as far as I understand it (apart from what page number it is) and neither do slides, they just have children. So, since the indicators render one per page, we can tell it which page number it is being rendered for but I'm not sure what other data there is?

I think I get what you're saying about cloning them

Ah, no, cloning wouldn't happen here. It would only happen in example 2. With the child as function I would do something like this internally:

function CarouselIndicator({ children }) {
	const context = useCarouselContext();
	return [...Array(context.pagesCount)].map((_, index) => children({ page: index + 1 }))
}

The consumer doesn't create the pages, we do internally, they're like an invisible concept that only we know about because the number of pages created depends entirely on viewport width, number of slides & slidesPerPage etc. so there isn't really anything for them to "map" over. They don't have titles for example, slides might but slides !== pages and indicators are for pages.

Tbh, I'm actually starting to wonder if we should just do away with 2 and have 1 and 3? I like to try to avoid children as function where possible but maybe it is the best approach here. It would cover case 2 and keep things open for whatever crazy stuff people get up to.

Or, maybe we should just do 3 only 😬 It's a consistent API for all cases then 😂

jjenzz avatar Jan 15 '21 18:01 jjenzz

The consumer doesn't create the pages, we do internally, they're like an invisible concept that only we know about because the number of pages created depends entirely on viewport width, number of slides & slidesPerPage etc. so there isn't really anything for them to "map" over.

This was my assumption, my thought is that by exposing this as data, that's what I'm imagining they could map over to render the indicators. That way they never have to think about how many indicators they need but they also have full control of composition for each indicator that is rendered.

It's very possible that I'm completely misreading the entire concept here somehow, haha. Happy to chat quickly if you want to walk me through it, because in my head what I'm thinking still kind of makes the most sense to me. 😅

chaance avatar Jan 15 '21 19:01 chaance

Okay so, @chaance and I just had a quick call. We're on the same page now and have agreed to do the map thing so indicator would look something like this:

function CarouselIndicator({ children }) {
	const context = useCarouselContext();
	return children([...Array(context.pagesCount)]);
}

Even though they're just mapping over some page numbers, we decided to go with this anyway because having the map on the consumer side is more explicit and clearly shows for them that their render is rendering multiple things there.

Also, I'm going to go with this API alone and remove options 1 and 2 as this covers all cases.

Let me know if anyone has any objections 🙂

jjenzz avatar Jan 15 '21 20:01 jjenzz

Catching up with this now, and it's awesome you got to the conclusion I was about to suggest to! ♥️ My main reason for it was that we've been trying to have all parts exposed for people to hook their own events, etc. Having us clone the indicators would kinda prevent that as users wouldn't be able to for example add an onClick to a specific indicator.

A few other minor points:

  • I feel like CarouselIndicator should be CarouselIndicators (as I understand it's the list containing the indicators)
  • the I think CarouselIndicatorItem would become CarouselIndicator too, this way we would have these potential hierarchies:
    • CarouselIndicators
      • CarouselIndicator or:
    • CarouselIndicators
      • CarouselIndicator
        • CarouselIndicatorButton
  • CarouselButtonPrevious and CarouselButtonNext could be CarouselPrevious and CarouselNext like in Dialog, we've gone simpler names like DialogClose, AlertDialogAction, AlertDialogCancel, etc

onDragScroll - Drag scroll only.

What is drag scroll?

benoitgrelard avatar Jan 18 '21 11:01 benoitgrelard

Another option would be:

CarouselIndicatorList
  CarouselIndicator

This would be inline with TabList.

It seems odd to me that CarouselIndicatorButton is nested inside CarouselIndicator. I would have expected CarouselIndicator to be the actual


Some more carousels for inspiration.

https://cleanshot.com/

If you scroll down to "Annotate", you can see a carousel where the indicator buttons actually have the progress indicator around them. This is a common design pattern on marketing sites.

If you scroll down to "Feedback", you'll see a testimonial carousel. As the carousel slides, the first slide shrinks and fades out. Of course this animation would be handled consumer-side, I'm just noting it so we make sure it's possible.

colmtuite avatar Jan 18 '21 12:01 colmtuite

Another option would be:

CarouselIndicatorList
  CarouselIndicator

This would be inline with TabList.

I like that too.

It seems odd to me that CarouselIndicatorButton is nested inside CarouselIndicator. I would have expected CarouselIndicator to be the actual , no?

Isn't it so that we can do both interactive and non-interactive indicators?

benoitgrelard avatar Jan 18 '21 12:01 benoitgrelard

Agree with all the naming changes. Thanks @benoitgrelard :+1:

It seems odd to me that CarouselIndicatorButton is nested inside CarouselIndicator. I would have expected CarouselIndicator to be the actual <button>, no?

@colmtuite I had originally thought that too but as Benoit has pointed out we need both non-interactive and interactive. So, CarouselIndicator would be an li and if you want an interactive one you would put a CarouselIndicatorButton inside it that would be an a under the hood and bind all the click logic for you. You would end up with something like this DOM for each:

// non-interactive
<ol>
  <li aria-current="step"><span style="visually hidden styles">1</span></li>
  <li><span style="visually hidden styles">2</span></li>
</ol>

// interactive
<ol>
 	 <li aria-current="step">
		<a href="#slide-id1">
			<span style="visually hidden styles">1</span>
		</a>
	</li>
	<li>
		<a href="#slide-id2">
			<span style="visually hidden styles">2</span>
		</a>
	</li>
</ol>

We would add the visually hidden number for them if they don't provide their own children for accessibility purposes. Also:

Using an anchor tag by default as I plan to get this working without JS as one of our first progressively enhanced SSR components [...].

jjenzz avatar Jan 19 '21 10:01 jjenzz

What is drag scroll?

Some sliders when you drag slightly they slide the whole pane for you (scroll snap). Others, it behaves the same as dragging a scrollbar (the Google cast example does this, it just scrolls). I was differentiating these two behaviours.

For now we're thinking, just let it scroll and if people want the snap behaviour they can use CSS or control it with whichever animation libs they choose.

jjenzz avatar Jan 19 '21 10:01 jjenzz

Here's a carousel where the progress bar is not attached to the indicator buttons. https://www.jpl.nasa.gov/

On Twitch front page, they have a carousel where the slides themselves act as indicators, sliding the carousel. https://www.twitch.tv/

colmtuite avatar Jan 21 '21 16:01 colmtuite

Any news on when this will ship? 🤓 Currently using swiperjs and my hands feel dirty

schonert avatar Sep 01 '21 10:09 schonert

@schonert haha. Right now we're working on Select, Toast, Menubar, and HoverMenu. We'll move back to Carousel after that, though it will likely become ScrollableList (from the Stitches website)

colmtuite avatar Sep 01 '21 18:09 colmtuite

Hi! I saw that Select and Toast is in beta and congrats! Im curious if the Carousel next in line?

kharann avatar Mar 06 '22 17:03 kharann

Hi again! Any update on this issue :), perhaps anything we could do to help?

kharann avatar Jun 25 '22 13:06 kharann

Any updates? What can we do to help? I sincerely look forward to this primitive, so as not to connect third-party libraries, and this was already in our favorite RadixUI 🙏

its-monotype avatar Sep 26 '22 18:09 its-monotype