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

@asilluron/horizon

v0.3.0

Published

Embeddable React visual roadmap for Linear data — a flight plan, not a Gantt chart.

Readme

Horizon

A stunning, embeddable visual roadmap for Linear data — a flight plan, not a Gantt chart.

Horizon is a read-only React component that renders Linear Initiatives, Projects, and Milestones as a horizontally scrolling timeline, centered on today. It is built for non-technical audiences — clients, stakeholders, leadership — who need to answer two questions at a glance: how far along is this feature? and how close are we to the release?

All editing happens in Linear. Horizon never writes back.

Features

  • Now beam — a soft vertical beam of light marks today; the timeline opens centered on current work (40% past / 60% future)
  • Three-state visual grammar — solid past, filling present, diagonal-hatched future
  • Free scrolling — trackpad, drag-to-pan, shift+wheel, arrow keys, with a one-click "Back to now" pill
  • Three zoom levels — Month / Quarter / Year, cursor-anchored (ctrl/⌘+wheel or +/-)
  • Plain-language everywhereDone, In progress, Up next · On track, At risk, Off track · "Mid-July", never ISO strings
  • Release mode — header band with scope-weighted progress, "9 of 14 features complete", a pace indicator, and a flag line on the timeline
  • Open-ended mode — no release configured? Rolling stats instead ("3 features shipped this quarter · 5 in progress"); a first-class mode, not a broken state
  • Drill-in side panel — status, health, lead, dates, progress ring, milestones, and an optional "What's inside" issue list
  • Initiative drill-in — click an initiative name to open a full issue waterfall: start→completion bars per issue, grouped by project, with dependency arrows between blocked issues and a Now line
  • Not-yet-scheduled tray — undated projects stay visible, but honest about being undated
  • Detail mode — milestone diamonds and issue lists for internal team reviews, off by default
  • Light & dark themes, tablet-friendly, keyboard-navigable, prefers-reduced-motion aware
  • Mobile fallback — below 768px the timeline becomes a vertical now-centered list

Install

npm install @asilluron/horizon

React 18+ is the only peer dependency. No runtime dependencies.

Quick start

import { HorizonRoadmap } from "@asilluron/horizon";
import "@asilluron/horizon/styles.css";

<HorizonRoadmap data={data} height={560} />;

data is a plain JSON document (HorizonData): initiatives, projects, an optional release, and a lastSyncedAt timestamp. See src/types.ts for the full contract.

<HorizonRoadmap
  data={data}
  theme="dark"             // "light" | "dark" | "auto"
  defaultZoom="quarter"    // "month" | "quarter" | "year"
  defaultDetailMode        // milestone diamonds + issue lists
  showLinearLinks          // "View in Linear" for authenticated internal users
  syncUrl                  // encode scroll, zoom + open drill-ins in the URL; Back closes drill-ins
  loading={isLoading}      // skeleton timeline with the Now beam in place
/>

Getting data out of Linear

Horizon deliberately ships no Linear API client. Sync server-side so your Linear API key never reaches the browser, cache the result (Postgres, Redis, a JSON file — anything), and hand the document to the component. The mapLinearData adapter converts raw Linear shapes into HorizonData:

// server-side, e.g. a cron job or API route
import { mapLinearData } from "@asilluron/horizon";

const data = mapLinearData(rawInitiatives, rawProjects, {
  release: { name: "Product Launch 2.0", initiativeId: "...", targetDate: "2026-09-01" },
  lastSyncedAt: new Date().toISOString(),
});

A release can be derived three ways, in order of preference: an Initiative is the release, a project label (e.g. release:2.0) selects the set, or a manual list of project ids. Omit it entirely for open-ended mode.

To power the initiative drill-in view, include each project's issues in your sync query — issues { id identifier title state { type name } startedAt completedAt relations { nodes { type relatedIssue { id } } } } — and pass them on RawLinearProject.issues. The adapter maps Linear state types to plain language and inverts blocks relations into the arrows you see on screen. In-progress issues whose state name contains "QA" get a purple bar. Omit issues and the roadmap behaves exactly as before.

Recommended sync cadence: poll every ~10 minutes as a consistency backstop, with Linear webhooks (Project, Initiative, ProjectMilestone) triggering targeted refreshes in between. Viewer traffic should never touch Linear's API directly.

Progress semantics

  • A project's number comes from Linear's own progress field by default — one number, no arguments
  • 100% requires the project status to be Done; all-issues-done-but-not-called-done caps at 99%
  • A started project at 0% renders a minimum sliver, so In progress never looks like Not started
  • Release progress is the scope-weighted mean of member projects (issue-estimate totals → issue counts → equal), with the weighting basis disclosed on hover

Design tokens

The "night flight" palette and type system are CSS custom properties on .hz-root — override them to re-brand. Load Archivo, Inter, and IBM Plex Mono in your host app for the intended look; system fallbacks apply otherwise.

| Token | Default | Role | |---|---|---| | --hz-horizon | #3D6B8E | Now beam, links, focus | | --hz-ember | #D98E4A | progress fill (the warmest thing in view) | | --hz-green / --hz-amber / --hz-red | | health signals | | --hz-mist | #8A94A0 | gridlines, secondary text |

Linear's project color is used only as a 3px edge stripe on bars — brand consistency beats the rainbow.

Accessibility

Status and health are always encoded in text + shape, never color alone. The timeline is keyboard-navigable (←/→ pan, +/− zoom, Tab through bars, Enter opens the panel, Home returns to now). Focus is visible throughout, and all motion collapses to instant under prefers-reduced-motion.

Development

npm install
npm run dev        # demo app with sample data
npm test           # unit tests (vitest)
npm run typecheck
npm run build      # esm + cjs + d.ts via tsup

License

MIT © Andrew Silluron