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 install jsg-stylizer
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.
<!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>
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>
);
}
<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>
<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>
---
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>
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"
});
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();
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);
});
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;
}
Stylizer includes a DevTools sidebar that slides in from the right side of the screen:
The sidebar is automatically mounted when you call Stylizer.configure(). It provides an easy way to experiment with fonts during development.
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:
Stylizer.configure({
googleApiKey: 'YOUR_API_KEY_HERE'
});
# 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
MIT © JSG Tech Check
Contributions welcome! Please open an issue or PR.
Made with ❤️ by JSG Tech Check