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

cancado-ios4

v0.0.1

Published

An iOS 4 (2010) component kit for React — springboard, app chrome, navigation and the mobile-first primitives to build screens inside them

Readme

cancado-ios4

An iOS 4 (2010) component kit for React — a full device shell (lock screen, springboard, app chrome, navigation stack, multitasking tray) plus the mobile-first primitives to build screens that live inside it.

Skeuomorphic on purpose: glossy icons, pinstriped table backgrounds, the blue-grey navigation bar, slide-to-unlock.

pnpm add cancado-ios4

Quick start

import { IosShell, ScrollView, TableView, TableSection, TableRow } from 'cancado-ios4';
import type { IosApp } from 'cancado-ios4';

const apps: IosApp[] = [
  {
    id: 'settings',
    title: 'Settings',
    icon: '⚙️',
    iconBackground: 'linear-gradient(to bottom, #b8bec7 0%, #6b7480 100%)',
    render: ({ push }) => (
      <ScrollView grouped>
        <TableView>
          <TableSection header="General">
            <TableRow
              title="About"
              onPress={() => push({ title: 'About', render: () => <p>iOS 4.3.5</p> })}
            />
          </TableSection>
        </TableView>
      </ScrollView>
    ),
  },
  { id: 'phone', title: 'Phone', icon: '📞', dock: true, render: () => null },
];

export function App() {
  return <IosShell apps={apps} />;
}

One registry drives everything: the springboard icon, the dock, the navigation title and the screen content all come from the same IosApp.

Mobile-first

The shell fills whatever box it's given and defaults to the viewport — a real phone gets the real UI, not a picture of a phone. Everything else follows:

  • Touch targets are 44pt minimum, the iOS floor
  • Gestures are pointer-events based, so they work with touch, mouse and stylus
  • touch-action, -webkit-overflow-scrolling and overscroll-behavior are set so flicks scroll the right thing and never rubber-band the page behind
  • env(safe-area-inset-*) is respected when installed as a web app
  • Tap highlight and text-size inflation are disabled

Pass bezel to draw a phone frame instead — that's for showing the UI on a desktop page, not for shipping to phones.

Architecture

The package is layered, and each layer is importable on its own.

| Layer | Path | What it is | | --- | --- | --- | | core/ | cancado-ios4/core | Pure TypeScript: the device reducer, springboard layout maths, the app-registry model. No React, no DOM, no CSS. | | hooks/ | cancado-ios4/hooks | React bindings: useIosRuntime, usePointerDrag, useLongPress, useDismiss, useElementSize, useClock. | | primitives/ | cancado-ios4/primitives | Buttons, table views, switches, segmented controls, fields, indicators. | | components/ | cancado-ios4/IosShell, /Springboard, … | The shell pieces. Every one is presentational and controlled. |

State transitions live in core as a pure reducer, so device behaviour is exercisable without rendering anything.

The runtime

useIosRuntime() is usable on its own if you want the behaviour without the chrome:

import { useIosRuntime } from 'cancado-ios4/runtime';

const rt = useIosRuntime();
rt.launch('music', 'songs');
rt.push({ title: 'Now Playing', render: () => <Player /> });
rt.openSwitcher();

Pass it to <IosShell runtime={rt} /> to drive the device from outside — a router, a demo script, a test.

The reducer is exported too:

import { runtimeReducer, createRuntimeState } from 'cancado-ios4/core';

const state = runtimeReducer(createRuntimeState(), {
  type: 'launch',
  appId: 'settings',
});

It models iOS 4 multitasking faithfully: launching an app doesn't destroy the previous one, going Home leaves apps running with their navigation stacks intact, and only the tray actually terminates an app.

Talking to the device from inside a screen

Every render receives a context:

render: ({ push, pop, selectTab, openApp, home, params }) => (
  <TableRow
    title="Wi-Fi"
    onPress={() => push({ title: 'Wi-Fi', backTitle: 'Settings', render: WifiScreen })}
  />
)

Anywhere deeper in the tree, useDevice() gives the same handles.

Components

| Component | Role | | --- | --- | | IosShell | The whole device — composes everything below | | LockScreen | Wallpaper, clock, working slide-to-unlock | | Springboard | Paged icon grid, page dots, dock, drag-to-rearrange | | AppIcon | Rounded tile with baked-in gloss, badge, jiggle | | AppScreen | An app's chrome: nav bar, content, toolbar, tab bar | | NavigationBar / BarButton | The 44pt bar and its pointed back button | | TabBar | The 49pt dark tab bar | | StatusBar | Signal, carrier, live clock, battery | | MultitaskingTray | Double-press Home — the recent-apps tray |

Gestures

Three gestures share the springboard without fighting:

  • Swipe pages the grid — active only when not rearranging
  • Long press enters rearrange mode — never captures the pointer and cancels on movement, so it coexists with the swipe
  • Drag reorders icons — active only while rearranging

Paging commits on either distance (25% of the screen) or flick velocity, whichever lands first, and rubber-bands at the ends.

Theming

Every colour and metric is a CSS custom property (see src/styles/tokens.css):

:root {
  --ios4-navbar-top: #c6a8b4;
  --ios4-navbar-bottom: #996d7f;
  --ios4-tint: #b8336a;
}

Styles

Component subpaths inject their own CSS automatically. For everything at once:

import 'cancado-ios4/styles';   // all components
import 'cancado-ios4/tokens';   // just the variables

Development

pnpm install
pnpm storybook     # component gallery + a live device
pnpm typecheck
pnpm build

License

MIT