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

@patternmode/thumbnail

v0.1.2

Published

A framed image — an inset hairline and an optional whisper of lift, so a photograph reads as a window rather than as a form field containing one.

Readme

@patternmode/thumbnail

A framed image — an inset hairline and an optional whisper of lift.

A photograph with an edge drawn inside it reads as a window onto the image. The same photograph inside an outer border reads as a form field containing one. That is the whole component, and it is the reason imagery in an application so often fails to look like imagery: it gets a border because everything else on the screen has one.

npm install @patternmode/thumbnail
import { Thumbnail } from "@patternmode/thumbnail";
import "@patternmode/thumbnail/styles.css";

<Thumbnail src={thumbHref} alt="" size={32} />;

Requires Tailwind CSS v4.

What it already handles

So you don't rebuild something that is one import away:

| | | | ------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | The edge holds | Painted by an overlay pseudo-element, never box-shadow: inset on the frame. An inset shadow is drawn behind the frame's children, so a picture that fills the box hides it — correct in a skeleton, gone in production. | | Any image | Pass children and the frame clips and edges whatever it is given: a framework <Image>, a <video>, a <canvas>. It does not wrap, clone or intercept props. | | Dark mode | The edge inverts to a highlight, because on a dark surface that is what reads as a shadow falling inside an opening. Black-on-dark is invisible. | | No baseline gap | The media is display: block, so the frame does not measure 4px taller at the bottom than it does anywhere else. | | Clipping | overflow: hidden, so the radius reaches the picture rather than curving the hairline around square corners. |

Sizing

Square at size by default — a thumbnail is a stamp of fixed footprint, and a picture that sizes itself is what makes a list ragged.

size={null} hands sizing back to the caller, for the one case that needs it: a preview whose height follows the photograph's own aspect ratio.

<Thumbnail size={null} fit="contain">
  <Image src={previewHref} alt={caption} width={640} height={640} />
</Thumbnail>

Props

| Prop | Type | Default | | | ---------- | -------------------------- | --------- | ------------------------------------------------------------------------------- | | src | string | — | Renders a plain <img>. Omit and pass children instead. | | alt | string | "" | Empty is often correct — if the row already names the thing, a repeat is noise. | | children | ReactNode | — | Your own media element. Wins over src. | | size | number \| string \| null | "2rem" | Numbers are pixels. null hands sizing back. | | radius | number \| string | 3px | | | fit | "cover" \| "contain" | "cover" | | | position | string | center | object-position, for a crop that is not from the centre. | | raised | boolean | false | One tight downward layer. Not a card shadow. |

Theming

Every value is a custom property, so nothing here needs forking:

| Property | Default | | -------------------------------- | -------------------------------------------------- | | --patternmode-thumbnail-frame | rgb(0 0 0 / 0.1), rgb(255 255 255 / 0.12) dark | | --patternmode-thumbnail-lift | 0 1px 2px -1px rgb(0 0 0 / 0.12) | | --patternmode-thumbnail-radius | 3px | | --patternmode-thumbnail-size | 2rem |

The frame colour is a transparent black rather than a border token on purpose. It sits over whatever the photograph happens to be, and a solid colour either disappears against a dark image or cuts a hard line across a pale one. A translucent edge darkens whatever is underneath it by the same proportion everywhere, which is why one value holds across a grid of unrelated pictures.

The dark default rides prefers-color-scheme rather than light-dark(), since light-dark() silently resolves to light unless the host has declared color-scheme and a package cannot assume that. If your theme is class-based, set --patternmode-thumbnail-frame on your own selector — that beats the media query cleanly, with neither fighting the other.

.dark {
  --patternmode-thumbnail-frame: rgb(255 255 255 / 0.12);
}

When not to use it

On an image that is the subject of its own screen. A hero photograph does not want a frame; it wants the whole box. This is for pictures that live in something — a row, a grid, a card, a picker.

License

MIT