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

@lpdsgn/astro-themes

v0.1.3

Published

Perfect dark mode in Astro with no flash. System preference, multiple themes, and sync across tabs.

Readme

astro-themes

Astro TypeScript

Perfect dark mode in Astro with no flash. An Astro integration that mirrors the behavior of next-themes.

Features

  • Perfect dark mode in 2 lines of code
  • No flash on load (SSR and SSG)
  • System preference with prefers-color-scheme
  • Themed browser UI with color-scheme
  • Sync theme across tabs and windows
  • Force pages to specific themes
  • Class or data attribute selector
  • TypeScript support

Quick Start

1. Install

pnpm astro add @lpdsgn/astro-themes
npx astro add @lpdsgn/astro-themes
yarn astro add @lpdsgn/astro-themes

2. Add ThemeProvider

---
import { ThemeProvider } from "@lpdsgn/astro-themes/components";
---

<!doctype html>
<html lang="en">
  <head>
    <ThemeProvider />
  </head>
  <body>
    <slot />
  </body>
</html>

That's it! Your Astro app now supports dark mode with system preference detection and cross-tab sync.

Styling

CSS Variables

By default, @lpdsgn/astro-themes sets data-theme on the <html> element:

:root {
  --background: white;
  --foreground: black;
}

[data-theme='dark'] {
  --background: black;
  --foreground: white;
}

TailwindCSS

For Tailwind's class-based dark mode, set attribute="class":

<ThemeProvider attribute="class" />

Then configure Tailwind:

Tailwind v4:

@import 'tailwindcss';

@custom-variant dark (&:is(.dark *));

Tailwind v3:

module.exports = {
  darkMode: 'selector',
}

Now use dark-mode classes:

<h1 class="text-black dark:text-white">Hello</h1>

Changing the Theme

Using Client Helpers (Recommended)

<script>
  import { setTheme, toggleTheme, onThemeChange, getResolvedTheme } from '@lpdsgn/astro-themes';
  
  // Toggle between light and dark
  toggleTheme();
  
  // Set a specific theme
  setTheme('dark');
  
  // Get current resolved theme
  const current = getResolvedTheme(); // 'light' | 'dark'
  
  // Listen to theme changes
  const unsubscribe = onThemeChange(({ theme, resolvedTheme }) => {
    console.log('Theme changed:', resolvedTheme);
  });
</script>

Using the Global Object

<button id="theme-toggle">Toggle</button>

<script>
  document.getElementById('theme-toggle').addEventListener('click', () => {
    const { resolvedTheme, setTheme } = window.__ASTRO_THEMES__;
    setTheme(resolvedTheme === 'light' ? 'dark' : 'light');
  });
</script>

Configuration

ThemeProvider Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | storageKey | string | 'theme' | localStorage key for theme setting | | defaultTheme | string | 'system' | Default theme ('light', 'dark', or 'system') | | themes | string[] | ['light', 'dark'] | Available theme names | | attribute | string \| string[] | 'data-theme' | HTML attribute to set. Use 'class' for Tailwind | | value | object | - | Map theme names to custom attribute values | | forcedTheme | string | - | Force a specific theme on this page | | enableSystem | boolean | true | Enable system preference detection | | enableColorScheme | boolean | true | Set color-scheme CSS property | | disableTransitionOnChange | boolean | false | Disable transitions when switching | | nonce | string | - | CSP nonce for the script tag | | scriptProps | object | - | Additional props for the script tag |

Integration Options

The integration can be configured in your astro.config.mjs:

import { defineConfig } from "astro/config";
import astroThemes from "@lpdsgn/astro-themes";

export default defineConfig({
  integrations: [
    astroThemes({
      devToolbar: true, // Enable Dev Toolbar App (default: true)
    }),
  ],
});

| Option | Type | Default | Description | |--------|------|---------|-------------| | devToolbar | boolean | true | Enable the Dev Toolbar App for theme switching during development |

Examples

Force a Page Theme

---
import { ThemeProvider } from "@lpdsgn/astro-themes/components";
import Layout from "../layouts/Layout.astro";
---

<Layout>
  <ThemeProvider forcedTheme="dark" />
  <!-- This page is always dark -->
</Layout>

Multiple Themes

<ThemeProvider themes={['light', 'dark', 'purple', 'pink']} />

Custom Attribute Values

<ThemeProvider 
  attribute="data-theme"
  value={{ light: 'light-mode', dark: 'dark-mode' }} 
/>

Disable Transitions on Change

<ThemeProvider disableTransitionOnChange />

Cloudflare Rocket Loader

<ThemeProvider scriptProps={{ 'data-cfasync': 'false' }} />

API Reference

Client Helpers

Import from @lpdsgn/astro-themes:

| Function | Description | |----------|-------------| | getTheme() | Get complete theme state object | | setTheme(theme) | Set theme (string or callback function) | | toggleTheme() | Toggle between light and dark | | getResolvedTheme() | Get resolved theme ('light' or 'dark') | | getSystemTheme() | Get system preference | | getThemes() | Get list of available themes | | isForcedTheme() | Check if current page has forced theme | | onThemeChange(callback) | Subscribe to changes (returns unsubscribe) |

Theme State Object

Available via window.__ASTRO_THEMES__:

| Property | Type | Description | |----------|------|-------------| | theme | string | Current theme name | | resolvedTheme | string | Resolved theme ('light' or 'dark') | | systemTheme | 'light' \| 'dark' | System preference | | forcedTheme | string \| undefined | Forced theme if set | | themes | string[] | Available themes | | setTheme(theme) | function | Update the theme |

Avoiding Hydration Mismatch

Since the theme is unknown on the server, use CSS to conditionally show content:

[data-theme='dark'] .light-only { display: none; }
[data-theme='light'] .dark-only { display: none; }

Or check in client-side scripts:

<script>
  if (window.__ASTRO_THEMES__) {
    const { resolvedTheme } = window.__ASTRO_THEMES__;
    // Update UI based on theme
  }
</script>

Contributing

This package is structured as a monorepo:

  • playground – Development testing environment
  • packages/astro-themes – The integration package
pnpm i --frozen-lockfile
pnpm dev

License

MIT Licensed. Made with ❤️

Acknowledgements