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

@ivanalbizu/astro-swiperjs-webgl

v0.0.1

Published

Astro component for WebGL image transitions using CurtainsJS and Swiper.js

Readme

@ivanalbizu/astro-swiperjs-webgl

Astro component for WebGL image transitions using displacement maps, powered by CurtainsJS and Swiper.js.

Features

  • WebGL transitions between images: displacement maps or particle scatter effect
  • Thumbnail navigation carousel (Swiper.js)
  • Per-slide title and content overlays using named slots
  • Automatic performance detection with CSS fallback for low-end devices
  • Respects prefers-reduced-motion user preference
  • Compatible with Astro View Transitions (<ClientRouter>)
  • Multiple independent instances on the same page
  • Keyboard navigation scoped per instance
  • Full accessibility: ARIA roles, labels, and focus management
  • Customizable via props and CSS custom properties
  • i18n support via labels prop
  • Active thumbnail indicator
  • TypeScript support

Installation

npm install @ivanalbizu/astro-swiperjs-webgl

Peer dependencies (curtainsjs and swiper) are installed automatically with npm 7+. With pnpm, install them explicitly:

pnpm add @ivanalbizu/astro-swiperjs-webgl curtainsjs swiper

Usage

Basic

---
import { WebglSlider, WebglSliderImg } from '@ivanalbizu/astro-swiperjs-webgl';
---

<div style="width: 100%; height: 100vh;">
  <WebglSlider>
    <WebglSliderImg src="/img-01.jpg" alt="First image">
      <h2 slot="title">First Slide</h2>
      <p slot="content">Description for the first slide</p>
    </WebglSliderImg>
    <WebglSliderImg src="/img-02.jpg" alt="Second image">
      <h2 slot="title">Second Slide</h2>
      <p slot="content">Description for the second slide</p>
    </WebglSliderImg>
    <WebglSliderImg src="/img-03.jpg" alt="Third image" />
  </WebglSlider>
</div>

The slider fills its parent container. Set width and height on the wrapper element.

With all props

<WebglSlider
  transition="displacement"
  displacement="/my-displacement-map.jpg"
  overlayPosition="bottom-left"
  overlayTheme="dark"
  transitionDamping={0.94}
  pixelRatio={2}
  navigation={true}
  loop={true}
  slidesPerView="auto"
  spaceBetween={20}
  ariaLabel="Product gallery"
  labels={{
    prevSlide: 'Previous',
    nextSlide: 'Next',
    slideLabel: '{current} of {total}',
  }}
>
  <WebglSliderImg src="/photo.jpg" alt="Photo description">
    <h2 slot="title">Title with <em>HTML</em></h2>
    <p slot="content">Rich content with <a href="#">links</a></p>
  </WebglSliderImg>
</WebglSlider>

Custom navigation buttons

<button id="prev-btn" aria-label="Previous slide">Prev</button>
<button id="next-btn" aria-label="Next slide">Next</button>

<WebglSlider navigation={false} prevEl="#prev-btn" nextEl="#next-btn">
  <!-- slides -->
</WebglSlider>

Components

<WebglSlider>

Main slider wrapper. Accepts <WebglSliderImg> children.

<WebglSliderImg>

Individual slide item with an image and optional named slots.

| Prop | Type | Required | Description | | :---- | :------- | :------- | :------------------ | | src | string | Yes | Image source URL | | alt | string | No | Image alt text ("") |

Named slots:

  • title — Slide title overlay (any HTML)
  • content — Slide content overlay (any HTML)

Props

All props for <WebglSlider>:

| Prop | Type | Default | Description | | :----------------- | :-------------------- | :------------------- | :--------------------------------------------- | | transition | 'displacement' \| 'particles' | 'displacement' | Transition type between slides | | displacement | string | '/displacement4.jpg' | Path to the displacement map image (only for displacement transition) | | overlayPosition | OverlayPosition | 'bottom-left' | Position of the title/content overlay (9 positions) | | overlayTheme | 'dark' \| 'light' | 'dark' | Overlay background theme for contrast | | transitionDamping| number | 0.96 | Damping factor for transition speed (0-1). Lower = faster | | pixelRatio | number | 1.5 | WebGL pixel ratio (capped at devicePixelRatio) | | navigation | boolean | true | Show prev/next navigation buttons | | prevEl | string | — | CSS selector for external prev button | | nextEl | string | — | CSS selector for external next button | | loop | boolean | true | Enable infinite loop (requires more than 3 slides) | | slidesPerView | number \| 'auto' | 'auto' | Number of thumbnails visible | | spaceBetween | number | 16 | Space between thumbnails (px) | | ariaLabel | string | 'Image carousel' | Accessible label for the carousel region | | labels | SliderLabels | See below | Customizable UI strings for i18n |

