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

typography-toolkit

v1.5.2

Published

Letter-by-letter text animations with proximity-based disintegration effects and Google Fonts selection

Readme

Typography Toolkit

Letter-by-letter text animations with proximity-based disintegration effects. Create animated text where each letter moves independently with base animations (falling, splitting, glitching, floating) and reacts to cursor proximity.

Features

  • Letter-by-Letter Control - Each letter is a separate DOM element with independent animations
  • Base Animations - Falling, splitting, glitching, and floating animations
  • Proximity-Based Disintegration - Letters react to cursor without direct interaction
  • Font Selection - Suggest Google Fonts based on natural language descriptions
  • Font Refinement - Refine suggestions based on feedback (e.g., "too casual, want gothic")
  • Dynamic Font Loading - Automatically load Google Fonts without manual <link> tags
  • DOM-Based - Uses CSS transforms (GPU-accelerated), accessible, and styleable
  • Modular & Extensible - Easy to add new animation types and behaviors
  • Zero Dependencies - Vanilla TypeScript/JavaScript

Installation

npm

npm install typography-toolkit

CDN (UMD)

<script src="https://unpkg.com/typography-toolkit/dist/typography-toolkit.umd.js"></script>
<script>
  // Option 1: Use TypographyToolkit namespace
  const text = new TypographyToolkit.AnimatedText({...});
  
  // Option 2: Use AnimatedText directly (also available on window)
  const text = new AnimatedText({...});
</script>

Global Names: When using the UMD build, the library exposes:

  • window.TypographyToolkit - Full namespace object
  • window.AnimatedText - Direct access to AnimatedText class (for convenience)

ES Modules

import { AnimatedText } from 'typography-toolkit';

Quick Start

Basic Usage

import { AnimatedText } from 'typography-toolkit';

const text = new AnimatedText({
  text: 'HELLO',
  container: document.body,
  animations: ['falling', 'glitching']
});

// Clean up when done
setTimeout(() => text.destroy(), 10000);

With Disintegration

const text = new AnimatedText({
  text: 'FEED ME',
  container: document.body,
  animations: ['falling', 'splitting'],
  disintegration: {
    enabled: true,
    radius: 80,
    behaviors: ['fall-away', 'explode'],
    strength: 0.8
  },
  style: {
    fontFamily: 'Arial',
    fontSize: 24,
    color: 'rgba(60, 60, 60, 0.8)',
    textShadow: '2px 2px 4px rgba(0,0,0,0.5)',
    letterSpacing: '2px'
  }
});

Advanced Usage

const text = new AnimatedText({
  text: 'ANIMATED TEXT',
  container: document.getElementById('container'),
  animations: ['falling', 'splitting', 'glitching', 'floating'],
  cycle: true, // Cycle through animation types per letter
  speed: 1.5, // Animation speed multiplier
  amplitude: 1.2, // Animation amplitude multiplier
  disintegration: {
    enabled: true,
    radius: 100,
    behaviors: ['fall-away', 'split-apart', 'explode'],
    strength: 1.0
  },
  style: {
    fontFamily: 'Georgia, serif',
    fontSize: 32,
    color: 'rgba(100, 50, 50, 0.9)',
    fontWeight: 'bold'
  },
  position: {
    x: 100,
    y: 200
  },
  fadeOut: 8000 // Auto-destroy after 8 seconds
});

API Reference

AnimatedText

Main class for creating animated text.

Constructor Options

interface AnimatedTextOptions {
  text: string;                    // Text to animate
  container: HTMLElement;           // Container element
  animations?: AnimationType[];    // Animation types (default: all)
  cycle?: boolean;                  // Cycle through types per letter (default: true)
  speed?: number;                   // Speed multiplier (default: 1.0)
  amplitude?: number;               // Amplitude multiplier (default: 1.0)
  disintegration?: DisintegrationOptions; // Disintegration config
  style?: StyleOptions;             // CSS style options
  position?: { x?: number; y?: number }; // Position (default: random)
  fadeOut?: number;                 // Auto-destroy after ms (default: 0 = never)
}

