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

glyphflow

v3.3.1

Published

Lucide icons animated on native Web Animations API for Angular — zero animation-library dependencies.

Readme

glyphflow

Lucide icons that actually move, for Angular — per-shape choreography on the native Web Animations API. No @angular/animations, no third-party animation library, nothing to configure. Plus morphing between icons, in its own entry point.

Free, MIT, no backend. See NOTICE for third-party attribution.

npm i glyphflow

Import one icon, pay for one icon

Measured in CI on every push, not promised — npm run bundle-check:

| What you import | gzip | | --------------------------------- | ------------- | | The component alone, no icons | 5.50 KB | | One icon ([iconDef]="bellIcon") | 5.87 KB | | The whole catalog (name="bell") | 174.74 KB |

An icon costs about 0.36 KB on top of the runtime. Looking one up by name forces the bundler to keep all 1767, because it cannot know which one you will ask for — so that route is opt-in via provideIconCatalog(ANIMATED_ICONS), never a hidden fallback.

import { GfIconComponent, bellIcon } from 'glyphflow';
<gf-icon [iconDef]="bellIcon" trigger="hover" /> <gf-icon [iconDef]="searchIcon" label="Search" />

Triggers

| trigger | Fires | | ------------------ | -------------------------------------------------------------------------------------------------------------------------------------------- | | group (default) | Draws on mount, animates on the nearest .group hover — the same ancestor Tailwind's group-hover: reads. Falls back to its own hover with no .group around. | | hover | Pointer enters. | | tap | Click. | | view | Enters the viewport. viewOnce (default true) decides once vs. every re-entry. | | auto | On mount. | | manual | Never on its own. |

manual pairs with three public methods — grab the instance with ViewChild/viewChild():

@ViewChild(GfIconComponent) icon!: GfIconComponent;

this.icon.play('pulse'); // plays a VARIANT — not a trigger; no argument replays `animation`
this.icon.reverse(); // reverses the animation currently running
this.icon.cancel(); // stops it and resets to the base pose

On touch screens

A touch screen has no pointer passing over things, so hover-driven icons (group, hover) only draw on mount there. That is on purpose: a tap fires a synthetic pointerenter, and animating on it would swallow the feel of the tap. The touch input is the opt-in:

| touch | On a touch screen | | ---------------- | -------------------------------------------------------------------------------------------------------- | | none (default) | Nothing new — the behavior above. | | press | Pressing with a finger plays the hover variant; lifting it reverses it when the variant has reverseOnLeave. For buttons and controls. | | view | Only on screens without hover ((hover: none)): plays the hover variant when the icon enters the viewport. For grids and lists. |

<button type="button"><gf-icon [iconDef]="bellIcon" touch="press" /> Notify me</button>

It only adds listeners: mouse hover keeps working exactly as before, and tap, view, auto and manual ignore it. Set it once for the whole app with provideGfIcons({ touch: 'press' }); an icon's own touch input wins over the global one.

Inside a web component whose real <button> lives in its shadow root — <ion-button>, <ion-tab-button>, a clickable <ion-item>, or any other — the icon hooks onto the component itself, so hover and press count over the whole button, not just over the icon.

The catalog

| | | | ------------------ | ------------------------------------------------- | | Total icons | 1767 — the complete canonical Lucide 1.31 set | | Hand-choreographed | 1767 — every one, motion with intent | | More than one gesture | 1095 — a second variant beyond default |

Every icon in the catalog is now hand-choreographed: generated-icons.ts is empty, and stays as the landing zone for whatever a future Lucide release adds. The two files exist on purpose (curated-icons.ts and generated-icons.ts): the generator never touches the curated ones. Choreography with intent is human judgement, icon by icon. See CONTRIBUTING.md.

Accessibility: one rule, not a matrix

You never coordinate aria-hidden with aria-label. You either pass a label or you don't:

<!-- Decorative: the button already says "Save" -->
<button><gf-icon [iconDef]="saveIcon" /> Save</button>

<!-- Semantic: no text, so the icon carries the meaning -->
<button><gf-icon [iconDef]="saveIcon" label="Save" /></button>

Both components honour prefers-reduced-motion by default — but honouring it means something different in each, and that is deliberate. In <gf-icon> the choreography is decoration on an icon that is already correct, so it holds still. In <gf-icon-morph> holding still would leave you staring at the wrong icon, so it jumps straight to the destination. Reduced motion removes the motion, not the state change.

Morphing

Lives in a separate entry point, glyphflow/morph, so anyone who only wants animated icons doesn't pay for shape-interpolation math.

import { GfIconMorphComponent } from 'glyphflow/morph';
// The binding IS the state: changing [icon] transitions from the previous value.

The first value renders statically — morphing "from nothing" doesn't exist. SSR and browsers without WAAPI land there too: you see the icon, it just doesn't animate.

import { Component } from '@angular/core';
import { GfIconMorphComponent } from 'glyphflow/morph';
import { moonIcon, sunIcon } from 'glyphflow';

@Component({
  imports: [GfIconMorphComponent],
  template: `
    <button (click)="dark = !dark">
      <gf-icon-morph [icon]="dark ? moonIcon : sunIcon" />
    </button>
  `,
})
export class ThemeToggle {
  dark = false;
}

Bots

Animated characters that react to an AI agent. They live in the same package (from 3.2.0) behind four entry points, so you only pay for what you import:

| Entry point | What it gives you | | --- | --- | | glyphflow/bots | The engine, <gf-bot> and the shapes | | glyphflow/bots/gestures | 20 gestures, each one importable on its own | | glyphflow/bots/extras | Hats, toys and work routines | | glyphflow/bots/ai | bindAgent and adapters for the Vercel AI SDK and Anthropic |

import { GfBotComponent, catShape } from 'glyphflow/bots';
import { superBounce } from 'glyphflow/bots/gestures';
// <gf-bot [shape]="shape" skin="g1" [gestures]="{ superBounce }" label="Cat" />

You bring the model and its stream: glyphflow has no AI API and asks for no key. Try every type of output on the Bots page and read the guide.

Speed control

One multiplier for every calculated duration — choreography and morph transitions alike:

import { provideGfIcons } from 'glyphflow';

providers: [provideGfIcons({ durationScale: 0.8 })]; // 20% faster, everywhere

1 is the default — no change. It applies from the next playback: anything already animating keeps the duration WAAPI already received when it started.

Compatibility

Angular 20, 21 and 22 — each version is tested in CI by scaffolding a fresh app with that version's CLI, installing the real .tgz, and building it with SSR prerendering. The peerDependencies range reflects only what that matrix confirmed.

Workspace

  • projects/glyphflow — the library (ng-packagr, gf prefix → <gf-icon>).
  • projects/playground — the live site: icon catalog, patterns, geometry editor and authoring lab. It consumes the published package from npm, never the source in this repo, so that anything the public API fails to cover shows up immediately.
npm install
ng build glyphflow
ng test glyphflow
ng serve playground

npm run verify:clean      # wipes node_modules/dist and runs the whole pipeline from scratch
npm run generate:icons    # regenerates generated-icons.ts from lucide-static
npm run lucide:diff       # diffs the catalog against the installed Lucide version

License

MIT — see LICENSE.