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

@quadrel-enterprise-ui/icons

v1.12.1

Published

Quadrel icon font — WOFF2, SCSS @font-face and icon definitions, generated from SVG sources.

Readme

@quadrel-enterprise-ui/icons

Icon font of the Quadrel design system by the Swiss Federal Office for Customs and Border Security (BAZG). The source are SVG files; the font is generated here and published to npm. The quadrel-framework consumes only the published artefacts. No icomoon needed.

Usage

npm install @quadrel-enterprise-ui/icons
@use '@quadrel-enterprise-ui/icons/icon-font';   // @font-face Quadrel-Icon, font embedded as base64
import { quadrelIcons, quadrelIconNames, type QuadrelIconName } from '@quadrel-enterprise-ui/icons';

quadrelIcons;     // [{ name: ['trash', 'trashLight'], htmlEntity: '&#xe005' }, …]
quadrelIconNames; // ['trash', 'trashLight', …] — every primary name and alias
let icon: QuadrelIconName = 'trash'; // the union of every primary name and alias

QuadrelIconName lets you type an icon input against the font instead of against string. To keep accepting foreign names (your own glyphs, say), use QuadrelIconName | (string & {}).

Each entry maps one codepoint to its primary name and aliases. Render an icon by setting font-family: Quadrel-Icon and inserting the codepoint, e.g. content: "\e005".

The package contains icon-font.scss (package root, so a plain Sass load path on node_modules finds it) and the files from dist/: Quadrel.woff2 (also exported as @quadrel-enterprise-ui/icons/Quadrel.woff2 for consumers that serve the font themselves), the definitions as .mjs + .d.ts, and — for consumers that copy the file into their sources — icon-definitions.ts. The full list of icons with glyph previews is in ICONS.md. Versions follow Versioning: ^1.10.0 brings new icons automatically, never a removed one.

Prerequisites

Node 22 and npm — nothing else. All font tooling comes as npm packages; neither icomoon nor any local font software is needed.

npm ci --ignore-scripts

--ignore-scripts is not a detail: without it, ttf2woff2 builds its native addon via node-gyp — over a minute, and it needs a compiler and Python. The build only ever uses the bundled wasm binary anyway, which is what keeps it reproducible (see Build).

Preparing SVGs takes a vector editor, in practice Illustrator or Figma — see Requirements for delivered SVGs.

After every npm install that changes package-lock.json: npm run lock:public. A workstation with ~/.npmrc → repo.bit.admin.ch writes Nexus URLs into the lockfile, and the release workflow outside the BAZG network cannot install from those. CI rejects such a lockfile.

Adding an icon

cp newIcon.svg import/goodsReturn.svg   # file name = icon name, camelCase
npm run add
git add -A && git commit

add validates, normalizes, assigns the next codepoint, builds the font and empties import/. Several files at once are fine — they are processed alphabetically so the codepoint assignment is reproducible.

Aliases are added by hand in icons.json afterwards, followed by a rebuild.

Replacing an icon

cp newVersion.svg update/trash.svg        # file name = existing icon name
npm run update
git add -A && git commit

update mirrors add: the name must already exist. Codepoint and aliases stay untouched, icons.json is not modified — that is the only way consumers already shipped keep the same glyph at the same position.

The separate folder is deliberate. Overwriting an existing icon is a conscious act and must not happen because someone accidentally drops a file under a taken name into import/. Both commands point to the other one when a file lands in the wrong folder.

Check the glyphs visually after an update before committing — the build validates structure, not looks.

Without a local setup: upload the file

On GitHub open the folder import/ (for a replacement: update/), Add file → Upload files, drop the SVG in — file name = icon name, camelCase. When committing choose "Create a new branch for this commit and start a pull request"; committing straight to main is not possible anyway.

In the pull request CI runs npm run add or npm run update as a dry run. Green: the file is fine, codepoint and new version are in the run summary, the normalized drawing is attached as the artifact glyphen. Red: the reason is in the summary, remedies under Requirements for delivered SVGs — replace the file under the same name in the PR.

After the merge the workflow Icons verarbeiten processes the file on main: the same command, this time with a commit — icons/, icons.json, ICONS.md, version. The PR itself only carries the raw SVG. That is why two open upload PRs can never collide: codepoints are assigned on main only, and there one after the other.