Methods

  • destroy() - Clean up and remove the animated text
  • getElement() - Get the text container element

Animation Types

  • 'falling' - Letters drift downward
  • 'splitting' - Letters drift outward horizontally
  • 'glitching' - Letters randomly shift position
  • 'floating' - Letters slowly rise

Disintegration Behaviors

  • 'fall-away' - Letters drop down when cursor approaches
  • 'split-apart' - Letters spread horizontally
  • 'explode' - Letters scatter in all directions

Style Options

interface StyleOptions {
  fontFamily?: string;
  fontSize?: number;
  color?: string;
  fontWeight?: string;
  textShadow?: string;      // CSS text-shadow value
  letterSpacing?: string;  // CSS letter-spacing value
  textTransform?: string;   // CSS text-transform value
}

Container Styling

containerClass?: string;                    // CSS class to add to container
containerStyle?: Record<string, string>;    // Inline styles for container

Position Constraints

position?: {
  x?: number;
  y?: number;
  constrainToViewport?: boolean;  // Keep text within viewport bounds
}

Event Callbacks

callbacks?: {
  onCreate?: (element: HTMLElement) => void;    // Called when text is created
  onDestroy?: () => void;                       // Called when text is destroyed
  onDisintegrate?: (letterIndex: number) => void; // Called when a letter disintegrates
}

Font Selection API

suggestFont(description: string): FontSuggestion | undefined

Get a single font suggestion based on a natural language description.

const font = suggestFont('gothic horror');
// Returns best matching font, or undefined if no matches

suggestFonts(description: string): FontSuggestion[]

Get multiple font suggestions sorted by relevance.

const fonts = suggestFonts('hand-drawn');
// Returns array of matching fonts, sorted by relevance

loadGoogleFont(fontName: string): Promise<void>

Dynamically load a Google Font. Prevents duplicate loading.

await loadGoogleFont('Caveat');
// Adds <link> tag to document head if not already loaded

getFontFamily(fontName: string, fallback?: string): string

Get CSS font-family string with appropriate fallback.

getFontFamily('Caveat'); // "'Caveat', cursive"
getFontFamily('VT323'); // "'VT323', monospace"

refineFont(description: string, feedback: RefinementFeedback): FontSuggestion | undefined

Refine font suggestions based on user feedback using scoring algorithm.

const refined = refineFont('hand-drawn', {
  rejectedFont: 'Caveat',
  negativeAspects: ['too casual'],
  positiveAspects: ['gothic', 'ornate']
});

refineSuggestion(description: string, feedback: RefinementFeedback): FontSuggestion[]

Get multiple refined suggestions sorted by score.

Font Suggestion Types

interface FontSuggestion {
  name: string;              // Display name
  googleFontsName: string;   // Exact Google Fonts name
  categories: string[];       // Tags for matching
  description: string;        // Human-readable description
  artistic: boolean;         // True if striking/unique (not "safe")
}

interface RefinementFeedback {
  rejectedFont: string;      // Font name they didn't like
  negativeAspects: string[]; // What they don't like
  positiveAspects: string[]; // What they want instead
}

Examples

Simple Falling Text

const text = new AnimatedText({
  text: 'FALLING',
  container: document.body,
  animations: ['falling']
});

Glitching Text with Disintegration

const text = new AnimatedText({
  text: 'GLITCH',
  container: document.body,
  animations: ['glitching'],
  disintegration: {
    enabled: true,
    radius: 60,
    behaviors: ['explode']
  }
});

Multiple Animation Types

const text = new AnimatedText({
  text: 'VARIED',
  container: document.body,
  animations: ['falling', 'splitting', 'glitching', 'floating'],
  cycle: true // Each letter uses different animation
});

Font Selection & Loading

import { suggestFont, loadGoogleFont, getFontFamily, AnimatedText } from 'typography-toolkit';

// Suggest a font based on description
const font = suggestFont('hand-drawn');
// Returns: { name: 'Hand-drawn Casual', googleFontsName: 'Caveat', ... }

