Hooks — beforeChange / afterChange

hooks() exposes lifecycle callbacks. Here beforeChange fades the outgoing slide out and afterChange logs the active slide.

1
2
3
4
5

View code
<div id="my-slider">
  <div class="demo-slide s-purple">1</div>
  <div class="demo-slide s-blue">2</div>
  <div class="demo-slide s-green">3</div>
  <div class="demo-slide s-amber">4</div>
  <div class="demo-slide s-red">5</div>
</div>
<p id="hook-log" style="margin-top:1rem;font-size:.85rem;color:#6b7280"></p>
.demo-slide { height:260px; display:flex; align-items:center; justify-content:center; font-size:2.5rem; font-weight:700; color:#fff; transition: opacity .3s; }
.demo-slide { height:260px; display:flex; align-items:center; justify-content:center; font-size:2.5rem; font-weight:700; color:#fff; transition: opacity .3s; }
import { Slider } from '@andresclua/sliderkit'
import { arrows, hooks } from '@andresclua/sliderkit-plugins'

const log = document.getElementById('hook-log')

new Slider('#my-slider', {
  items: 1,
  loop: true,
  speed: 300,
  plugins: [
    arrows(),
    hooks({
      onInit({ slides }) {
        log.textContent = 'onInit — ' + slides.length + ' slides ready'
      },
      beforeChange({ outgoing, incoming, direction }) {
        if (outgoing) outgoing.style.opacity = '0.3'
        log.textContent = 'beforeChange → ' + direction
      },
      afterChange({ slide }) {
        slide.style.opacity = '1'
        log.textContent = 'afterChange — now showing slide ' + slide.textContent.trim()
      },
      onDragStart() { log.textContent = 'onDragStart' },
      onDragEnd()   { log.textContent = 'onDragEnd'   },
    }),
  ],
})

Hooks — GSAP content stagger

fadeEffect handles the cross-fade; hooks + GSAP stagger the inner elements of each incoming slide.

01 / Setup

One import,
one line of code.

No config files. No wrappers. Drop it in and go.

02 / Plugins

Only load
what you need.

Tree-shakeable plugin system. Zero dead code in your bundle.

03 / TypeScript

Full type safety
out of the box.

Written in TypeScript. Autocomplete for every option and event.

04 / Performance

Under 10 kb
gzipped.

Tiny core, no runtime dependencies, hardware-accelerated CSS.

05 / A11y

Accessible
by default.

WAI-ARIA carousel pattern built in. Keyboard navigation included.

View code
<div id="my-slider">
  <div class="fade-slide" style="background:linear-gradient(135deg,#6C2BD9,#3b82f6)">
    <span class="fade-tag">01 / Setup</span>
    <h2 class="fade-title">One import,<br>one line of code.</h2>
    <p class="fade-sub">No config files. No wrappers. Drop it in and go.</p>
  </div>
  <div class="fade-slide" style="background:linear-gradient(135deg,#0ea5e9,#06b6d4)">
    <span class="fade-tag">02 / Plugins</span>
    <h2 class="fade-title">Only load<br>what you need.</h2>
    <p class="fade-sub">Tree-shakeable plugin system. Zero dead code in your bundle.</p>
  </div>
  <div class="fade-slide" style="background:linear-gradient(135deg,#10b981,#84cc16)">
    <span class="fade-tag">03 / TypeScript</span>
    <h2 class="fade-title">Full type safety<br>out of the box.</h2>
    <p class="fade-sub">Written in TypeScript. Autocomplete for every option and event.</p>
  </div>
  <div class="fade-slide" style="background:linear-gradient(135deg,#f59e0b,#ef4444)">
    <span class="fade-tag">04 / Performance</span>
    <h2 class="fade-title">Under 10 kb<br>gzipped.</h2>
    <p class="fade-sub">Tiny core, no runtime dependencies, hardware-accelerated CSS.</p>
  </div>
  <div class="fade-slide" style="background:linear-gradient(135deg,#ec4899,#8b5cf6)">
    <span class="fade-tag">05 / A11y</span>
    <h2 class="fade-title">Accessible<br>by default.</h2>
    <p class="fade-sub">WAI-ARIA carousel pattern built in. Keyboard navigation included.</p>
  </div>
</div>
.fade-slide {
  height: 340px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 56px;
  border-radius: 12px;
  color: #fff;
}
.fade-tag {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.7;
  margin-bottom: 16px;
}
.fade-title {
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1.15;
  margin: 0 0 16px;
}
.fade-sub {
  font-size: 1rem;
  opacity: 0.8;
  max-width: 380px;
  margin: 0;
  line-height: 1.6;
}
.fade-slide {
  height: 340px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 56px;
  border-radius: 12px;
  color: #fff;
}
.fade-tag {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.7;
  margin-bottom: 16px;
}
.fade-title {
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1.15;
  margin: 0 0 16px;
}
.fade-sub {
  font-size: 1rem;
  opacity: 0.8;
  max-width: 380px;
  margin: 0;
  line-height: 1.6;
}
import gsap from 'gsap'
import { Slider } from '@andresclua/sliderkit'
import { arrows, pagination, hooks } from '@andresclua/sliderkit-plugins'
import { fadeEffect } from '@andresclua/sliderkit-effects'

function animateIn(slide) {
  gsap.from(slide.querySelectorAll('.fade-tag, .fade-title, .fade-sub'), {
    y: 28,
    autoAlpha: 0,
    stagger: 0.1,
    duration: 0.55,
    ease: 'power3.out',
    clearProps: 'all',
  })
}

new Slider('#my-slider', {
  items: 1,
  loop: true,
  speed: 600,
  plugins: [
    arrows(),
    pagination({ type: 'dots', clickable: true }),
    fadeEffect({ duration: 600, easing: 'ease-in-out' }),
    hooks({
      onInit({ slides }) {
        animateIn(slides[0])
      },
      beforeChange({ incoming }) {
        gsap.from(incoming.querySelectorAll('.fade-tag, .fade-title, .fade-sub'), {
          y: 28,
          autoAlpha: 0,
          stagger: 0.1,
          duration: 0.55,
          ease: 'power3.out',
          delay: 0.25,
          clearProps: 'all',
        })
      },
    }),
  ],
})

Hooks — direction-aware content slide-in

The direction value tells you which arrow was clicked. Content enters from the right on forward, from the left on backward.

01 / Setup

One import,
one line of code.

No config files. No wrappers. Drop it in and go.

02 / Plugins

Only load
what you need.

Tree-shakeable plugin system. Zero dead code in your bundle.

03 / TypeScript

Full type safety
out of the box.

Written in TypeScript. Autocomplete for every option and event.

04 / Performance

Under 10 kb
gzipped.

Tiny core, no runtime dependencies, hardware-accelerated CSS.

05 / A11y

Accessible
by default.

WAI-ARIA carousel pattern built in. Keyboard navigation included.

View code
<div id="my-slider">
  <div class="fade-slide" style="background:linear-gradient(135deg,#6C2BD9,#3b82f6)">
    <span class="fade-tag">01 / Setup</span>
    <h2 class="fade-title">One import,<br>one line of code.</h2>
    <p class="fade-sub">No config files. No wrappers. Drop it in and go.</p>
  </div>
  <div class="fade-slide" style="background:linear-gradient(135deg,#0ea5e9,#06b6d4)">
    <span class="fade-tag">02 / Plugins</span>
    <h2 class="fade-title">Only load<br>what you need.</h2>
    <p class="fade-sub">Tree-shakeable plugin system. Zero dead code in your bundle.</p>
  </div>
  <div class="fade-slide" style="background:linear-gradient(135deg,#10b981,#84cc16)">
    <span class="fade-tag">03 / TypeScript</span>
    <h2 class="fade-title">Full type safety<br>out of the box.</h2>
    <p class="fade-sub">Written in TypeScript. Autocomplete for every option and event.</p>
  </div>
  <div class="fade-slide" style="background:linear-gradient(135deg,#f59e0b,#ef4444)">
    <span class="fade-tag">04 / Performance</span>
    <h2 class="fade-title">Under 10 kb<br>gzipped.</h2>
    <p class="fade-sub">Tiny core, no runtime dependencies, hardware-accelerated CSS.</p>
  </div>
  <div class="fade-slide" style="background:linear-gradient(135deg,#ec4899,#8b5cf6)">
    <span class="fade-tag">05 / A11y</span>
    <h2 class="fade-title">Accessible<br>by default.</h2>
    <p class="fade-sub">WAI-ARIA carousel pattern built in. Keyboard navigation included.</p>
  </div>
</div>
.fade-slide {
  height: 340px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 56px;
  border-radius: 12px;
  color: #fff;
}
.fade-tag {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.7;
  margin-bottom: 16px;
}
.fade-title {
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1.15;
  margin: 0 0 16px;
}
.fade-sub {
  font-size: 1rem;
  opacity: 0.8;
  max-width: 380px;
  margin: 0;
  line-height: 1.6;
}
.fade-slide {
  height: 340px;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 0 56px;
  border-radius: 12px;
  color: #fff;
}
.fade-tag {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.7;
  margin-bottom: 16px;
}
.fade-title {
  font-size: 2.25rem;
  font-weight: 800;
  line-height: 1.15;
  margin: 0 0 16px;
}
.fade-sub {
  font-size: 1rem;
  opacity: 0.8;
  max-width: 380px;
  margin: 0;
  line-height: 1.6;
}
import gsap from 'gsap'
import { Slider } from '@andresclua/sliderkit'
import { arrows, pagination, hooks } from '@andresclua/sliderkit-plugins'
import { fadeEffect } from '@andresclua/sliderkit-effects'

new Slider('#my-slider', {
  items: 1,
  loop: true,
  speed: 600,
  plugins: [
    arrows(),
    pagination({ type: 'dots', clickable: true }),
    fadeEffect({ duration: 600, easing: 'ease-in-out' }),
    hooks({
      onInit({ slides }) {
        gsap.from(slides[0].querySelectorAll('.fade-tag, .fade-title, .fade-sub'), {
          x: 40, autoAlpha: 0, stagger: 0.09, duration: 0.5, ease: 'power3.out', clearProps: 'all',
        })
      },
      beforeChange({ incoming, direction }) {
        const sign = direction === 'forward' ? 1 : -1
        gsap.from(incoming.querySelectorAll('.fade-tag, .fade-title, .fade-sub'), {
          x: sign * 50,
          autoAlpha: 0,
          stagger: 0.09,
          duration: 0.5,
          ease: 'power3.out',
          delay: 0.2,
          clearProps: 'all',
        })
      },
    }),
  ],
})

Hooks — multi-item with gutter + GSAP

slideBy:'page' swaps all 3 cards at once. hooks + GSAP animate each card in from the navigation direction, with a vertical stagger on the inner elements.

01

Instant setup

One import, one constructor call. No config files, no build plugins required.

Learn more →
02

Plugin system

Tree-shakeable plugins. Import only what you use — zero dead code in your bundle.

Explore plugins →
03

TypeScript

Written in TypeScript. Full autocomplete for every option, method, and event.

View types →
04

Performance

Under 10 kb gzipped. No runtime dependencies, hardware-accelerated CSS.

Benchmark →
05

Accessible

WAI-ARIA carousel pattern built in. Keyboard navigation and screen reader support.

A11y docs →
06

Effects

Fade, flip, and clip-path transitions from a separate effects package.

See effects →
View code
<div id="my-slider">
  <div class="hc">
    <div class="hc__accent" style="background:linear-gradient(135deg,#6C2BD9,#3b82f6)"></div>
    <div class="hc__body">
      <span class="hc-num">01</span>
      <h3 class="hc-title">Instant setup</h3>
      <p class="hc-text">One import, one constructor call. No config files, no build plugins required.</p>
      <a class="hc-cta" href="#">Learn more →</a>
    </div>
  </div>
  <div class="hc">
    <div class="hc__accent" style="background:linear-gradient(135deg,#0ea5e9,#06b6d4)"></div>
    <div class="hc__body">
      <span class="hc-num">02</span>
      <h3 class="hc-title">Plugin system</h3>
      <p class="hc-text">Tree-shakeable plugins. Import only what you use — zero dead code in your bundle.</p>
      <a class="hc-cta" href="#">Explore plugins →</a>
    </div>
  </div>
  <div class="hc">
    <div class="hc__accent" style="background:linear-gradient(135deg,#10b981,#84cc16)"></div>
    <div class="hc__body">
      <span class="hc-num">03</span>
      <h3 class="hc-title">TypeScript</h3>
      <p class="hc-text">Written in TypeScript. Full autocomplete for every option, method, and event.</p>
      <a class="hc-cta" href="#">View types →</a>
    </div>
  </div>
  <div class="hc">
    <div class="hc__accent" style="background:linear-gradient(135deg,#f59e0b,#ef4444)"></div>
    <div class="hc__body">
      <span class="hc-num">04</span>
      <h3 class="hc-title">Performance</h3>
      <p class="hc-text">Under 10 kb gzipped. No runtime dependencies, hardware-accelerated CSS.</p>
      <a class="hc-cta" href="#">Benchmark →</a>
    </div>
  </div>
  <div class="hc">
    <div class="hc__accent" style="background:linear-gradient(135deg,#ec4899,#8b5cf6)"></div>
    <div class="hc__body">
      <span class="hc-num">05</span>
      <h3 class="hc-title">Accessible</h3>
      <p class="hc-text">WAI-ARIA carousel pattern built in. Keyboard navigation and screen reader support.</p>
      <a class="hc-cta" href="#">A11y docs →</a>
    </div>
  </div>
  <div class="hc">
    <div class="hc__accent" style="background:linear-gradient(135deg,#14b8a6,#6366f1)"></div>
    <div class="hc__body">
      <span class="hc-num">06</span>
      <h3 class="hc-title">Effects</h3>
      <p class="hc-text">Fade, flip, and clip-path transitions from a separate effects package.</p>
      <a class="hc-cta" href="#">See effects →</a>
    </div>
  </div>
</div>
.hc {
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  overflow: hidden;
}
.hc__accent { height: 6px; }
.hc__body {
  padding: 24px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.hc-num {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: #9ca3af;
  text-transform: uppercase;
}
.hc-title {
  font-size: 1.15rem;
  font-weight: 700;
  color: #111827;
  margin: 0;
  line-height: 1.3;
}
.hc-text {
  font-size: 0.875rem;
  color: #6b7280;
  margin: 0;
  line-height: 1.6;
}
.hc-cta {
  font-size: 0.8rem;
  font-weight: 600;
  color: #6C2BD9;
  text-decoration: none;
  margin-top: 4px;
}
.hc-num {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  color: #9ca3af;
  text-transform: uppercase;
}

.hc-title {
  font-size: 1.15rem;
  font-weight: 700;
  color: #111827;
  margin: 0;
  line-height: 1.3;
}

.hc-text {
  font-size: 0.875rem;
  color: #6b7280;
  margin: 0;
  line-height: 1.6;
}

.hc-cta {
  font-size: 0.8rem;
  font-weight: 600;
  color: #6C2BD9;
  text-decoration: none;
  margin-top: 4px;
}

.hc {
  background: #fff;
  border: 1px solid #e5e7eb;
  border-radius: 12px;
  overflow: hidden;

  &__accent {
    height: 6px;
  }

  &__body {
    padding: 24px;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
}
import gsap from 'gsap'
import { Slider } from '@andresclua/sliderkit'
import { arrows, hooks } from '@andresclua/sliderkit-plugins'

const INNER = '.hc-num, .hc-title, .hc-text, .hc-cta'

function animateIn(cards, sign = 1) {
  cards.forEach((card, i) => {
    const delay = i * 0.1
    gsap.fromTo(card,
      { x: sign * 48, autoAlpha: 0 },
      { x: 0, autoAlpha: 1, duration: 0.45, ease: 'power3.out', delay, clearProps: 'all' }
    )
    gsap.from(card.querySelectorAll(INNER), {
      y: sign * 18, autoAlpha: 0, stagger: 0.07,
      duration: 0.38, ease: 'power2.out', delay: delay + 0.1, clearProps: 'all',
    })
  })
}

new Slider('#my-slider', {
  items: 3,
  gutter: 24,
  slideBy: 'page',
  loop: true,
  speed: 400,
  plugins: [
    arrows(),
    hooks({
      onInit({ slides }) {
        const active = slides.filter(s =>
          s.classList.contains('sliderkit__item--active') &&
          !s.classList.contains('sliderkit__item--clone')
        )
        animateIn(active)
      },
      afterChange({ slide, direction }) {
        const sign = direction === 'forward' ? 1 : -1
        const active = [...slide.parentElement.querySelectorAll(
          '.sliderkit__item--active:not(.sliderkit__item--clone)'
        )]
        animateIn(active, sign)
      },
    }),
  ],
})

Hooks — focus center

3 slides visible. The center one is always at full opacity and scale; the sides are dimmed. slideBy:1 moves one at a time so the focus always lands on the center.

1
2
3
4
5
6
View code
<div id="my-slider">
  <div class="focus-slide s-purple">1</div>
  <div class="focus-slide s-blue">2</div>
  <div class="focus-slide s-green">3</div>
  <div class="focus-slide s-amber">4</div>
  <div class="focus-slide s-red">5</div>
  <div class="focus-slide s-pink">6</div>
</div>
.focus-slide {
  height: 260px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3.5rem;
  font-weight: 800;
  color: #fff;
  border-radius: 12px;
  opacity: 0.5;
  transform: scale(0.92);
}
.focus-slide {
  height: 260px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 3.5rem;
  font-weight: 800;
  color: #fff;
  border-radius: 12px;
  opacity: 0.5;
  transform: scale(0.92);
}
import gsap from 'gsap'
import { Slider } from '@andresclua/sliderkit'
import { arrows, hooks } from '@andresclua/sliderkit-plugins'

const ORIG = '.sliderkit__item:not(.sliderkit__item--clone)'

function applyFocus(track) {
  track.querySelectorAll(ORIG).forEach(s =>
    gsap.to(s, { opacity: 0.5, scale: 0.92, duration: 0.35, ease: 'power2.out', overwrite: true })
  )
  track.querySelectorAll('.sliderkit__item--clone').forEach(s =>
    gsap.set(s, { opacity: 0.5, scale: 0.92 })
  )
  const active = [...track.querySelectorAll('.sliderkit__item--active')]
  if (active[1])
    gsap.to(active[1], { opacity: 1, scale: 1, duration: 0.35, ease: 'power2.out', overwrite: true })
}

new Slider('#my-slider', {
  items: 3,
  gutter: 16,
  slideBy: 1,
  loop: true,
  speed: 350,
  plugins: [
    arrows(),
    hooks({
      onInit({ slider }) { applyFocus(slider.container) },
      afterChange({ slide }) {
        requestAnimationFrame(() => applyFocus(slide.parentElement))
      },
    }),
  ],
})