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

@ogasphere/theme-manager

v1.2.1

Published

A reusable theme management package for Next.js and Vite applications

Readme

@ogasphere/theme-manager

A reusable theme management package for Next.js and Vite applications. Automatically discovers themes from CSS files and provides utilities for theme management.

Features

  • Automatic theme discovery from CSS files
  • Type-safe theme management
  • React hooks for client-side usage
  • Server-side functions for Next.js
  • Client-side parser for Vite
  • CLI tool for adding new themes
  • Framework-agnostic core logic
  • Framework auto-detection (Next.js / Vite)
  • Ready-to-use branding page component
  • Base theme CSS styles included

Installation

pnpm add @ogasphere/theme-manager

🤖 Prompt pour IDE (Cursor / Claude Code)

Copiez ce prompt dans votre IDE pour une intégration automatique complète :

Intègre le package @ogasphere/theme-manager dans ce projet. 

CONTEXTE:
- Framework: [Next.js ou Vite - détecte automatiquement]
- Type de projet: [nouveau projet ou projet existant - détecte automatiquement]
- Package: @ogasphere/theme-manager (github:Olivier340/theme-manager)

TÂCHES À EFFECTUER:

1. INSTALLATION:
   - Installer le package: pnpm add github:Olivier340/theme-manager
   - Installer les dépendances nécessaires si manquantes: lucide-react, @radix-ui/react-select (si shadcn/ui utilisé)

2. INTÉGRATION CSS:
   - Détecter le fichier CSS global (app/globals.css, src/index.css, src/globals.css, etc.)
   - Ajouter l'import: @import "@ogasphere/theme-manager/styles/themes.css";
   - Placer l'import après les imports Tailwind si présents

3. CONFIGURATION THEME PROVIDER (Next.js uniquement):
   - Créer ou modifier app/providers.tsx (ou app/layout.tsx)
   - Importer ThemeProvider depuis "@ogasphere/theme-manager/client"
   - Envelopper l'application avec ThemeProvider
   - Configurer defaultTheme="system" et enableSystem

4. API ROUTE (Next.js uniquement):
   - Créer app/api/themes/route.ts
   - Importer getAvailableThemes depuis "@ogasphere/theme-manager/server"
   - Créer un endpoint GET qui retourne { themes } en JSON
   - Gérer les erreurs avec NextResponse

5. PAGE BRANDING:
   - Créer app/branding/page.tsx (Next.js) ou src/pages/branding.tsx (Vite)
   - Importer BrandingPage depuis "@ogasphere/theme-manager/client"
   - Importer les composants UI nécessaires (Card, Select, etc. depuis shadcn/ui ou équivalent)
   - Importer les icônes depuis lucide-react (SunMedium, Moon, Monitor)
   - Créer la logique handleThemeChange qui:
     * Applique le thème au body avec applyTheme depuis "@ogasphere/theme-manager/utils"
     * Sauvegarde dans localStorage
   - Restaurer le thème au chargement depuis localStorage
   - Passer tous les props nécessaires à BrandingPage (currentTheme, onThemeChange, components, icons)

6. APPLICATION DU THÈME GLOBALEMENT:
   - Créer un composant ThemeInitializer ou intégrer dans le layout
   - Utiliser useEffect pour appliquer le thème sauvegardé au chargement
   - Appliquer le thème au document.body avec applyTheme

7. VÉRIFICATIONS:
   - Vérifier que tous les imports sont corrects
   - Vérifier que les composants UI requis sont disponibles
   - S'assurer que le CSS est bien importé
   - Tester que la page branding est accessible

IMPORTANT:
- Adapter le code selon le framework détecté (Next.js vs Vite)
- Utiliser les chemins corrects selon la structure du projet
- Si shadcn/ui n'est pas installé, proposer une alternative ou installer les composants nécessaires
- Créer tous les fichiers manquants
- Ne pas modifier les fichiers existants de manière destructive
- Documenter les changements effectués

Quick Start

1. Import Base Theme Styles

Import the base theme CSS in your global stylesheet:

/* app/globals.css or styles/globals.css */
@import "@ogasphere/theme-manager/styles/themes.css";

Or in your main CSS file:

@import "tailwindcss";
@import "@ogasphere/theme-manager/styles/themes.css";

2. Use the Branding Page Component

"use client";

import { BrandingPage } from "@ogasphere/theme-manager/client";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Monitor, Moon, SunMedium } from "lucide-react";

