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

@stealthscale/component-feedback

v0.1.0

Published

Draws the system reporting on itself: what is loading, what went wrong, what state a thing is in.

Downloads

107

Readme

@stealthscale/component-feedback

Draws the system reporting on itself: what is loading, what went wrong, what state a thing is in. Every component binds a recipe and draws nothing of its own, so a theme restyles all of them by extending the recipe. The preset under ./theme registers the recipes with an application's compiler.

Every value a theme can change on a component is an axis of its recipe, so a caller sets it as a prop and writes no style. A caller changes the element a component draws with as.

Install

pnpm add @stealthscale/component-feedback

The package peers on react and @stealthscale/theme. An application lists the preset under ./theme among the presets its compiler installs.

Skeleton

Stands in for content that has not arrived. It wraps that content rather than replacing it, so a caller writes one tree and sets one prop, and the stand-in comes out the size of the thing it stands in for without anybody stating a width. While it waits it hides everything inside it. Once the content arrives the stand-in fades out and the content is shown.

import { Skeleton } from "@stealthscale/component-feedback";

<Skeleton loading={pending}>
  <Avatar src={person.photo} />
</Skeleton>;
<Skeleton motion="shimmer" radius="full" />;

| Axis | Values | Default | | --------- | -------------------------- | ------- | | loading | true, false | true | | motion | none, pulse, shimmer | pulse | | radius | l1, l2, l3, full | l2 |

The element carries no role. A reader learns more from being told the region is busy than from being told each box is, so a page states aria-busy on whatever is waiting and each bar announces nothing of its own.

SkeletonText

Stands in for a paragraph of text. Every length is read off the line it stands in for, so a bar is one line tall and the space between two is half a line. Place it where text is read and it comes out the size of that text, so the page does not jump when the real words replace it.

import { SkeletonText } from "@stealthscale/component-feedback";

<SkeletonText />;
<SkeletonText lines={5} motion="shimmer" />;

The last bar of several is short, because a paragraph rarely fills its final line and a block of bars all one width reads as a table rather than as writing. lines defaults to three and never draws fewer than one. Draw this while the text is loading and draw the text itself once it arrives, so nothing here takes a loading state.

EmptyState

Draws the panel a page shows where there is nothing to show, composed as EmptyState.Root holding a mark, a heading and a line saying what would be here.

import { EmptyState } from "@stealthscale/component-feedback";

<EmptyState.Root size="lg">
  <EmptyState.Content>
    <EmptyState.Indicator aria-hidden>
      <InboxIcon />
    </EmptyState.Indicator>
    <EmptyState.Title>No invoices yet</EmptyState.Title>
    <EmptyState.Description>Send your first one to get started.</EmptyState.Description>
  </EmptyState.Content>
</EmptyState.Root>;

| Axis | Values | Default | | ------ | ------------------------------------------------- | ------- | | size | xs, sm, md, lg, xl, 2xl, 3xl, 4xl | md |

One axis sets four parts together. The room inside the panel, the gap in the content, the box of the mark and the size of the title each read the scale of that name, so a small panel in a sidebar and a large one filling a page are one name apart. The description holds its size, because a line of explanation is read at the size the rest of the page is read at however big the panel is.

The mark fills whatever box its size states, so a caller passes a glyph without sizing it. Hide it with aria-hidden, since the title beneath says the same thing in words. The title is an h2, which suits a panel standing in for a section's content. A page whose outline puts it deeper states its own level with as.

The root carries no role. A page of empty panels would otherwise be a page of landmarks.

Licence

MIT. See LICENSE.