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

awesome-cube-carousel

v0.1.1

Published

A stunning 3D cube carousel React component with CSS3 perspective transitions, per-slide accent colours, autoplay, and a sci-fi-inspired HUD chrome.

Readme

awesome-cube-carousel

A stunning 3D cube carousel React component with smooth CSS3 perspective transitions, per-slide accent colours, ambient glow effects, a scanline overlay, a starfield background, and a sci-fi HUD chrome — all with zero external dependencies beyond React itself.


Features

  • True 3D CSS cube — 6-face box with preserve-3d and perspective, no canvas or WebGL required
  • 4-direction rotation sequence — Right→Left, Left→Right, Top→Bottom, Bottom→Top cycling automatically
  • Per-slide accent colour — every slide has its own hex accent that drives glow, gradients, edge pulses, and the progress bar
  • Background image support — optional image blended in luminosity mode over the grid background
  • Autoplay with progress bar — configurable interval with a smooth bottom progress bar
  • Direction indicator HUD — animated arrow, label, and sequence step dots
  • Slide info strip — zero-padded counter and title below the cube
  • Navigation dots — pill-shaped dots expanding to show the active slide
  • Ambient starfield — 90 subtly blinking stars behind the cube
  • ESM + CJS dual build — works in Vite, CRA, Next.js, and any modern bundler
  • Full TypeScript types included

Installation

npm install awesome-cube-carousel
# or
yarn add awesome-cube-carousel
# or
pnpm add awesome-cube-carousel

Peer dependencies — React 17+ and ReactDOM 17+ must already be installed in your project.


Importing Styles

The component uses CSS animations (scanline, fadeUp, edgePulse, dotBlink, progressBar) and CSS custom properties (--font-display, --font-body) defined in a separate stylesheet. You must import it once in your app:

import 'awesome-cube-carousel/style.css';

The stylesheet also expects two Google Fonts to be loaded for the full visual effect. Add these to your index.html <head>:

<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
  href="https://fonts.googleapis.com/css2?family=Bebas+Neue&family=Outfit:wght@300;400;600&display=swap"
  rel="stylesheet"
/>

If you skip the fonts the component will still render correctly — it will fall back to sans-serif.


🚀 Quick Start

import { AwesomeCubeCarousel } from 'awesome-cube-carousel';
import 'awesome-cube-carousel/style.css';

const slides = [
  {
    title: 'AURORA',
    subtitle: 'Northern Lights',
    description: 'A breathtaking natural light display in the sky.',
    tag: 'Nature',
    accent: '#00f5c8',
  },
  {
    title: 'NEBULA',
    subtitle: 'Deep Space',
    description: 'Clouds of gas and dust where new stars are born.',
    tag: 'Space',
    accent: '#a855f7',
  },
  {
    title: 'HORIZON',
    subtitle: 'Ocean View',
    description: 'Where the sea meets the endless sky at dawn.',
    tag: 'Landscape',
    accent: '#f97316',
  },
];

export default function App() {
  return <AwesomeCubeCarousel slides={slides} />;
}

📐 Props

| Prop | Type | Default | Description | |---|---|---|---| | slides | CubeSlide[] | required | Array of slide objects (see below). Minimum 2 slides recommended. | | size | number | 380 | Width and height of each cube face in pixels. | | intervalSeconds | number | 3.5 | Seconds each slide is displayed before advancing. | | autoPlay | boolean | true | Whether the carousel advances automatically. | | onSlideChange | (index: number) => void | undefined | Callback fired after each slide transition completes, with the new 0-based index. |

CubeSlide object

| Field | Type | Required | Description | |---|---|---|---| | title | string | ✅ | Main heading displayed on the face. | | subtitle | string | ➖ | Smaller coloured line below the title. | | description | string | ➖ | Body paragraph visible only on the active face. | | tag | string | ➖ | Category badge with a glowing dot shown at the top-left. | | accent | string | ✅ | Hex colour (e.g. '#00f5c8') used for all glows, gradients, and the progress bar. | | image | string | ➖ | URL of a background image blended over the grid in luminosity mode. | | onClick | () => void | ➖ | Called when the EXPLORE CTA button on the active face is clicked. |


Examples

Example 1 — Minimal (no images, no callbacks)

The simplest possible usage with three slides and default autoplay.

import { AwesomeCubeCarousel } from 'awesome-cube-carousel';
import 'awesome-cube-carousel/style.css';

const slides = [
  {
    title: 'VELOCITY',
    subtitle: 'Speed redefined',
    accent: '#f97316',
  },
  {
    title: 'PRECISION',
    subtitle: 'Every detail matters',
    accent: '#3b82f6',
  },
  {
    title: 'VISION',
    subtitle: 'See what is possible',
    accent: '#a855f7',
  },
];

export default function MinimalDemo() {
  return <AwesomeCubeCarousel slides={slides} />;
}

Example 2 — Full featured with images and onSlideChange

Uses all available fields including background images, tags, descriptions, click handlers, a custom cube size, and a slower rotation interval.

import { useState } from 'react';
import { AwesomeCubeCarousel } from 'awesome-cube-carousel';
import 'awesome-cube-carousel/style.css';