export function MyBrandingPage() {
  const handleThemeChange = async (themeId: string) => {
    // Your theme update logic here
    await updateTheme(themeId);
  };

  return (
    <BrandingPage
      currentTheme="default"
      onThemeChange={handleThemeChange}
      components={{
        Card,
        CardHeader,
        CardTitle,
        CardDescription,
        CardContent,
        Select,
        SelectTrigger,
        SelectValue,
        SelectContent,
        SelectItem,
      }}
      icons={{
        SunMedium,
        Moon,
        Monitor,
      }}
    />
  );
}

Usage

Next.js Setup

1. Server-side: Get Available Themes

import { getAvailableThemes } from "@ogasphere/theme-manager/server";

const themes = getAvailableThemes();

2. API Route (Next.js)

// app/api/themes/route.ts
import { getAvailableThemes } from "@ogasphere/theme-manager/server";
import { NextResponse } from "next/server";

export async function GET() {
  try {
    const themes = getAvailableThemes();
    return NextResponse.json({ themes });
  } catch {
    return NextResponse.json(
      { error: "Failed to get themes" },
      { status: 500 },
    );
  }
}

3. Client-side: Use Themes Hook

"use client";

import { useThemes } from "@ogasphere/theme-manager/client";

export function ThemeSelector() {
  const { data: themes, isLoading } = useThemes();

  if (isLoading) return <div>Loading themes...</div>;

  return (
    <select>
      {themes?.map((theme) => (
        <option key={theme.id} value={theme.id}>
          {theme.label}
        </option>
      ))}
    </select>
  );
}

4. Apply Theme to Element

import { applyTheme } from "@ogasphere/theme-manager/utils";

const element = document.body;
applyTheme(element, "modern-minimal");

Branding Page Component

The package includes a ready-to-use BrandingPage component that provides a complete UI for theme selection and color mode switching.

Basic Usage

"use client";

import { BrandingPage } from "@ogasphere/theme-manager/client";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Monitor, Moon, SunMedium } from "lucide-react";

export function MyBrandingPage() {
  const handleThemeChange = async (themeId: string) => {
    // Your theme update logic here
    await updateTheme(themeId);
  };

  return (
    <BrandingPage
      currentTheme="default"
      onThemeChange={handleThemeChange}
      components={{
        Card,
        CardHeader,
        CardTitle,
        CardDescription,
        CardContent,
        Select,
        SelectTrigger,
        SelectValue,
        SelectContent,
        SelectItem,
      }}
      icons={{
        SunMedium,
        Moon,
        Monitor,
      }}
    />
  );
}

Customization

You can customize all text labels and messages:

<BrandingPage
  currentTheme="default"
  onThemeChange={handleThemeChange}
  components={components}
  icons={icons}
  title="Custom Branding"
  description="Customize your application theme"
  cardTitle="Theme Settings"
  cardDescription="Choose a theme"
  colorModeLabel="Color Mode"
  themeSelectLabel="Select Theme"
  loadingText="Loading..."
  successMessage="Theme updated!"
  errorMessage="Failed to update theme"
  apiPath="/api/themes"
/>

Props

  • currentTheme: The currently selected theme ID
  • onThemeChange: Callback function called when theme changes
  • components: Object containing your UI components (Card, Select, etc.)
  • icons: Optional object with icon components (SunMedium, Moon, Monitor)
  • title: Page title (default: "Branding")
  • description: Page description
  • cardTitle: Card title
  • cardDescription: Card description
  • colorModeLabel: Label for color mode section
  • themeSelectLabel: Label for theme select
  • loadingText: Loading message
  • successMessage: Success toast message
  • errorMessage: Error toast message
  • apiPath: Custom API path for fetching themes (default: "/api/themes")

CSS Theme Styles

The package includes base theme CSS styles. Import them in your global stylesheet:

/* app/globals.css */
@import "tailwindcss";
@import "@ogasphere/theme-manager/styles/themes.css";

The CSS file includes:

  • Base theme variables for the default theme
  • Dark mode variants
  • Support for custom themes (add your own .theme-{id} classes)

Advanced Configuration

Custom CSS Path

import { getAvailableThemes } from "@ogasphere/theme-manager/server";

const themes = getAvailableThemes({
  cssPath: path.join(process.cwd(), "styles", "themes.css"),
  prefix: "custom-theme",
  fallbackThemes: [
    { id: "default", label: "Default" },
  ],
});

Custom API Path

import { useThemes } from "@ogasphere/theme-manager/client";

const { data: themes } = useThemes({
  apiPath: "/api/custom/themes",
  staleTime: 10 * 60 * 1000, // 10 minutes
});

Vite Setup

1. Client-side: Parse Themes Directly

"use client";

