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

@paradox-design/icons

v0.2.0

Published

Paradox Design System icon registry — default icon set included, replaceable as a whole

Readme

@paradox-design/icons

The icon registry of Paradox Design System.

Works out of the box. <pdx-icon> renders icons from the Paradox icon set (@paradox-design/icon-set) without any configuration. A client with its own icons calls setIconSet() once — and from then on our set is ignored entirely.

Sets are never mixed

An icon set is a whole unit. setIconSet() replaces the active set; it does not extend it. An icon missing from the client's set is missing — it does not silently fall back to ours. Two drawing styles in one interface are worse than a visible gap, which is reported by the pdx-icon-error event and a console warning.

The default set is loaded lazily, one module per icon: a client who switches to their own set downloads none of our icons.

Installation

npm install @paradox-design/icons

The only runtime dependency is @paradox-design/icon-set. The package depends neither on Lit nor on any framework.

Default set — nothing to do

<pdx-icon name="check"></pdx-icon>

The list of names lives in @paradox-design/icon-set (iconNames) and in the Storybook gallery.

Your own set

A record of SVG strings

import { setIconSet } from '@paradox-design/icons';

setIconSet({
  save: '<svg viewBox="0 0 24 24"><path d="M4 4h16v16H4z"/></svg>',
});

A record is sanitized once, up front: an invalid icon fails at configuration time, and the previously active set stays in place.

A set available as modules

import { setIconSet, createLazyResolver } from '@paradox-design/icons';

setIconSet(
  createLazyResolver({
    save: () => import('./icons/save.svg?raw').then((m) => m.default),
    trash: () => import('./icons/trash.svg?raw').then((m) => m.default),
  }),
);

A module is imported only on the first use of the icon, and exactly once.

Files on a server

import { setIconSet, createFetchResolver } from '@paradox-design/icons';

setIconSet(createFetchResolver({ url: '/assets/icons/{name}.svg' }));

SVG sprite

import { setIconSet, createSpriteResolver } from '@paradox-design/icons';

setIconSet(createSpriteResolver({ url: '/assets/icons.svg', idPrefix: 'icon-' }));

Beware of classic sprites. <use href="sprite.svg#id"> does not work inside Shadow DOM in Chrome or Safari, and every component of this design system uses Shadow DOM. That is why createSpriteResolver fetches the file and extracts individual symbols from it instead of relying on an external reference. The sprite is fetched once, regardless of how many icons are used.

Names different from the design system's

The most common case in practice: components ask for close, the client's set has x.

import { setIconSet, createAliasResolver } from '@paradox-design/icons';

setIconSet(createAliasResolver({ close: 'x', trash: 'trash-2' }, clientIconSet));

Without this layer the set's names leak into application code, and changing the icon vendor becomes a project-wide refactor. chainResolvers combines several sources of your own into one set — it is still one set passed to setIconSet().

When to call setIconSet()

Once, before the first render. Calling it later works — every rendered <pdx-icon> re-resolves its icon — but users see our icons flash first, so the registry warns about it in the console.

Results are cached per set — negative results too. Changing the set clears the cache, and a request that was still in flight for the previous set never lands in the new one.

onIconSetChange(listener) notifies about set changes; <pdx-icon> uses it, and framework wrappers can do the same. resetIconSet() restores the default set.

Security

Every icon is sanitized before it reaches the DOM. This is not excessive caution — SVG can execute code in several ways, and <script> is merely the most obvious one:

| Vector | What it does | | ----------------------------------------- | -------------------------------- | | <svg onload="..."> | event handler | | <a href="javascript:..."> | javascript: protocol | | <foreignObject><iframe> | arbitrary HTML inside SVG | | <set attributeName="onload" to="..."> | animation replacing an attribute | | <use href="https://other.host/x.svg#a"> | remote content in an attribute |

The sanitizer works with an allow-list, not a deny-list. A deny-list always loses — one unknown vector is enough. Everything not on the list is removed, and removals are reported via console.warn with the icon name.

Sanitization can be disabled for content compiled into the build. The default set does not need it — the icon-set build validates every icon structurally:

setIconSet(ownIconSet, { trusted: true });

Never set trusted for content coming from a client, a CMS, an API or any user-provided configuration. That is exactly the case the sanitizer protects against.

Accessibility

The sanitizer sets aria-hidden="true" and focusable="false" on every icon.

An icon is a presentational element — accessibility is the responsibility of the component that embeds it, through aria-label on its host. If the label lived inside the SVG, a screen reader would announce it twice.

The sanitizer also adds fill="currentColor" when the icon does not set its own color — so the icon inherits the text color and follows the theme with no extra configuration.

API

| Function | Description | | ---------------------- | --------------------------------------------------- | | setIconSet | replaces the active icon set entirely | | resetIconSet | restores the default Paradox set | | onIconSetChange | subscribes to set changes; returns an unsubscribe | | resolveIcon | asynchronously returns an icon or null | | getIconSync | synchronous read — only for icons already available | | hasIcon | whether an icon is already available synchronously | | getIconRegistryStats | active set (default/custom) and cache size | | resetIconRegistry | restores the initial state; intended for tests | | sanitizeSvg | the sanitizer, used standalone | | createFetchResolver | icons over HTTP | | createSpriteResolver | icons from an SVG sprite | | createLazyResolver | icons from lazy imports | | createAliasResolver | maps semantic names to the set's names | | chainResolvers | combines your own sources into one set |

Multiple copies of the package

Registry state lives on globalThis under a global symbol, not in the module. With an unlucky dependency tree an application may load two copies of this package — a plain module singleton would then give two independent registries. The symptom ("wrong icons", even though the set was configured) leads to an investigation that takes hours.

Tests

pnpm test:unit

Tests run in real Chromium, not jsdom. The sanitizer relies on DOMParser in image/svg+xml mode, and DOM simulations differ from browsers exactly where this code is tested: namespace handling, xlink: attributes and recovery from parse errors. A green test in jsdom would prove nothing about security code.

License

MIT © Paradox Software