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

@nudeui/icons

v0.0.3

Published

CSS-only icon system: icons are design tokens, placed with a custom property, rendered with a mask

Readme

@nudeui/icons

A CSS-only icon system. Icons are design tokens (CSS variables); you place one on any element by setting a custom property, and CSS does the rest.

<button style="--icon: var(--icon-trash)">Delete</button>

No markup, no <svg>, no components. Icons follow the text color, scale with the font size, and can change in response to any CSS condition: :hover, media queries, container queries, dark mode, print, writing direction.

Read the rationale in The best way to use icons on the Web.

Installation

npm install @nudeui/icons
@import "@nudeui/icons";

or link path/to/@nudeui/icons/icons.css however your project links CSS.

Usage

1. Define icons as tokens

The package ships no icons. Define your own as --icon-* variables, embedded or as files:

:root {
	--icon-trash: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'><path d='…'/></svg>");
	--icon-link-external: url("/icons/link-external.svg");
}

Icons must be monochrome (varying opacity is fine), since they are applied as a mask.

2. Place icons

| Property | Renders in | Meaning | | -------------- | ----------- | ---------------------------------------- | | --icon-start | ::before | Icon before the element's content | | --icon-end | ::after | Icon after the element's content | | --icon | (component) | "Give this component an icon", see below |

Set them anywhere a declaration goes: a stylesheet, an inline style, a media query, a state:

button.delete {
	--icon-start: var(--icon-trash-outlined);

	&:hover {
		--icon-start: var(--icon-trash);
	}
}

a[href^="https://"] {
	@media print {
		--icon-end: var(--icon-link-external);
	}
}

Icons are 1em squares, so they follow font-size. Elements with no content (e.g. an icon-only button) get no gap between icon and text.

Styling icons

These are separate from the --icon-* tokens so they can never collide with an icon name. They inherit, so set them on any ancestor to style every icon inside it.

| Property | Default | Meaning | | ---------------------- | -------------- | ------------------------------------------ | | --icon-style-color | currentColor | Icon color | | --icon-style-size | 1em | Icon width and height | | --icon-style-spacing | 0.3em | Gap between icon and content | | --icon-style-voffset | -0.02em | Vertical nudge, positive moves the icon up |

Icons are centered on the font's cap height (why). Use --icon-style-voffset for optical corrections.

3. Let components own the location

--icon is the high-level property: it means "add an icon to this component". Each component redirects it to whichever location makes sense for it, so consumers never have to know:

:where(button) {
	--icon-start: var(--icon);
}

Then <button style="--icon: var(--icon-trash)">, button.delete { --icon: var(--icon-trash) } and so on just work. You can add your own state properties the same way, e.g. --icon-hover:

button:hover {
	--icon-start: var(--icon-hover, var(--icon));
}

Standalone icons

For an icon that is not attached to a component, use the .icon class, which redirects --icon to --icon-start on an empty element:

<i class="icon" style="--icon: var(--icon-trash)"></i>

Since the icon is a pseudo-element, the element itself can carry a background, border, padding, etc.

Without style queries

Icons are applied to any element that sets --icon-start / --icon-end, which relies on style queries. In browsers without them, icons are simply absent, which makes them a progressive enhancement.

Where an icon is essential, opt in explicitly with .icon, .icon-start or .icon-end. These work in any browser that supports masks. They only hardcode the location, not the icon: if the property is unset, no pseudo-element is generated.

Caveats

  • Relative URLs in icon tokens resolve relative to the stylesheet that uses the icon, not the one that defines it. Use root-relative or absolute URLs for icon files, embed them, or register them as an <image> property via @property which forces them to be resolved relative to the stylesheet that defines them.
  • Multi-color icons are not supported, since the icon is a mask.
  • Backgrounds or borders on the icon itself are not possible; put them on the element instead (see standalone icons).

Browser support

Everything is Baseline. Style queries are the newest requirement (Chrome 111, Safari 18, Firefox 2026). Safari 18+ needs workarounds for two WebKit bugs (#301609, #325900), which the stylesheet ships, scoped to WebKit and in their own cascade layers so your own declarations always win.

Development

The source is icons-mixins.css, written with CSS mixins (@mixin / @apply) so the icon styles are declared once. No browser applies them natively yet, so icons.css is transpiled from it with @csstools/postcss-mixins and committed.

npm run build   # icons-mixins.css → icons.css
npm run dev     # rebuild on change

To check rendering, serve the repo root (e.g. npx serve) and open test/index.html. Append ?native to load the untranspiled source instead, for testing native mixin support.