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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@crimsonsunset/stylizer

v0.0.1

Published

Config-driven font picker with DevTools sidebar for experimenting with Google Fonts

Readme

🎨 Stylizer

Config-driven font picker with DevTools sidebar for experimenting with Google Fonts

A framework-agnostic font picker that lets you quickly experiment with different Google Fonts on your site. Works in vanilla JavaScript, React, Vue, Svelte, Astro, and any other framework (or no framework at all).

npm version License: MIT

✨ Features

  • 🌐 Framework Agnostic - Works everywhere (React, Vue, Svelte, Astro, vanilla JS)
  • 🎯 Dual Font Support - Separate Primary & Secondary font selection
  • 🎨 Curated Mode - 38 handpicked fonts, zero API key needed
  • 🔍 Browse All Mode - Access 1500+ Google Fonts with API key
  • Favorites System - Save your favorite fonts to localStorage
  • 🎭 Font Variants - Select font weights (100-900) and italic styles
  • 🌓 Theme Integration - Automatic light/dark mode support via CSS variables
  • 🛠️ DevTools Sidebar - Slide-in panel for easy font selection
  • 📦 Tiny Bundle - Lightweight with minimal dependencies
  • 🚀 Zero Config - Works out of the box with sensible defaults

📦 Installation

npm install @jsg/stylizer

CSS Import (Optional)

Stylizer includes CSS that is automatically injected when you import the JavaScript module. If you prefer to import CSS manually (e.g., for better control over loading order), you can import it separately:

import '@jsg/stylizer/style.css';

Note: All dependencies (Preact, Evergreen UI, fontpicker) are bundled with the package, so no additional peer dependencies are required.

🚀 Quick Start

Vanilla JavaScript

<!DOCTYPE html>
<html>
<head>
  <style>
    :root {
      --font-primary-family: "Changa One", sans-serif;
      --font-primary-weight: 400;
      --font-primary-style: normal;
      --font-secondary-family: "Nova Square", sans-serif;
      --font-secondary-weight: 400;
      --font-secondary-style: normal;
    }
    
    body {
      font-family: var(--font-primary-family);
      font-weight: var(--font-primary-weight);
      font-style: var(--font-primary-style);
    }
    
    h1, h2, h3 {
      font-family: var(--font-secondary-family);
      font-weight: var(--font-secondary-weight);
      font-style: var(--font-secondary-style);
    }
  </style>
</head>
<body>
  <h1>Hello Stylizer!</h1>
  
  <script type="module">
    import { Stylizer } from '@jsg/stylizer';
    
    // Configure Stylizer
    Stylizer.configure({
      fonts: {
        primary: 'Changa One',
        secondary: 'Nova Square'
      },
      cssVariables: {
        primary: {
          family: '--font-primary-family',
          weight: '--font-primary-weight',
          style: '--font-primary-style'
        },
        secondary: {
          family: '--font-secondary-family',
          weight: '--font-secondary-weight',
          style: '--font-secondary-style'
        }
      }
    });
    
    // Listen to font changes
    window.addEventListener('stylizer-font-changed', (e) => {
      console.log('Font changed:', e.detail);
    });
  </script>
</body>
</html>

React

import { Stylizer } from '@jsg/stylizer';
import { useEffect } from 'react';

function App() {
  useEffect(() => {
    Stylizer.configure({
      fonts: {
        primary: 'Roboto',
        secondary: 'Open Sans'
      },
      cssVariables: {
        primary: {
          family: '--font-primary-family',
          weight: '--font-primary-weight',
          style: '--font-primary-style'
        },
        secondary: {
          family: '--font-secondary-family',
          weight: '--font-secondary-weight',
          style: '--font-secondary-style'
        }
      }
    });
    
    const handleFontChange = (e: CustomEvent) => {
      console.log('Font changed:', e.detail);
    };
    
    window.addEventListener('stylizer-font-changed', handleFontChange);
    
    return () => {
      window.removeEventListener('stylizer-font-changed', handleFontChange);
    };
  }, []);
  
  return (
    <div>
      <h1>My React App</h1>
    </div>
  );
}