If processing on main fails (red run in the Actions tab, reason in the summary), the file stays in import/. Put the corrected file under the same name in a new PR — the merge triggers processing again.

Discarding a run

As long as nothing is committed, an add, update or delete can be taken back completely:

git checkout -- icons icons.json ICONS.md originals package.json package-lock.json
git clean -fdq icons originals import update
npm run build

checkout restores modified files (including the bumped version), clean removes the newly created ones in icons/ and originals/. The final build restores dist/ and ICONS.md.

The paths are listed one by one on purpose: git checkout -- . would also discard unrelated changes in the working tree.

Contents

| | | |---|---| | import/ | Inbox for new SVGs. Normally empty. | | update/ | Inbox for replacements of existing icons. Normally empty. | | originals/ | The raw file as delivered — before normalization. For further editing. | | icons/ | Normalized source: one <path>, viewBox="0 0 1024 1024". | | icons.json | Source of truth: names, aliases, codepoints. | | ICONS.md | Generated table: glyph, codepoint, name, aliases. | | dist/, icon-font.scss | Contents of the npm package: Quadrel.woff2, icon-definitions.ts / .mjs / .d.ts, and icon-font.scss at the package root. Not committed (.gitignore) — npm run build creates it, publish.yml publishes it. | | scripts/ | build.mjs, add.mjs, update.mjs, delete.mjs, normalize.mjs, ingest.mjs, release-check.mjs. |

ICONS.md, dist/ and icon-font.scss are generated — do not edit by hand. ICONS.md is committed (the table renders on GitHub) and CI checks it for drift; dist/ is not.

The set only changes on purpose

No script touches an existing icon unless explicitly asked to via update/. In particular there is no bulk run over icons/ — not even when the normalizer improves later.

Reason: the glyphs are in production through the framework. An icon that looks right today must not change just because a library rounds differently.

That is why the name check is strict in both directions: add aborts when the name is taken, update aborts when it is not — each against primary names, against aliases, and regardless of case.

Deleting an icon

npm run delete -- goodsReturn
git add -A && git commit

Removes the entry, icons/ and originals/, rebuilds. Several names at once are fine.

The codepoint is never reused — nextCodepoint stays where it is, the counter only grows. If a freed codepoint were handed out again, every consumer that has not caught up yet would silently show a different icon.

Aliases cannot be deleted this way: they are not icons of their own, delete points to the primary name. An alias is removed by hand in icons.json.

Whether a name is still used in the framework is something this repo cannot check — delete shows beforehand what disappears.

Versioning

The version in package.json describes the icon set from a consumer's point of view. The scripts set it themselves — nobody has to remember:

| Command | Bump | Reason | |---|---|---| | npm run add | minor | new name, new codepoint | | npm run update | patch | same names and codepoints, different drawing | | npm run delete | major | a name disappears — consumers using it break |

The bump happens before the build so the stamp in dist/ carries the new number. A run with several files bumps once. npm run build on its own never changes the version, and a failed build rolls it back. Every merge with a new number is a release (see Release); earlier states are git tags v<version> and versions on npm, nothing is archived in the repo.

Release

When a version lands on main that npmjs.org does not have yet, the workflow Release (publish.yml) publishes the contents of dist/ as @quadrel-enterprise-ui/icons, sets the git tag v<version> and creates a GitHub release. A merge with a version bump is a release — there is no separate step. Re-running is safe: what is already published is skipped.

What a version changes in the set is determined by scripts/release-check.mjs against the latest release tag v<version> — publish.yml sets it only after a successful publish, so it is the same state consumers get from npm, just without the network. Because dist/ is not committed, the script rebuilds the tagged state from its sources (git archive + build.mjs with QD_ICONS_ROOT); the build is byte-reproducible, so the result is exactly the package published back then. It reports: added, removed, moved, redrawn (drawings compared by the glyph outlines in the font, not by the SVGs). From that follows the required bump: removed or moved name → major, new name → minor, new drawing → patch.

The gate for it runs in the pull request's CI — if the bump is smaller than required, the PR is red and main never gets it. At publish time the same comparison only produces the list for the release notes on the tag (--no-gate).