// Load the font dynamically
await loadGoogleFont(font.googleFontsName);

// Use in animation
const text = new AnimatedText({
  text: 'HELLO',
  container: document.body,
  style: {
    fontFamily: getFontFamily(font.googleFontsName) // "'Caveat', cursive"
  }
});

Font Refinement with Feedback

import { suggestFont, refineFont, loadGoogleFont, getFontFamily, AnimatedText } from 'typography-toolkit';

// Initial suggestion
const initial = suggestFont('hand-drawn');
// User doesn't like it: "too casual, want gothic and ornate"

// Refine based on feedback
const refined = refineFont('hand-drawn', {
  rejectedFont: initial.googleFontsName,
  negativeAspects: ['too casual', 'too simple'],
  positiveAspects: ['gothic', 'ornate', 'striking']
});
// Returns: { name: 'Gothic Blackletter', googleFontsName: 'UnifrakturMaguntia', ... }

// Load and use
await loadGoogleFont(refined.googleFontsName);
const text = new AnimatedText({
  text: 'GOTHIC',
  container: document.body,
  style: {
    fontFamily: getFontFamily(refined.googleFontsName)
  }
});

Performance

  • Uses requestAnimationFrame for smooth 60fps animations
  • CSS transforms are GPU-accelerated
  • Efficient proximity calculations
  • Automatic cleanup on destroy

Common Patterns

Creating Multiple Texts

const messages = ['HELLO', 'WORLD', 'ANIMATED'];
messages.forEach((msg, i) => {
  new AnimatedText({
    text: msg,
    container: document.body,
    position: { x: 100 + i * 200, y: 100 },
    fadeOut: 5000
  });
});

Styling with CSS Classes

const text = new AnimatedText({
  text: 'STYLED',
  container: document.body,
  containerClass: 'my-custom-class',
  style: {
    fontSize: 32,
    color: '#ff0000'
  }
});

// CSS:
// .my-custom-class { background: rgba(0,0,0,0.1); padding: 10px; }

Tracking Lifecycle Events

const text = new AnimatedText({
  text: 'TRACKED',
  container: document.body,
  callbacks: {
    onCreate: (element) => {
      console.log('Text created:', element);
      // Analytics tracking, etc.
    },
    onDestroy: () => {
      console.log('Text destroyed');
    },
    onDisintegrate: (letterIndex) => {
      console.log('Letter disintegrated:', letterIndex);
    }
  }
});

Constraining to Viewport

const text = new AnimatedText({
  text: 'CONSTRAINED',
  container: document.body,
  position: {
    x: window.innerWidth - 100, // Might be off-screen
    y: window.innerHeight - 50,
    constrainToViewport: true  // Automatically adjusts to stay on screen
  }
});

Troubleshooting

Text Not Animating?

  • Check container: Ensure the container element exists and is visible
  • Check animations array: Make sure at least one animation type is specified
  • Check browser support: Requires modern browser with requestAnimationFrame support

Disintegration Not Working?

  • Check disintegration.enabled: Must be set to true
  • Check radius: Increase radius if cursor needs to be closer
  • Check mouse tracking: Ensure mouse events are not being blocked

Text Positioned Off-Screen?

  • Use constrainToViewport: Set position.constrainToViewport: true
  • Check container bounds: Ensure container has proper dimensions
  • Manual bounds checking: Calculate position manually based on container size

Performance Issues?

  • Reduce animation types: Fewer animations = better performance
  • Reduce letter count: Shorter text = better performance
  • Use fadeOut: Auto-destroy texts after a delay to prevent memory buildup
  • Destroy manually: Call destroy() when done with text instances

Fonts Not Loading?

  • Check font name: Ensure Google Font name is correct
  • Check network: Verify Google Fonts CDN is accessible
  • Check font-family: Use getFontFamily() helper for correct CSS format

Browser Support

  • Modern browsers with ES2020 support
  • Requires requestAnimationFrame API
  • CSS transforms support
  • Mobile browsers supported (touch events coming soon)

License

MIT