npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wethegit/gordito-carousel

v1.0.0

Published

A robust carousel with support for infinite slides and touch.

Downloads

61

Readme

WTC Gordito Carousel 🐽

A small vanilla carousel core with an explicit DOM contract. The library owns behavior, runtime state, and measurement. Authors own markup, semantics, layout styling, and animation.

Quick Start

npm install @wethegit/gordito-carousel
import { WtcGorditoCarousel } from '@wethegit/gordito-carousel';
import '@wethegit/gordito-carousel/wtc-gordito-carousel.css';

const carousel = new WtcGorditoCarousel(document.querySelector('[data-wtcg-carousel]'), {
  pagination: true,
});
<section data-wtcg-carousel aria-roledescription="carousel" aria-label="Featured items">
  <div id="featured-carousel-slides" data-wtcg-list>
    <ul data-wtcg-track>
      <li data-wtcg-slide>First slide</li>
      <li data-wtcg-slide>Second slide</li>
      <li data-wtcg-slide>Third slide</li>
    </ul>
  </div>

  <button
    data-wtcg-prev
    type="button"
    aria-controls="featured-carousel-slides"
    aria-label="Previous slide"
  >
    Previous
  </button>
  <button
    data-wtcg-next
    type="button"
    aria-controls="featured-carousel-slides"
    aria-label="Next slide"
  >
    Next
  </button>

  <div role="group" aria-label="Choose slide">
    <ol data-wtcg-pagination>
      <li>
        <button data-wtcg-page type="button" aria-label="Slide 1">1</button>
      </li>
      <li>
        <button data-wtcg-page type="button" aria-label="Slide 2">2</button>
      </li>
      <li>
        <button data-wtcg-page type="button" aria-label="Slide 3">3</button>
      </li>
    </ol>
  </div>

  <p data-wtcg-status aria-live="polite" aria-atomic="true">Slide {current} of {total}</p>
</section>

Options can also be set in markup. Programmatic settings are merged first, then data-wtcg overrides them.

<section data-wtcg-carousel data-wtcg='{"pagination":true,"drag":"free"}'>...</section>

Exports

| Export | Purpose | | ------------------------------------ | ------------------------------------------------- | | WtcGorditoCarousel | Carousel class. | | WTC_GORDITO_CAROUSEL_DEFAULTS | Default option object. | | WTC_GORDITO_CAROUSEL_STATUS_TOKENS | {current} and {total} status template tokens. |

DOM Contract

Required elements:

  • [data-wtcg-carousel]: Carousel root.
  • [data-wtcg-list]: Viewport/list element.
  • [data-wtcg-track]: Moving track element.
  • [data-wtcg-slide]: Slide element. Direct children of the track are treated as slides by default.

Optional controls:

  • [data-wtcg-prev]: Previous control.
  • [data-wtcg-next]: Next control.
  • [data-wtcg-pagination] > * > [data-wtcg-page]: Pagination controls.
  • [data-wtcg-status]: Status template for current/total text.

The core never creates wrappers, arrows, pagination, or status markup. If optional controls exist and their corresponding option is enabled, the core wires behavior. If controls are missing, it does nothing.

Controls

Arrows are attach-only controls. Keep arrows enabled when you provide arrow markup; setting arrows: false disables arrow behavior. You do not need to set arrows: false to prevent generated arrows because the library never generates controls.

Pagination controls are regular slide-picker buttons. This matches the carousel grouped-button model from the WAI-ARIA Authoring Practices.

Arrows can live in the same list as pagination:

<ol data-wtcg-pagination>
  <li>
    <button
      data-wtcg-prev
      type="button"
      aria-controls="featured-carousel-slides"
      aria-label="Previous slide"
    >
      Previous
    </button>
  </li>
  <li><button data-wtcg-page type="button" aria-label="Slide 1">1</button></li>
  <li><button data-wtcg-page type="button" aria-label="Slide 2">2</button></li>
  <li><button data-wtcg-page type="button" aria-label="Slide 3">3</button></li>
  <li>
    <button
      data-wtcg-next
      type="button"
      aria-controls="featured-carousel-slides"
      aria-label="Next slide"
    >
      Next
    </button>
  </li>
</ol>

Core behavior for pagination:

  • Click or press a pagination button to choose the represented slide.
  • The active pagination button receives [data-wtcg-active] and aria-current="true".
  • Inactive pagination buttons have aria-current removed.
  • Extra pagination buttons use the native hidden attribute when the current layout has fewer reachable positions than slides.

Status

[data-wtcg-status] is an optional template. The author owns live-region semantics and localization. The core replaces exported template tokens with the current logical slide number and total logical slide count.

<p data-wtcg-status aria-live="polite" aria-atomic="true">
  Slide {WTC_GORDITO_CAROUSEL_STATUS_TOKENS.CURRENT} of {WTC_GORDITO_CAROUSEL_STATUS_TOKENS.TOTAL}
</p>

Tokens:

  • WTC_GORDITO_CAROUSEL_STATUS_TOKENS.CURRENT: Current original slide number.
  • WTC_GORDITO_CAROUSEL_STATUS_TOKENS.TOTAL: Original slide count.

