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

@whereq/react

v0.7.0

Published

WhereQ's open-source React component library. Lightweight, accessible, themeable UI primitives — starting with a beautiful thin Scrollbar.

Readme

@whereq/react

WhereQ's open-source React component library. Lightweight, accessible, themeable UI primitives — a thin Scrollbar, a generic Avatar, an inline-SVG CountryFlag, a multi-variant Tag, and a portal-style Toast.

npm version npm downloads bundle size license types CI

📖 Docs · 🧪 Storybook


The Scrollbar was extracted from whereq.cc, our privacy-first photo gallery. It styles the native scrollbar — no scroll hijacking — so wheel, keyboard, touch, and accessibility all keep working, with zero runtime scroll math.

  • 🪶 Tiny & dependency-free — one <div>, native scrollbars, a few bytes of CSS.
  • 🎨 Themeable — light/dark/auto presets, or full control via colour props / CSS variables.
  • 🌗 Auto light-dark — the default adapts to the page's color-scheme.
  • 🙈 Auto-hide — optionally reveal the thumb only on hover / while scrolling.
  • ♿ Accessible & native — real scrollbars, real keyboard & screen-reader behaviour.
  • 🧩 SSR-safe & RSC-ready — ships a "use client" boundary; styles inject before paint.
  • 🔔 Toasts included — built-in ToastProvider + imperative toast.error("…") API with 4 variants and 6 positions.
  • 🔠 First-class TypeScript — full types, ESM + CJS.

Install

npm install @whereq/react
# or
pnpm add @whereq/react
# or
yarn add @whereq/react

react >= 18 is a peer dependency.

Usage

<Scrollbar> — a scoped scroll container

import { Scrollbar } from '@whereq/react'

export function Sidebar() {
  return (
    <Scrollbar style={{ maxHeight: 320 }}>
      {/* any tall content */}
      <ul>{/* … */}</ul>
    </Scrollbar>
  )
}

Auto-hide + a thicker, rounded thumb, adapting to light/dark:

<Scrollbar autoHide size={8} theme="auto" style={{ maxHeight: '70vh' }}>
  <Content />
</Scrollbar>

Horizontal, with custom colours:

<Scrollbar axis="horizontal" thumbColor="#17a2ae" thumbHoverColor="#0f7f89">
  <Row />
</Scrollbar>

<GlobalScrollbar> — style every scrollbar on the page

Reproduces the whereq.cc app-wide look. Mount it once near your root; it renders nothing.

import { GlobalScrollbar } from '@whereq/react'

export function App() {
  return (
    <>
      <GlobalScrollbar theme="auto" size={6} />
      <Routes />
    </>
  )
}

Prefer an imperative call (e.g. outside React)? Use injectGlobalScrollbarStyles:

import { injectGlobalScrollbarStyles } from '@whereq/react'

const cleanup = injectGlobalScrollbarStyles({ theme: 'dark', size: 6 })
// cleanup() removes the styles

<Avatar> — image, with a graceful initials fallback

import { Avatar, AvatarGroup } from '@whereq/react'

// Image; falls back to initials on a name-derived colour if it 404s.
;<Avatar src="/users/ada.jpg" name="Ada Lovelace" status="online" />

// No image → deterministic initials chip.
;<Avatar name="Grace Hopper" />

// Head-crop a portrait so the face fills the circle, with a ring.
;<Avatar src="/mascot.png" name="Q" position="top" ring="#e4e8ee" ringWidth={2} />

// Overlapping stack with a +N overflow chip.
;<AvatarGroup size={32} max={3}>
  <Avatar src="/a.jpg" name="Ada" />
  <Avatar src="/b.jpg" name="Ben" />
  <Avatar name="Cai" />
  <Avatar name="Dee" />
</AvatarGroup>

API

<Scrollbar> props

Extends React.HTMLAttributes<HTMLDivElement> (so className, style, onScroll, ref, … all work).

| Prop | Type | Default | Description | | ----------------- | --------------------------------------------- | ------------ | ----------------------------------------------------------------- | | axis | 'vertical' \| 'horizontal' \| 'both' | 'vertical' | Which axis may scroll. | | size | number | 6 | Thumb/track thickness in px. | | radius | number | size / 2 | Thumb corner radius in px. | | theme | 'auto' \| 'dark' \| 'light' \| 'neutral' | 'auto' | Colour preset (see Theming). | | thumbColor | string | theme value | Override the thumb colour. | | thumbHoverColor | string | theme value | Override the thumb hover colour. | | trackColor | string | transparent| Override the track colour. | | autoHide | boolean | false | Reveal the thumb only on hover / while scrolling (webkit). | | autoHideDelay | number | 1000 | ms the thumb stays after scrolling stops. | | firefoxWidth | 'auto' \| 'thin' \| 'none' | 'thin' | Firefox scrollbar-width keyword. |

