akiran / react-slick

React carousel component
http://react-slick.neostack.com/
MIT License
11.73k stars 2.1k forks source link

Add an option to position dots above or below the main slider #2300

Closed T-Fletcher closed 8 months ago

T-Fletcher commented 11 months ago

Situation

I'm replicating a slideshow widget using React Slick to serve slides containing images wrapped in links.

This functionality could be hand-rolled but given my project already use React Slick elsewhere and the control it provides, I've gone down this path.

The UI I need to copy has the dots/slide group titles above the slides themselves i.e. pseudocoded:

group 1 | group 2

group 1
 - link 1
 - link2

group 2
 - link 1
 - link2
...

In my component, I've used appendDots and customPaging to insert text from slide group headings into the Dots as navigation:

const mainSliderSettings = {
    accessibility: true,
    adaptiveHeight: true,
    arrows: false,
    autoplay: false,
    dots: true,
    fade: true,
    className: 'slick-tiles',
    infinite: false,
    slidesToScroll: 1,
    slidesToShow: 1,
    appendDots: (dots) => buildNavWrapper(dots),
    customPaging: (i) =>
      node.relationships?.field_tile_image_groups[i].field_title ? (
        <div
          className="section__nav-item"
          key={`${sectionTitle && convertStringToId(sectionTitle)}-pager-${i}`}
        >
          <a data-slide-index={i} href="#">{node.relationships?.field_tile_image_groups[i].field_title}</a>
        </div>
      ) : (
        <></>
      ),
  };

...

const buildNavWrapper = (dots) => {
    return (
      tileGroupLength() > 1 && (
        <nav className="section__nav-wrapper t-center">
          <ul
            className="section__nav text-dots"
            id={
              `${sectionTitle && convertStringToId(sectionTitle)}-pager` ||
              `slider-${Math.ceil(Math.random() * 10000000)}`
            }
            style={{
              position: 'relative',
              left: '-50%',
            }}
          >
            {dots}
          </ul>
        </nav>
      )
    );
  };

Which results in:

<div class="slick-slider slick-tiles slick-initialized" dir="ltr">
  <div class="slick-list" style="height: 715px">
    <div class="slick-track">...slides here</div>
  </div>
  <nav class="slick-dots">
    <ul
      class="section__nav text-dots"
      id="tile-images-pt-heading-show-all-enabled-pager"
    >
      <li class="slick-active">
        <a data-slide-index="0" href="#">Tile images group 1</a>
      </li>
      <li class="">
        <a data-slide-index="1" href="#">Tile images group 2</a>
      </li>
      <li class="">
        <a data-slide-index="2" href="#">Tile images group 3</a>
      </li>
    </ul>
  </nav>
</div>

Problem

This works nicely except for one snag:

By default, Dots render under the slide, and there seems to be no way to change this at a HTML level. There may be a good reason this isn't a feature?

This creates an accessibility issue when dots visually appear high up or above the slides: tabbing through the page skips the dots, jumps below to any tab-able elements in the slides, then jumps back up to the dots navigation. This is confusing and jarring for users.

The prevailing wisdom around altering element positions and tabbing order is to just fix the HTML wherever possible, and only tamper with tabindex when this cannot be done.

Proposed solution

I'd like to add a prop that lets users specify if the dots should be rendered above or below the slideshow, provided this doesn't clash with existing features. Something like:

const sliderSettings = {
   dots: true,
   dotsAboveSlides: true,
   ...
}

This would turn the above HTML into:

<div class="slick-slider slick-tiles slick-initialized" dir="ltr">
  <nav class="slick-dots">
   ...
  </nav>
  <div class="slick-list" style="height: 715px">
    <div class="slick-track">...slides here</div>
  </div>
</div>

People have asked about how to move the dots visually, but apparently not semantically (unless I just can't find it).

Would anyone else find this useful?

Related issues

114

1441

akiran commented 8 months ago

We follow the slick-carousel API. So we can't add new options.

You can implement this usecase with customPaging feature or passing custom CSS class to dotsClass https://react-slick.neostack.com/docs/example/custom-paging

T-Fletcher commented 8 months ago

@akiran No worries, I don't believe the customPaging feature gives this level of control as while you can customize the rendered HTML output of the controls, you can't change where that HTML is added semantically (my original issue).

I'll stick with a patch for my case, but it'd be great to see this added to Slick someday.