Infinite clones are not counted in status text.

Accessibility

Author responsibilities:

  • Give the carousel root an accessible name with aria-label or aria-labelledby.
  • Add aria-roledescription="carousel" to the carousel root when useful.
  • Use native button elements for previous, next, and picker controls.
  • Give controls accessible names in author markup.
  • Give slides useful accessible names where possible.
  • Add aria-live="polite" to a dedicated status element when announcements are useful.

Core accessibility behavior:

  • Slides outside the active rendered range are set to inert and aria-hidden="true".
  • Active rendered slides receive aria-hidden="false" and are not inert.
  • During pointer drag, rendered slide state follows the drag preview.
  • Pagination, status text, events, and committed currentSlide update only after release.
  • Pagination state is exposed with aria-current="true".
  • Previous and next disabled state is exposed with native disabled and aria-disabled.
  • Static roles, labels, roledescriptions, and control relationships remain author markup.

CSS Model

The library CSS is intentionally structural and uses :where([data-wtcg-*]) selectors inside @layer wtc-gordito-carousel. Specificity stays low so ordinary app CSS can override it.

Animation is CSS-owned. Core styles include a basic transform transition, the core writes transforms, and two data attributes on the carousel root control when the track transform must NOT animate:

[data-wtcg-track] {
  transition: transform 400ms ease;
}

/* State attributes live on the carousel root [data-wtcg-carousel] */
[data-wtcg-carousel] {
  &[data-wtcg-instant] [data-wtcg-track],
  &[data-wtcg-dragging] [data-wtcg-track] {
    transition: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-wtcg-track] {
    transition: none;
  }
}

Two suppression states

| Attribute | Element | When set | | ---------------------- | ---------------------- | ------------------------------------------------------------------------------------------------------ | | [data-wtcg-instant] | [data-wtcg-carousel] | Invisible position corrections: initial layout, responsive refresh, infinite-loop clone normalization. | | [data-wtcg-dragging] | [data-wtcg-carousel] | Active pointer drag — the track must follow the pointer, not animate. |

Neither attribute should affect slide-level transitions (opacity, scale, etc.). Slide transitions remain active during both drag and instant corrections.

Mandatory suppression rules

⚠️ If you override the track transition, you MUST also restore the suppression rules. Without them, the carousel will jank during drag and visible clone corrections.

The core library ships the suppression rules in @layer wtc-gordito-carousel using :where() (zero specificity). Any consumer CSS with non-zero specificity or unlayered CSS will override BOTH the default transition AND the suppression rules — even accidentally.

Minimal required suppression when customizing the track transition:

.my-carousel {
  [data-wtcg-track] {
    transition: transform 500ms cubic-bezier(0.22, 1, 0.36, 1);
  }

  /* ⚠️ MANDATORY — suppress track transform during instant corrections */
  &[data-wtcg-instant] [data-wtcg-track],
  /* ⚠️ MANDATORY — suppress track transform during pointer drag */
  &[data-wtcg-dragging] [data-wtcg-track] {
    transition: none;
  }
}

Responsive behavior

Responsive behavior is CSS-first. Change custom properties with media queries or container queries; the core watches size changes and recalculates from the rendered DOM layout.

.my-carousel {
  --wtcg-slides: 1;
  --wtcg-scroll: 1;
  --wtcg-slide-gap: 1rem;
  --wtcg-slide-size: 100cqw;
  container-type: inline-size;
}

@container (min-width: 42rem) {
  .my-carousel {
    --wtcg-slides: 3;
    --wtcg-scroll: 2;
    --wtcg-slide-size: calc((100cqw - var(--wtcg-slide-gap) * 2) / 3);
  }
}

CSS Variables

Author variables:

| Variable | Purpose | Default | | ----------------------- | ---------------------------------------------------------------------------- | -------- | | --wtcg-slides | Number of slides treated as visible/active. | 1 | | --wtcg-scroll | Number of slides advanced by arrows and fixed drag. | 1 | | --wtcg-slide-size | Width applied to each [data-wtcg-slide]. Core measures the rendered boxes. | auto | | --wtcg-slide-gap | Track gap between adjacent slide boxes. Core measures the computed gap. | 0px | | --wtcg-center-padding | List inline padding when centerMode is enabled. | 0px | | --wtcg-pagination-gap | Gap between pagination controls. | 0.5rem |

Runtime slide variables written by JavaScript:

| Variable | Purpose | | --------------------------- | --------------------------------------------------------------------------------- | | --wtcg-slide-index | Original slide index, normalized to the original slide count. | | --wtcg-slide-render-index | Rendered index in the track, including clones. | | --wtcg-slide-offset | Signed distance from the active rendered slide. Fractional during pointer drag. | | --wtcg-slide-distance | Absolute distance from the active rendered slide. Fractional during pointer drag. | | --wtcg-slide-side | Direction from the active slide: -1, 0, or 1. |

State Attributes