import { useThemes } from "@ogasphere/theme-manager/client";

export function ThemeSelector() {
  const { data: themes, isLoading } = useThemes({
    useClientParser: true,
    clientParserOptions: {
      cssPath: "/src/index.css", // Path to your CSS file
    },
  });

  if (isLoading) return <div>Loading themes...</div>;

  return (
    <select>
      {themes?.map((theme) => (
        <option key={theme.id} value={theme.id}>
          {theme.label}
        </option>
      ))}
    </select>
  );
}

2. Alternative: Use Client Parser Directly

"use client";

import { getAvailableThemesClient } from "@ogasphere/theme-manager/client";

async function loadThemes() {
  const themes = await getAvailableThemesClient({
    cssPath: "/index.css", // Public path or use cssContent option
  });
  return themes;
}

Note: For Vite, you can also import CSS as text and pass it directly:

import cssContent from "./index.css?raw";
import { getAvailableThemesClient } from "@ogasphere/theme-manager/client";

const themes = await getAvailableThemesClient({
  cssContent, // Pass CSS content directly
});

3. Vite API Route (if using a backend)

If you're using Vite with a backend API, you can create an API endpoint:

// src/api/themes.ts or your API route handler
import { getAvailableThemes } from "@ogasphere/theme-manager/server";

export async function getThemes() {
  return getAvailableThemes({
    cssPath: path.join(process.cwd(), "src", "index.css"),
  });
}

Note: For Vite projects, the package automatically detects the framework and uses appropriate default paths:

  • src/index.css
  • src/main.css
  • src/styles.css
  • src/globals.css

Adding New Themes

Use the CLI tool to add new Shadcn themes:

pnpm theme-manager-add <theme-name> <theme-url> [css-path]

Example:

pnpm theme-manager-add modern-minimal https://tweakcn.com/r/themes/modern-minimal.json

Theme Provider

The package includes a theme provider that wraps next-themes:

"use client";

import { ThemeProvider } from "@ogasphere/theme-manager/client";

export function Providers({ children }) {
  return (
    <ThemeProvider defaultTheme="system" enableSystem>
      {children}
    </ThemeProvider>
  );
}

API Reference

Core

parseThemesFromCSS(cssContent: string, options?: ThemeParserOptions): Theme[]

Parse CSS content to extract theme classes.

formatThemeLabel(id: string): string

Convert theme ID to human-readable label (e.g., "modern-minimal" → "Modern Minimal").

validateThemeId(themeId: string, availableThemes: Theme[]): boolean

Validate if a theme ID exists in the available themes.

getThemeById(themeId: string, availableThemes: Theme[]): Theme | undefined

Get a theme by its ID.

Server

getAvailableThemes(options?: ThemeConfig): Theme[]

Get all available themes from CSS file. Returns fallback themes if parsing fails.

Client

useThemes(options?: UseThemesOptions)

React hook that fetches themes from API using TanStack Query.

ThemeProvider

React component that wraps next-themes ThemeProvider.

Utils

applyTheme(element: HTMLElement, themeId: string, prefix?: string): void

Apply a theme class to an HTML element.

removeTheme(element: HTMLElement, prefix?: string): void

Remove all theme classes from an HTML element.

Types

type Theme = {
  id: string;
  label: string;
};

type ThemeConfig = {
  cssPath?: string;
  prefix?: string;
  fallbackThemes?: Theme[];
};

type UseThemesOptions = {
  apiPath?: string;
  staleTime?: number;
};

Migration Guide

From Local Implementation

  1. Install the package:

    pnpm add @ogasphere/theme-manager
  2. Replace imports:

    // Before
    import { getAvailableThemes } from "@/lib/themes/get-available-themes";
       
    // After
    import { getAvailableThemes } from "@ogasphere/theme-manager/server";
  3. Update API route:

    // app/api/themes/route.ts
    import { getAvailableThemes } from "@ogasphere/theme-manager/server";
  4. Update hooks:

    // Before
    import { useThemes } from "@/hooks/use-themes";
       
    // After
    import { useThemes } from "@ogasphere/theme-manager/client";
  5. Use validation utilities:

    import { validateThemeId } from "@ogasphere/theme-manager/core";
       
    if (!validateThemeId(themeId, availableThemes)) {
      throw new Error("Invalid theme ID");
    }

CSS Structure

The package expects themes to be defined in CSS with the following structure:

.theme-{id} {
  --background: ...;
  --foreground: ...;
  /* ... other CSS variables */
}

.dark .theme-{id} {
  --background: ...;
  --foreground: ...;
  /* ... dark mode variables */
}

License

MIT