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-particle-image-viewer

v0.0.4

Published

WebGL particle image viewer for Astro with stunning animation effects using Three.js

Readme

Astro Particle Image Viewer

npm version

An Astro component that displays images with a stunning WebGL particle animation effect using Three.js. When users click on a thumbnail, the image explodes into particles that animate and reassemble into a fullscreen view.

Features

  • WebGL-powered particle animations using Three.js
  • Smooth transitions between images
  • Keyboard navigation (Arrow keys, Escape)
  • Touch-friendly navigation buttons
  • Swipe gestures for mobile navigation
  • Responsive design with mobile optimizations
  • Customizable animation parameters
  • Sliding window pagination with WCAG-compliant 44px touch targets
  • Accessible with ARIA labels and focus management
  • Focus-visible styles for keyboard users
  • Respects accessibility preferences - uses SimpleLightbox fallback for reduced motion, low-performance devices, and no WebGL support
  • Dynamic imports - only loads Three.js when needed (code-splitting)
  • CSS custom properties for easy theming
  • View Transitions compatible - works seamlessly with Astro View Transitions

Installation

npm install @ivanalbizu/astro-particle-image-viewer

Usage

---
import { ParticleImageViewer } from '@ivanalbizu/astro-particle-image-viewer';
---

<ParticleImageViewer
  config={{
    openDuration: 2000,
    closeDuration: 1200,
    maxWidth: 1440,
  }}
>
  <img src="/image-1.jpg" width="300" alt="Description 1" />
  <img src="/image-2.jpg" width="300" alt="Description 2" />
  <img src="/image-3.jpg" width="300" alt="Description 3" />
</ParticleImageViewer>

The component uses a slot, so you can add any <img> elements as children. Each image will automatically be wrapped in an accessible button.

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | title | string | - | Optional title above the gallery | | class | string | - | Additional CSS class for the wrapper | | config | ParticleViewerConfig | {} | Animation configuration options |

ParticleViewerConfig

interface ParticleViewerConfig {
  segments?: number;       // Particle grid resolution (default: 180, mobile: 80)
  padding?: number;        // Image padding multiplier (default: 1.1)
  openDuration?: number;   // Open animation duration in ms (default: 2000)
  closeDuration?: number;  // Close animation duration in ms (default: 1200)
  crossfadeStart?: number; // When to start crossfade 0-1 (default: 0.2)
  srcAttribute?: string;   // Attribute for image source (default: 'src')
  maxWidth?: number;       // Maximum image width in pixels (default: 0 = no limit)
}

Lazy Loading Support

Use srcAttribute to work with lazy loading libraries that use custom attributes like data-src:

<ParticleImageViewer
  config={{
    srcAttribute: 'data-src'
  }}
>
  <img data-src="/image-1.jpg" src="/placeholder.jpg" alt="Description" />
</ParticleImageViewer>

Styling

The component uses CSS custom properties that you can override:

.particle-image-viewer-wrapper {
  /* Colors */
  --piv-title-color: #333;
  --piv-text-color: white;
  --piv-text-color-inverse: #111;
  --piv-shadow: rgba(0, 0, 0, 0.1);
  --piv-overlay-bg: rgba(0, 0, 0, 0.25);
  --piv-caption-bg: rgba(0, 0, 0, 0.5);
  --piv-button-bg: rgba(0, 0, 0, 0.6);
  --piv-button-bg-hover: rgba(0, 0, 0, 0.8);
  --piv-button-border: rgba(255, 255, 255, 0.5);
  --piv-button-border-hover: rgba(255, 255, 255, 0.8);
  --piv-button-shadow: rgba(0, 0, 0, 0.3);
  --piv-active-bg: rgba(255, 255, 255, 0.9);
  --piv-active-glow: rgba(255, 255, 255, 0.4);
  --piv-focus-ring: rgba(255, 255, 255, 0.9);

  /* Spacing */
  --piv-spacing-sm: 8px;
  --piv-spacing-md: 20px;
  --piv-spacing-lg: 30px;
  --piv-caption-bottom: 90px;
  --piv-pagination-bottom: 30px;

  /* Sizes */
  --piv-button-size: 50px;
  --piv-dot-size: 44px;
  --piv-border-radius: 8px;
  --piv-border-radius-round: 50%;
  --piv-border-radius-pill: 20px;
  --piv-border-width: 2px;

  /* Typography */
  --piv-font-size-close: 32px;
  --piv-font-size-nav: 24px;
  --piv-font-size-caption: 18px;
  --piv-font-size-dot: 14px;

  /* Blur */
  --piv-blur-overlay: 5px;
  --piv-blur-button: 4px;
  --piv-blur-caption: 10px;

  /* Transitions */
  --piv-transition-fast: 0.2s ease;
  --piv-transition-normal: 0.3s ease;
  --piv-transition-slow: 0.5s ease;

  /* Z-index */
  --piv-z-container: 1000;
  --piv-z-controls: 10;
  --piv-z-pagination: 20;
}

Keyboard Navigation

When the viewer is open:

  • Escape: Close the viewer
  • Arrow Left: Previous image
  • Arrow Right: Next image
  • Tab: Navigate between controls (with focus trap)

Touch Navigation

  • Swipe left: Next image
  • Swipe right: Previous image
  • Tap outside image: Close viewer

Accessibility & Performance

The component automatically provides a simplified lightbox experience when:

  • User has prefers-reduced-motion: reduce enabled
  • Browser has Data Saver mode enabled
  • Connection is 2G or slow-2g
  • Device has both low memory (<4GB) AND few CPU cores (≤2)
  • WebGL is not supported

When any of these conditions are detected, SimpleLightbox is used instead of ParticleViewer. This provides the same navigation functionality without WebGL animations, respecting user preferences and device capabilities.

Privacy browser compatible: Works with Brave and other privacy-focused browsers. Hardware detection requires both low memory AND low cores to avoid false positives from browsers that falsify individual values.

Manual fallback detection

import { shouldUseFallback, getFallbackReason } from '@ivanalbizu/astro-particle-image-viewer';

if (shouldUseFallback()) {
  console.log('Using SimpleLightbox:', getFallbackReason());
  // 'prefers-reduced-motion' | 'low-performance-device' | 'webgl-not-supported'
}

View Transitions Support

The component is fully compatible with Astro View Transitions. It automatically:

  • Cleans up resources before page navigation (astro:before-swap)
  • Reinitializes after navigation completes (astro:page-load)
  • Prevents memory leaks by properly disposing WebGL contexts and event listeners
  • Works without View Transitions - the component functions normally on pages without View Transitions enabled

No additional configuration is needed. The component handles both scenarios transparently.

Advanced Usage

For more control, you can use the classes directly:

import { ParticleViewer, SimpleLightbox } from '@ivanalbizu/astro-particle-image-viewer';

// WebGL version with particle animations
const viewer = new ParticleViewer(
  '.my-container',
  '.my-image-buttons',
  { openDuration: 3000, srcAttribute: 'data-src' }
);

// Simple version without animations (for reduced motion)
const lightbox = new SimpleLightbox(
  '.my-container',
  '.my-image-buttons',
  'data-src' // optional: custom source attribute
);

// Clean up when done
viewer.destroy();

Development

# Install dependencies
npm install

# Start development server
npm run dev

# Build site
npm run build

# Publish to npm
npm publish --access public

License

MIT