@crimsonsunset/stylizer
v0.0.1
Published
Config-driven font picker with DevTools sidebar for experimenting with Google Fonts
Maintainers
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).
✨ 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/stylizerCSS 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:
- Visit Google Cloud Console
- Create a new project
- Enable "Google Fonts Developer API"
- Create an API key
- 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
- Demo Site
- GitHub Repository
- JSFontPicker (underlying font picker library)
- JSG Logger
Made with ❤️ by JSG Tech Check