| Attribute | Owner | Element | Meaning | | ------------------------- | ----- | ---------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | [data-wtcg-initialized] | Core | [data-wtcg-carousel] | Carousel has initialized. | | [data-wtcg-active] | Core | [data-wtcg-slide] | Rendered slide is in the active visible range, or pagination control represents the current range. | | [data-wtcg-current] | Core | [data-wtcg-slide] | Rendered slide representing the current logical slide. | | [data-wtcg-center] | Core | [data-wtcg-carousel] | Root uses center mode, or rendered slide is currently centered. | | [data-wtcg-draggable] | Core | [data-wtcg-carousel] | Pointer dragging is enabled on the carousel. Consumers who override the track transition MUST suppress it under this attribute (see CSS Model section). | | [data-wtcg-dragging] | Core | [data-wtcg-carousel] | Pointer drag is active on the carousel. Track transform must follow the pointer. Consumers who override the track transition MUST suppress it under this attribute (see CSS Model section). | | [data-wtcg-instant] | Core | [data-wtcg-carousel] | Track transform must not animate. Set during initial layout, responsive refresh, and infinite-loop clone normalization. Consumers who override the track transition MUST suppress it under this attribute (see CSS Model section). |

Infinite Looping

Infinite mode uses a bounded clone loop. The core creates one or more full logical slide sets before and after the original slides, animates onto those clones when crossing an edge, then aligns the track position back to the matching original slide with [data-wtcg-instant].

Tradeoffs:

  • DOM growth is bounded and predictable.
  • Original child order is preserved for framework renderers.
  • Repeated order stays consistent because clones are created in full logical sets.
  • Variable-width slides stay measurable because the core positions against rendered DOM boxes.

Inactive duplicate rendered slides remain aria-hidden and inert. When an infinite clone is inside the active rendered range, that clone becomes the accessible representative for its logical slide. Status and pagination state are still based on original logical slide indexes.

Options

new WtcGorditoCarousel(element, {
  adaptiveHeight: false,
  arrows: true,
  centerMode: false,
  pagination: false,
  drag: true,
  edgeFriction: 0.35,
  focusOnSelect: false,
  focusOnChange: false,
  infinite: true,
  initialSlide: 0,
  slide: '',
  touchThreshold: 5,
  waitForAnimate: true,
});

| Option | Type | Description | | ---------------- | ---------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- | | adaptiveHeight | boolean | When --wtcg-slides resolves to 1, match list height to the current slide. | | arrows | boolean \| string \| HTMLElement | Attach existing previous/next controls. true searches the root; a selector or element searches that target. | | centerMode | boolean | Keep the current slide centered and allow partial neighboring slides. | | pagination | boolean \| string \| HTMLElement | Attach existing pagination controls. true searches the root; a selector or element searches that target. | | drag | boolean \| "fixed" \| "free" | false disables drag. true/"fixed" advances by --wtcg-scroll. "free" snaps to the nearest slide reached by drag distance. | | edgeFriction | number | Dampening multiplier when dragging past a non-infinite edge. | | focusOnSelect | boolean | Clicking a slide moves that slide into the current position. | | focusOnChange | boolean | Move browser focus to the current slide after each change. Use carefully. | | infinite | boolean | Clone edge slides so movement can wrap. | | initialSlide | number | Zero-based initial original slide index. | | slide | string | Optional selector to narrow which direct [data-wtcg-slide] children participate. | | touchThreshold | number | Swipe threshold as a fraction of carousel width. 5 means one-fifth of the width. | | waitForAnimate | boolean | Ignore new navigation requests while a transition is in progress. |

Methods

| Method | Description | | ------------------------------------------------- | ------------------------------------------------------------------------------------ | | next(event?) | Advance by --wtcg-scroll. | | prev(event?) | Move backward by --wtcg-scroll. | | goTo(index, dontAnimate = false) | Move to an original slide index. | | getOption(option) | Return a runtime option value. | | setOption(option, value, refresh = false) | Update one option. Pass refresh: true for layout/control changes. | | setOption(options, refresh = false) | Update multiple options. | | refresh(initializing = false) | Rebuild from current DOM children and options. | | destroy(refresh = false) | Remove clones/listeners/state and restore original slides. | | addSlide(markup, index?, addBefore?) | Add one slide and rebuild. Prefer declarative DOM changes in framework integrations. | | removeSlide(index, removeBefore?, removeAll?) | Remove one or all slides and rebuild. | | filterSlides(filter) | Filter original slides by selector or predicate and rebuild. | | unfilterSlides() | Clear the active filter and rebuild. | | WtcGorditoCarousel.initAll(selector?, options?) | Initialize all matching elements. |

Accessors

| Accessor | Description | | --------- | ----------------------------- | | current | Current original slide index. |

Events

Events are bubbling CustomEvents dispatched on the carousel root with the wtcg: prefix.

| Event | Detail | | ------------------- | --------------------------------------- | | wtcg:init | { carousel } | | wtcg:beforeChange | { carousel, currentSlide, nextSlide } | | wtcg:afterChange | { carousel, currentSlide } | | wtcg:reInit | { carousel } | | wtcg:setPosition | { carousel } | | wtcg:swipe | { carousel, direction } | | wtcg:destroy | { carousel, refresh } |