const slides = [
  {
    title: 'ARCTIC',
    subtitle: 'Ice & Silence',
    description:
      'Vast frozen tundra stretching to the horizon, lit only by the polar glow.',
    tag: 'Explore',
    accent: '#38bdf8',
    image: 'https://images.unsplash.com/photo-1517411032315-54ef2cb783bb?w=800',
    onClick: () => window.open('https://example.com/arctic', '_blank'),
  },
  {
    title: 'DESERT',
    subtitle: 'Heat & Dunes',
    description:
      'Golden sand sculpted by the wind into endless rolling waves.',
    tag: 'Journey',
    accent: '#f59e0b',
    image: 'https://images.unsplash.com/photo-1509316785289-025f5b846b35?w=800',
    onClick: () => window.open('https://example.com/desert', '_blank'),
  },
  {
    title: 'JUNGLE',
    subtitle: 'Life & Canopy',
    description:
      'A cathedral of ancient trees alive with the sound of unseen creatures.',
    tag: 'Wildlife',
    accent: '#22c55e',
    image: 'https://images.unsplash.com/photo-1448375240586-882707db888b?w=800',
    onClick: () => window.open('https://example.com/jungle', '_blank'),
  },
  {
    title: 'ABYSS',
    subtitle: 'Depth & Mystery',
    description:
      'The deep ocean floor — one of Earth\'s last unexplored frontiers.',
    tag: 'Discovery',
    accent: '#6366f1',
    image: 'https://images.unsplash.com/photo-1518020382113-a7e8fc38eac9?w=800',
    onClick: () => window.open('https://example.com/abyss', '_blank'),
  },
];

export default function FullFeaturedDemo() {
  const [current, setCurrent] = useState(0);

  return (
    <div>
      <p style={{ color: '#fff', textAlign: 'center', fontFamily: 'sans-serif' }}>
        Viewing slide {current + 1} of {slides.length}: {slides[current].title}
      </p>
      <AwesomeCubeCarousel
        slides={slides}
        size={440}
        intervalSeconds={5}
        autoPlay={true}
        onSlideChange={(index) => setCurrent(index)}
      />
    </div>
  );
}

Example 3 — Manual control (autoPlay={false})

Disables autoplay and drives the carousel using external buttons. The onSlideChange callback is used to keep a local counter in sync, while the carousel advances internally when you programmatically manage the slides array order — or simply use it as a display-only component with navigation handled by your own UI.

In this pattern we pass autoPlay={false} and provide our own Next / Prev buttons that reorder the slides array, letting us own the navigation entirely.

import { useState } from 'react';
import { AwesomeCubeCarousel } from 'awesome-cube-carousel';
import 'awesome-cube-carousel/style.css';

const ALL_SLIDES = [
  {
    title: 'MERCURY',
    subtitle: 'Closest to the Sun',
    description: 'The smallest planet, scarred by craters and extreme temperatures.',
    tag: 'Planet',
    accent: '#e2e8f0',
  },
  {
    title: 'VENUS',
    subtitle: 'The Veiled World',
    description: 'A toxic atmosphere that traps heat, making it the hottest planet.',
    tag: 'Planet',
    accent: '#fde68a',
  },
  {
    title: 'MARS',
    subtitle: 'The Red Planet',
    description: 'Iron-oxide dust coats the surface of this cold, barren world.',
    tag: 'Planet',
    accent: '#f87171',
  },
  {
    title: 'JUPITER',
    subtitle: 'King of Planets',
    description: 'A gas giant large enough to swallow all other planets combined.',
    tag: 'Planet',
    accent: '#fb923c',
  },
];

export default function ManualControlDemo() {
  const [startIdx, setStartIdx] = useState(0);

  // Rotate the array so the desired slide is first — the carousel
  // always treats index 0 as the initial active slide on mount.
  // For a true controlled carousel you would remount with a key prop.
  const [activeLabel, setActiveLabel] = useState(ALL_SLIDES[0].title);

  const next = () =>
    setStartIdx((i) => (i + 1) % ALL_SLIDES.length);

  const prev = () =>
    setStartIdx((i) => (i - 1 + ALL_SLIDES.length) % ALL_SLIDES.length);

  // Reorder slides so chosen index is first
  const slides = [
    ...ALL_SLIDES.slice(startIdx),
    ...ALL_SLIDES.slice(0, startIdx),
  ];

  const btnStyle = {
    padding: '10px 28px',
    background: 'transparent',
    border: '1px solid rgba(255,255,255,0.25)',
    color: '#fff',
    borderRadius: '4px',
    cursor: 'pointer',
    fontFamily: 'sans-serif',
    fontSize: '14px',
    letterSpacing: '0.1em',
  };

  return (
    <div style={{ position: 'relative' }}>
      <AwesomeCubeCarousel
        key={startIdx}
        slides={slides}
        autoPlay={false}
        onSlideChange={(i) => setActiveLabel(slides[i].title)}
      />

      {/* Navigation overlay */}
      <div
        style={{
          position: 'absolute',
          bottom: 60,
          left: '50%',
          transform: 'translateX(-50%)',
          display: 'flex',
          gap: '16px',
          zIndex: 50,
        }}
      >
        <button style={btnStyle} onClick={prev}>← PREV</button>
        <button style={btnStyle} onClick={next}>NEXT →</button>
      </div>
    </div>
  );
}

📄 License

MIT