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

safari-shot

v0.2.0

Published

Screenshots of web pages as iOS Safari shows them on iPhone, including what Safari paints under the status bar and the toolbar

Readme

safari-shot

CI npm

Screenshots of web pages as iOS 27 Safari shows them on iPhone — including what Safari paints under the status bar and the Liquid Glass toolbar — without a simulator. --ios 26 selects the iOS 26 glass; a simplified iOS 18 model (--ios 18, portrait) covers pre–Liquid Glass Safari. Runs anywhere Playwright runs (Linux, Docker, macOS) on Node.js 22.18 or later.

npx -y safari-shot https://example.com

The first run downloads Playwright's WebKit build (about 100 MB). On Linux, install its system libraries once with npx playwright install --with-deps webkit.

Playwright is a peer dependency, so installed beside your own Playwright it uses that one and its browsers: npm i -g playwright safari-shot && playwright install --with-deps webkit.

safari-shots/
  example-com/
    ios27/
      iphone-17-pro.portrait.light.initial.png  # 1206×2622, same framing as a Simulator screenshot
      iphone-17-pro.portrait.light.initial.json # why each panel looks the way it does
  manifest.json                                  # every shot of the run; also printed to stdout

Agent skill

The safari-shot skill teaches coding agents (Claude Code, Codex, Cursor and others) to run the CLI, read its reports and fix a page's panels:

npx skills add 0xBADC0FFEE/safari-shot --skill safari-shot

Add -g to install it for every project.

Usage

safari-shot <url...> [options]

| Option | Values | Default | |---|---|---| | -d, --device | preset ids, comma-separated, or all (--list-devices prints them) | iphone-17-pro | | --ios | 27, 26, 18, all (18: portrait only) | 27 | | -s, --state | initial, scrolled, overscroll-top, overscroll-bottom, all | scrolled with --scroll, else initial | | -t, --theme | light, dark, all | light | | -o, --orientation | portrait, landscape | portrait | | --mode | browser, standalone | browser | | --scroll | px scrolled in the scrolled state; implies it without --state | 400 | | --overscroll | pt of rubber-band in overscroll states | 150 | | --settle | ms to wait after load (capped at 10 s, then the capture goes ahead) | 500 | | --engine | webkit, chromium | webkit | | --output, --out | output directory | ./safari-shots |

Local files work too: safari-shot ./dist/index.html. From a checkout, put options after --: npm run shot -- https://example.com --scroll 200.

Exit codes: 0 success, 1 runtime error, 2 bad arguments.

States

  • initial — fresh load at the top; toolbar expanded.
  • scrolled — scrolled by --scroll; toolbar collapsed to the URL pill.
  • overscroll-top — pulled down past the top with the refresh control showing.
  • overscroll-bottom — pushed up past the end of the page.

Report

Each PNG has a JSON report next to it. The part agents usually need:

{
  "panels": {
    "top":    { "fill": "extension", "color": "#ff9800", "reason": "background-color of header.bar" },
    "bottom": { "fill": "content",   "color": "#ffffff", "reason": "no fixed/sticky container; content scrolls under the glass" },
    "statusBar": "dark-content",
    "overscrollColor": "#ff9800"
  },
  "underPageBackground": "#ffffff",
  "themeColor": { "color": "#d32f2f", "source": "<meta name=\"theme-color\" content=\"#d32f2f\">", "usedByPanels": false }
}
  • fill: "extension" — Safari paints a solid color under that bar (color), taken from a fixed or sticky element touching the edge. reason names the element and the rule that picked it.
  • fill: "content" — no extension: page content scrolls under the glass and fades into the scroll edge.
  • iOS 18 reports fill: "tint" for the solid status-bar band (theme-color, else the sampled page top, else the under-page color), "material" for the expanded toolbar and "bar" for the collapsed one.
  • fixedEdges.atLoad / fixedEdges.final — the raw per-side result of WebKit's fixedContainerEdges, before and after the scenario's scroll.

How it works

  1. Playwright (WebKit by default) loads the page with the layout viewport Safari would use (screen minus status bar and toolbar, measured on the iOS Simulator).
  2. A port of WebKit's LocalFrameView::fixedContainerEdges runs in the page and decides each edge color, including the history rules of Page::updateFixedContainerEdges across load and scroll.
  3. The page, the content above and below the layout viewport, and Safari's chrome are composed in a second browser page: color extensions, scroll edge effect, status bar, and glass toolbar.

The rules and where they come from are in docs/safari-rules.md.

Calibration (macOS with Xcode only)

calibration/ holds the reference corpus and the tooling that keeps the output matched to real Safari in the iOS Simulator. Agents using the CLI never need it.

node calibration/capture-references.ts --udid <simulator-udid>   # Safari screenshots of every corpus page
node calibration/compare.ts --ios 27 --sheets                     # re-render and compare panel regions
node calibration/fit-glass.ts --ios 27 --theme dark               # refit the glass bands of one appearance
node calibration/fit-glass.ts --ios 18                            # refit the iOS 18 toolbar material
node calibration/measure-devices.ts --types "iPhone 17,iPhone Air"