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

@brustack/alpine-theme-transitions

v1.0.3

Published

Alpine.js plugin for animated theme transitions using the View Transitions API

Readme

alpine-theme-transitions

made by brustack npm version license

Alpine.js plugin for animated theme transitions using the View Transitions API.

  • ✅ Multiple effects to choose from
  • ✅ Zero flash of the wrong theme on load
  • ✅ Syncs automatically with OS prefers-color-scheme
  • ✅ Custom themes beyond light/dark
  • ✅ Origin auto-derived from the click event, bind toggleTheme straight to @click
  • ✅ Works with zero build step, drop in via CDN, or install via npm
  • ✅ No Context, no Provider, no store, just x-data

Install

npm install @brustack/alpine-theme-transitions
# or
pnpm add @brustack/alpine-theme-transitions
# or
yarn add @brustack/alpine-theme-transitions

Not using a bundler? No install needed, see the CDN usage below.

Prefer to see it running first?

git clone https://github.com/brustack/theme-transitions.git
cd theme-transitions
npm run install:alpine-demo
npm run dev:alpine-demo

Usage

CDN (zero-build)

<head>
  <script src="https://cdn.jsdelivr.net/npm/@brustack/theme-transitions-core/dist/theme-init.js"></script>
  <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@brustack/theme-transitions-core/style.css">
</head>
<body>
  <div x-data="themeTransition()">
    <button @click="toggleTheme" x-text="theme"></button>
  </div>

  <script defer src="https://cdn.jsdelivr.net/npm/@brustack/alpine-theme-transitions/dist/alpine-theme-transitions.iife.js"></script>
  <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3/dist/cdn.min.js"></script>
</body>

Script order matters: the plugin's <script> tag comes before Alpine core's own <script> tag, both defer, the plugin attaches its alpine:init listener before Alpine's CDN bundle fires that event and starts itself. No manual Alpine.plugin(...) call needed for CDN usage.

npm / bundler

import Alpine from 'alpinejs';
import themeTransition from '@brustack/alpine-theme-transitions';
import '@brustack/theme-transitions-core/style.css';

Alpine.plugin(themeTransition);
Alpine.start();
<div x-data="themeTransition()">
  <button @click="toggleTheme" x-text="theme"></button>
</div>

Binding toggleTheme directly to @click works because it accepts the native MouseEvent and derives the spread effect's origin from the click position automatically. Options are passed through the markup (x-data="themeTransition({...})"), not through JS setup, see Configuration below.

Styling

themeTransition applies the current theme's name (dark, light, or a custom name, see Custom themes below) as a class on <html>. Style your palette off that class with any approach.

CSS variables

:root {
  --bg: #ffffff;
  --text: #111111;
}

html.dark {
  --bg: #0b0b10;
  --text: #f4f2ed;
}

html.sepia {
  --bg: #f4ecd8;
  --text: #4b3621;
}

body {
  background: var(--bg);
  color: var(--text);
}

Tailwind

Set darkMode: 'class' in your Tailwind config (see Install above), then map your color tokens to the CSS variables above:

// tailwind.config.js
module.exports = {
  darkMode: 'class',
  theme: {
    extend: {
      colors: {
        bg: 'var(--bg)',
        text: 'var(--text)',
      },
    },
  },
};

Configuration (optional)

| Variant | duration | easing | |---|:---:|:---:| | spread | '1s' | ❌ | | fade (default) | '400ms' | 'ease' | | none | ❌ | ❌ |

<div x-data="themeTransition({ variant: 'spread', duration: '1s' })">

The first component to initialize on the page sets the shared default. Pass a MouseEvent (as shown in Usage) or an options object to toggleTheme/setTheme to override just that one call.

Custom themes

Register extra theme names beyond light/dark/system via themes:

<div x-data="themeTransition({ themes: ['sepia', 'sunset'] })">

setTheme('sepia') then applies a sepia class the same way light/dark do (see Styling above). The component's themes property always includes ['light', 'dark', 'system', ...your custom names], useful for building a theme switcher. toggleTheme() is unaffected, it always flips between light and dark.

API

| | | |---|---| | Alpine.plugin(themeTransition) | Registers the themeTransition Alpine.data component | | x-data="themeTransition(options?)" | Creates a component instance; first one on the page sets the shared default | | theme | Current resolved theme: 'light', 'dark', or a custom theme name | | mode | Current preference: 'light', 'dark', 'system', or a custom theme name | | isAnimating | true while a transition is running | | themes | All registered theme names: ['light', 'dark', 'system', ...custom] | | toggleTheme(eventOrOptions?) | Switch between light and dark | | setTheme(mode, eventOrOptions?) | Set light, dark, system, or a custom theme name |

Notes

  • This package has no Alpine-specific storage. Persistence, system-preference sync, and all other state management come from @brustack/theme-transitions-core's shared controller, the same singleton every other adapter in this project reads from, themeTransition is just a thin x-data reflection of it.
  • @alpinejs/persist is deliberately not a dependency. Its $persist() restores its own localStorage copy into the data object before init() runs, which would create a second, unsynchronized copy of state the core controller already owns via its own storage key.

Known issues

  • Chrome 150 has a regression where the spread effect's clip-path animation can render from the wrong position after the browser window moves between displays with different DPI/scaling. This is a Chrome bug, not something this package can work around. It's already fixed upstream and verified in Chrome Canary; the fix should reach the Stable channel in a future release. See Chromium issue #535696703.