Svelte

<script lang="ts">
  import { onMount } from 'svelte';
  import { Stylizer } from '@jsg/stylizer';
  
  onMount(() => {
    Stylizer.configure({
      fonts: {
        primary: 'Roboto',
        secondary: 'Open Sans'
      },
      cssVariables: {
        primary: {
          family: '--font-primary-family',
          weight: '--font-primary-weight',
          style: '--font-primary-style'
        },
        secondary: {
          family: '--font-secondary-family',
          weight: '--font-secondary-weight',
          style: '--font-secondary-style'
        }
      }
    });
    
    const handleFontChange = (e: CustomEvent) => {
      console.log('Font changed:', e.detail);
    };
    
    window.addEventListener('stylizer-font-changed', handleFontChange);
    
    return () => {
      window.removeEventListener('stylizer-font-changed', handleFontChange);
    };
  });
</script>

<h1>My Svelte App</h1>

Vue 3

<script setup lang="ts">
import { onMounted, onUnmounted } from 'vue';
import { Stylizer } from '@jsg/stylizer';

let handleFontChange: ((e: CustomEvent) => void) | null = null;

onMounted(() => {
  Stylizer.configure({
    fonts: {
      primary: 'Roboto',
      secondary: 'Open Sans'
    },
    cssVariables: {
      primary: {
        family: '--font-primary-family',
        weight: '--font-primary-weight',
        style: '--font-primary-style'
      },
      secondary: {
        family: '--font-secondary-family',
        weight: '--font-secondary-weight',
        style: '--font-secondary-style'
      }
    }
  });
  
  handleFontChange = (e: CustomEvent) => {
    console.log('Font changed:', e.detail);
  };
  
  window.addEventListener('stylizer-font-changed', handleFontChange as EventListener);
});

onUnmounted(() => {
  if (handleFontChange) {
    window.removeEventListener('stylizer-font-changed', handleFontChange as EventListener);
  }
});
</script>

<template>
  <div>
    <h1>My Vue App</h1>
  </div>
</template>

Astro

---
import { Stylizer } from '@jsg/stylizer';

if (import.meta.env.DEV) {
  Stylizer.configure({
    fonts: {
      primary: 'Sansation',
      secondary: 'Michroma'
    },
    cssVariables: {
      primary: {
        family: '--font-primary-family',
        weight: '--font-primary-weight',
        style: '--font-primary-style'
      },
      secondary: {
        family: '--font-secondary-family',
        weight: '--font-secondary-weight',
        style: '--font-secondary-style'
      }
    },
    googleApiKey: import.meta.env.PUBLIC_GOOGLE_FONTS_API_KEY
  });
}
---

<h1>My Astro Site</h1>

📚 API Documentation

Configuration

Configure Stylizer using the Stylizer.configure() method:

import { Stylizer } from '@jsg/stylizer';

Stylizer.configure({
  fonts?: {
    primary?: string;      // Default: "Changa One"
    secondary?: string;    // Default: "Nova Square"
  };
  cssVariables?: {
    primary?: {
      family?: string;    // Default: "--font-primary-family"
      weight?: string;    // Default: "--font-primary-weight"
      style?: string;     // Default: "--font-primary-style"
    } | string;           // Legacy: string for backward compat
    secondary?: {
      family?: string;    // Default: "--font-secondary-family"
      weight?: string;    // Default: "--font-secondary-weight"
      style?: string;     // Default: "--font-secondary-style"
    } | string;           // Legacy: string for backward compat
  };
  theme?: {
    background?: string;
    text?: string;
    accent?: string;
    border?: string;
    surface?: string;
    textSecondary?: string;
  };
  googleApiKey?: string;  // Required for Browse All mode
  previewText?: string;    // Default: "The quick brown fox jumps over the lazy dog"
});