<Avatar> props

Extends React.HTMLAttributes<HTMLSpanElement> (minus color). Full table in the Avatar docs.

| Prop | Type | Default | Description | | ----------- | ------------------------------------------- | ---------- | ------------------------------------------------------- | | src | string | — | Image URL; falls back to initials if missing / errors. | | name | string | — | Drives auto-initials, the colour and the a11y label. | | size | number | 40 | Diameter in px. | | shape | 'circle' \| 'rounded' \| 'square' | 'circle' | Outline shape. | | position | CSSProperties['objectPosition'] | 'center' | Image object-position — use 'top' to head-crop. | | ring | string | — | Inset ring colour (ringWidth px, default 1). | | status | 'online' \| 'offline' \| 'busy' \| 'away' | — | Presence dot in the corner. |

<AvatarGroup> overlaps children (size, max, spacing, ring, shape).

Exports

  • Scrollbar, GlobalScrollbar
  • injectGlobalScrollbarStyles(options) → cleanup fn
  • buildGlobalScrollbarCss(options) → CSS string (for SSR / manual <style>)
  • ensureScrollbarStyles(), resolveColors(theme, overrides), scrollbarCss, SCROLLBAR_CLASS
  • Avatar, AvatarGroup
  • initialsFromName(name, max?), colorFromName(name, palette?), radiusFor(shape, size), AVATAR_PALETTE, STATUS_COLORS
  • CountryFlag — props: country (ISO 3166-1 alpha-2), size, shape, fit, className, alt
  • lookupFlag(slug), isSupported(slug), FLAG_CATALOG, SUPPORTED_SLUGS, FLAG_VIEWBOX, star5(cx, cy, r)
  • Tag, TagImpl<T> — props: variant (subtle/solid/dot/link), size (sm/md), as (span/a/button/div), solidColor, dotColor, className, children
  • tagClasses(variant?, size?, dotColor?, solidColor?) — pure helper
  • toast (imperative handle), ToastProvider, ToastViewport, useToast / useToastContext
  • Types: ScrollbarProps, ScrollbarAxis, ScrollbarTheme, ScrollbarColors, ScrollbarStyleOptions, FirefoxScrollbarWidth, GlobalScrollbarOptions, AvatarProps, AvatarGroupProps, AvatarShape, AvatarStatus, CountryFlagProps, CountryFlagShape, CountryFlagFit, CountrySlug, TagProps, TagPropsDefault, TagVariant, TagSize, TagAs, ToastApi, ToastItem, ToastOptions, ToastPosition, ToastVariant, ToastViewportProps, ToastContextValue

Theming

Colours are driven by CSS custom properties, so you can theme with props or with plain CSS.

  • theme="auto" (default) — uses the CSS light-dark() function and matches whereq.cc exactly on both schemes. For it to adapt, declare a color-scheme on your page (e.g. :root { color-scheme: light dark }). If none is declared it falls back to the light value.
  • theme="dark" | "light" — explicit whereq.cc palette values.
  • theme="neutral" — a translucent grey that's visible on any background, even without color-scheme.

Override individual tokens per instance:

<Scrollbar thumbColor="rgba(23,162,174,.5)" thumbHoverColor="rgba(23,162,174,.8)" />

…or globally with CSS (the component reads these variables):

.wq-scrollbar {
  --wq-sb-thumb: rgba(255, 255, 255, 0.1);
  --wq-sb-thumb-hover: rgba(255, 255, 255, 0.2);
  --wq-sb-size: 6px;
}

Browser support

Native scrollbar styling: Chromium/Safari via ::-webkit-scrollbar, Firefox via scrollbar-width + scrollbar-color. autoHide's per-state thumb reveal is a webkit enhancement; Firefox shows the thin scrollbar consistently.

Docs & playground

  • 📖 Docs — https://whereq.github.io/whereq-react/
  • 🧪 Storybook — https://whereq.github.io/whereq-react/storybook/ (pnpm storybook locally)
  • 🖼️ A zero-build demo lives at examples/demo.html.

Contributing & releasing

Issues and PRs welcome — see CONTRIBUTING.md. We ship from a dev → main → tag pipeline; see RELEASING.md. This is the first component in WhereQ's frontend library; more primitives are on the way.

License

MIT © WhereQ