Overlay Position

The overlayPosition prop accepts 9 positions arranged in a 3×3 grid:

| | Left | Center | Right | |:--|:--|:--|:--| | Top | top-left | top | top-right | | Middle | left | center | right | | Bottom | bottom-left | bottom | bottom-right |

Bottom positions automatically account for the thumbnail bar height (--wgl-swiper-height).

The overlayTheme prop adds a semi-transparent background for contrast:

  • 'dark' — dark background (rgba(0,0,0,0.6)), white text
  • 'light' — light background (rgba(255,255,255,0.8)), dark text
<WebglSlider overlayPosition="top-right" overlayTheme="light">

Labels

The labels prop allows customizing all UI strings, useful for i18n or personalization. All fields are optional — defaults are in English.

| Key | Default | Description | | :----------- | :----------------------------- | :----------------------------------------------- | | prevSlide | 'Previous slide' | Aria label for prev button / screen reader | | nextSlide | 'Next slide' | Aria label for next button / screen reader | | firstSlide | 'This is the first slide' | Screen reader announcement on first slide | | lastSlide | 'This is the last slide' | Screen reader announcement on last slide | | slideLabel | '{current} / {total}' | Thumbnail aria-label template |

The slideLabel template supports {current} and {total} placeholders.

<WebglSlider
  labels={{
    prevSlide: 'Diapositiva anterior',
    nextSlide: 'Siguiente diapositiva',
    firstSlide: 'Primera diapositiva',
    lastSlide: 'Última diapositiva',
    slideLabel: '{current} de {total}',
  }}
>

CSS Custom Properties

Override these on .webgl-slider or any parent to customize the appearance:

| Property | Default | Description | | :---------------------------- | :----------------------------- | :---------------------------------- | | --wgl-z-index | 0 | Slider stacking context z-index | | --wgl-overlay-color | #fff | Overlay text color | | --wgl-overlay-shadow | 0 2px 8px rgba(0, 0, 0, 0.6) | Overlay text shadow | | --wgl-overlay-padding | 24px | Overlay distance from edges | | --wgl-swiper-height | 110px | Space reserved for thumbnails (bottom positions) | | --wgl-transition-duration | 0.4s | Overlay fade in/out duration | | --wgl-transition-offset | 12px | Overlay slide-up offset | | --wgl-thumb-border-color | #fff | Thumbnail border color | | --wgl-thumb-border-radius | 3px | Thumbnail border radius | | --wgl-focus-outline-color | #fff | Focus-visible outline color |

.webgl-slider {
  --wgl-overlay-color: #1a1a1a;
  --wgl-thumb-border-color: transparent;
  --wgl-thumb-border-radius: 8px;
}

Displacement Maps

The displacement prop accepts a path to a grayscale image that controls the transition distortion pattern. Different maps produce different visual effects (waves, ripples, geometric shapes, etc.).

Place your displacement map in the public/ directory and reference it by path:

<WebglSlider displacement="/my-custom-displacement.jpg">

The image should be grayscale. Lighter areas produce more distortion during transitions.

Astro Integration

View Transitions

The component is fully compatible with Astro's <ClientRouter>. It automatically initializes on astro:page-load and cleans up on astro:before-swap, preventing memory leaks or duplicate instances during client-side navigation.

It also works on pages without View Transitions — initialization falls back to the load event.

Performance Detection

On low-end devices or when the user prefers reduced motion, WebGL is skipped entirely. The component detects:

  • prefers-reduced-motion: reduce media query
  • Data saver mode (navigator.connection.saveData)
  • Low device memory (navigator.deviceMemory < 4)
  • Low CPU cores (navigator.hardwareConcurrency <= 2)

When any condition is met, images transition with a CSS opacity crossfade instead of WebGL shaders. The thumbnail carousel and overlay content continue to work normally.

Accessibility

  • <section> with aria-roledescription="carousel" as semantic landmark
  • aria-live="polite" on the overlay for screen reader announcements
  • Keyboard navigation: Arrow Left/Right from focused thumbnails or navigation buttons
  • focus-visible outlines on thumbnails and navigation buttons
  • Each thumbnail has aria-label with position (customizable via labels.slideLabel)

Multiple Instances

Each slider is fully independent. Place as many <WebglSlider> components as needed — they do not interfere with each other's state, keyboard events, or WebGL contexts.

License

MIT - Ivan Albizu