Methods

const instance = Stylizer.getInstance();

// Get current font state
const fonts = instance.getFonts();
// Returns: { primary: { family, weight, italic }, secondary: { ... } }

// Get current configuration
const config = instance.getConfig();

// Open font picker programmatically
await instance.openFontPicker('primary', 'curated');  // or 'secondary', 'all'

// Reset fonts to defaults
await instance.reset();

// Destroy instance and cleanup
instance.destroy();

Events

Listen to custom events on the window object:

// Font changed event
window.addEventListener('stylizer-font-changed', (e: CustomEvent) => {
  console.log('Font type:', e.detail.fontType);        // 'primary' | 'secondary'
  console.log('Font family:', e.detail.fontFamily);   // 'Roboto'
  console.log('Weight:', e.detail.weight);             // 100-900
  console.log('Italic:', e.detail.italic);              // boolean
  console.log('CSS variables:', e.detail.cssVariables); // { family, weight, style }
});

// Font reset event
window.addEventListener('stylizer-font-reset', (e: CustomEvent) => {
  console.log('Primary:', e.detail.primaryFont);
  console.log('Secondary:', e.detail.secondaryFont);
});

🎨 Theme Integration

Stylizer uses CSS variables for theme integration. Define these in your theme:

:root {
  /* Required for fonts - separate variables for family, weight, and style */
  --font-primary-family: "Changa One", sans-serif;
  --font-primary-weight: 400;
  --font-primary-style: normal;
  --font-secondary-family: "Nova Square", sans-serif;
  --font-secondary-weight: 400;
  --font-secondary-style: normal;
  
  /* Optional: Customize Stylizer DevTools sidebar appearance */
  --stylizer-background: #ffffff;
  --stylizer-text: #000000;
  --stylizer-accent: #3b82f6;
  --stylizer-border: #e5e7eb;
  --stylizer-surface: #f9fafb;
  --stylizer-text-secondary: #6b7280;
}

/* Apply fonts */
body {
  font-family: var(--font-primary-family);
  font-weight: var(--font-primary-weight);
  font-style: var(--font-primary-style);
}

h1, h2, h3 {
  font-family: var(--font-secondary-family);
  font-weight: var(--font-secondary-weight);
  font-style: var(--font-secondary-style);
}

/* Dark mode */
[data-theme="dark"] {
  --stylizer-background: #1a1a1a;
  --stylizer-text: #ffffff;
  --stylizer-accent: #60a5fa;
  --stylizer-border: #374151;
  --stylizer-surface: #262626;
  --stylizer-text-secondary: #9ca3af;
}

🛠️ DevTools Sidebar

Stylizer includes a DevTools sidebar that slides in from the right side of the screen:

  • Visible by default - Opens automatically when configured
  • Collapsible - Click the X button to collapse to a small button in the top-right corner
  • Persistent state - Collapsed/expanded state persists across page reloads
  • Font selection - Click buttons to open font picker modal
  • Live preview - See font changes in real-time

The sidebar is automatically mounted when you call Stylizer.configure(). It provides an easy way to experiment with fonts during development.

🔑 Google Fonts API Key

Curated Mode (default) works without an API key - it uses 38 pre-selected fonts.

Browse All Mode requires a free Google Fonts API key to access all 1500+ fonts:

  1. Visit Google Cloud Console
  2. Create a new project
  3. Enable "Google Fonts Developer API"
  4. Create an API key
  5. Pass it to the configuration:
Stylizer.configure({
  googleApiKey: 'YOUR_API_KEY_HERE'
});

🛠️ Development

# Install dependencies
npm install

# Build the library
npm run build

# Watch mode
npm run dev

# Type check
npm run type-check

# Run demo site
npm run demo

📄 License

MIT © JSG Tech Check

🤝 Contributing

Contributions welcome! Please open an issue or PR.

🔗 Links


Made with ❤️ by JSG Tech Check