Provenance is off: npm issues it only for public source repositories, this one is internal.

Authentication via trusted publishing (OIDC), as for the framework: no token in the repo, the package is bound to this workflow on npmjs.org. dist/ is built during the run; ICONS.md and icons.json must match it (the same drift check as in CI).

The package carries this README; publish.yml strips scripts and devDependencies from the package.json in the tarball so that consumers see metadata and exports only.

Requirements for delivered SVGs

The usual source is IBM Carbon, often adjusted in Illustrator. Normalization cleans up on its own: <style>/classes, transforms, <rect>/<circle>/<polygon>, scaling to 1024, Illustrator <switch> wrappers. It merges several paths as long as they share the same winding.

Rejected is whatever cannot be resolved unambiguously:

| Abort reason | Remedy | |---|---| | not a valid icon name | rename the file to camelCase — eventLocation.svg, not event--location.svg | | a real stroke | convert to paths in Illustrator | | fill-rule="evenodd" | redraw as a compound path with nonzero winding | | yields several paths instead of one | the paths have different windings — unite them into one compound path in Illustrator | | missing or non-square viewBox | make the artboard square | | yields no path | everything was dropped as invisible — usually fill="none" on the <svg> element, as Figma exports it: remove the attribute | | name already taken | choose another name — see above |

The two attribute cases are deceptive because the file looks perfectly normal in the editor: a fill="none" on the root element counts as "paints nothing" for the normalizer and takes the whole drawing with it, fill-rule="evenodd" prevents the paths from being united. Figma ships both regularly, and both can usually be removed safely — but check the holes afterwards, because the font renders nonzero.

The scale factor is computed from the viewBox, not assumed. A 24×24 file ends up correctly at 1024.

Build

npm run build

The build is a pure function: icons.json + icons/ → dist/ + icon-font.scss + ICONS.md. It never writes to icons/ or icons.json. dist/ is not committed — whoever needs it builds it; ICONS.md is, so the table renders on GitHub.

It is byte-reproducible — building twice yields identical bytes. Two things are pinned for that: svg2ttf gets ts: 0 (otherwise the build time lands in the head table), and ttf2woff2 is pinned to its bundled wasm binary (otherwise the result would depend on whether the native build succeeded during npm ci). The drift check in CI relies on this.

Since the native addon is never loaded anyway, CI installs with npm ci --ignore-scripts — the same switch locally, see Prerequisites.

dist/

icon-font.scss                the package contents — the version from package.json
dist/Quadrel.woff2
dist/icon-definitions.ts
dist/icon-definitions.mjs
dist/icon-definitions.d.ts

Not committed, no archive. Earlier states live on npm (npm pack @quadrel-enterprise-ui/[email protected]) and as git tag v<version>, from which the state can be rebuilt at any time — exactly what release-check.mjs does for its comparison.

Every generated file carries the version in its first line. A hand-copied file in a consumer shows at a glance which state it is on.

Validation

npm run build aborts on: a missing SVG, an orphaned SVG without an entry, a duplicate codepoint, a codepoint ≥ nextCodepoint, a duplicate name or alias (case-insensitive too), a violated invariant in icons/ (viewBox, stroke, <style>/class, exactly one <path>), an empty glyph, a full-square glyph.

CI additionally checks that ICONS.md is committed up to date and that the lockfile points to npmjs.org. In pull requests it compares the set with the last release and checks the version bump (see Release), and runs add/update as a dry run for files in import/ and update/ (see Without a local setup).

Tests & lint

npm test         # vitest run --coverage
npm run lint     # eslint .

normalize.mjs is pure and unit-tested directly. add/update/delete/build run as CLIs with process.exit and start the build as a subprocess — they are tested behaviourally against a throwaway repo.

For that, build.mjs and ingest.mjs understand the environment variable QD_ICONS_ROOT: when set, it redirects only the data (icons.json, icons/, dist/, import/, update/) to that directory — the code stays in the repo. Without it the scripts work on the repo root as before; CI never sets it.

Font metrics

Em 1024, ascent 960, descent 64 — matching the previous icomoon font so rendering in the framework stays unchanged.

License

Apache-2.0. The icons are derived from IBM Carbon (Apache-2.0) — see NOTICE and LICENSE.