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

@m1kapp/ui

v0.1.16

Published

Mobile-first app shell UI components for side projects

Readme

@m1kapp/ui

Mobile-first app shell for side projects. Build apps that feel native — in minutes.

┌─────────────────────┐
│ Header (sticky)     │  ← AppShellHeader
├─────────────────────┤
│                     │
│ Content (scroll)    │  ← AppShellContent
│                     │
├─────────────────────┤
│ TabBar (sticky)     │  ← TabBar + Tab
└─────────────────────┘
   rounded · shadow · floating on color
          ↑
       Watermark (full-screen colored bg)

Install

npm install @m1kapp/ui

Requirements: React 18+, Tailwind CSS 4+

Add the font (optional but recommended):

<!-- index.html -->
<link rel="stylesheet" href="https://static.toss.im/dist/tps.css" />
// main.tsx
import { fontFamily } from "@m1kapp/ui";
document.body.style.fontFamily = fontFamily.toss;

Quick Start

import { useState } from "react";
import {
  Watermark, AppShell, AppShellHeader, AppShellContent,
  TabBar, Tab, Section, StatChip, ThemeButton, ThemeDialog,
  colors,
} from "@m1kapp/ui";

export default function App() {
  const [tab, setTab] = useState("home");
  const [themeColor, setThemeColor] = useState(colors.blue);
  const [themeOpen, setThemeOpen] = useState(false);
  const [dark, setDark] = useState(false);

  return (
    <>
      <Watermark color={themeColor} text="myapp">
        <AppShell>
          <AppShellHeader>
            <span className="text-xl font-black">myapp</span>
            <ThemeButton
              color={themeColor}
              dark={dark}
              onClick={() => setThemeOpen(true)}
            />
          </AppShellHeader>

          <AppShellContent>
            <Section>
              <StatChip label="Users" value={128} />
            </Section>
          </AppShellContent>

          <TabBar>
            <Tab
              active={tab === "home"}
              onClick={() => setTab("home")}
              label="Home"
              activeColor={themeColor}
              icon={<HomeIcon />}
            />
          </TabBar>
        </AppShell>
      </Watermark>

      <ThemeDialog
        open={themeOpen}
        onClose={() => setThemeOpen(false)}
        current={themeColor}
        onSelect={setThemeColor}
        dark={dark}
        onDarkToggle={() => {
          setDark((v) => !v);
          document.documentElement.classList.toggle("dark");
        }}
      />
    </>
  );
}

Components

Watermark

Full-screen colored background with a repeating text pattern. The "floating app" aesthetic.

<Watermark
  color="#3b82f6"       // background color
  text="myapp"          // repeating watermark text
  speed={20}            // animation speed in seconds. 0 = static
  sponsor={{ name: "@m1kapp/ui", url: "https://github.com/m1kapp/ui" }}
>
  <AppShell>...</AppShell>
</Watermark>

| Prop | Type | Default | Description | |------|------|---------|-------------| | color | string | #0f172a | Background color | | text | string | "m1k" | Watermark pattern text | | maxWidth | number | 430 | Max width of content area | | padding | number | 12 | Padding around the shell (px) | | speed | number | 20 | Drift animation speed (s). 0 = no animation | | sponsor | WatermarkSponsor | — | 1k milestone sponsor — name shown interleaved with watermark text; entire bg becomes a link |

interface WatermarkSponsor {
  name: string; // shown in background
  url: string;  // entire background becomes a clickable link
}

AppShell

Mobile app container — rounded corners, drop shadow, ring. Centers within Watermark.

<AppShell maxWidth={430}>
  <AppShellHeader>...</AppShellHeader>
  <AppShellContent>...</AppShellContent>
  <TabBar>...</TabBar>
</AppShell>

| Prop | Type | Default | |------|------|---------| | maxWidth | number | 430 | | className | string | — | | style | CSSProperties | — |


AppShellHeader

Sticky top bar (h-14) with blur backdrop.

<AppShellHeader>
  <span className="font-black">myapp</span>
  <ThemeButton color={themeColor} dark={dark} onClick={...} />
</AppShellHeader>

AppShellContent

Scrollable area between header and tab bar.

<AppShellContent>
  <Section>...</Section>
  <Divider />
  <Section>...</Section>
</AppShellContent>

TabBar + Tab

Sticky bottom nav (h-16) with active color support.

<TabBar>
  <Tab
    active={tab === "home"}
    onClick={() => setTab("home")}
    label="Home"
    icon={<HomeIcon />}
    activeColor={themeColor}   // optional, defaults to current text color
  />
</TabBar>

| Prop | Type | Description | |------|------|-------------| | active | boolean | Selected state | | onClick | () => void | — | | icon | ReactNode | Icon element | | label | string | Label below icon | | activeColor | string? | Color when active |


ThemeButton

Single circular button split diagonally — half light/dark indicator, half theme color dot. Put it in the header.

<ThemeButton
  color={themeColor}   // theme color (bottom-right half)
  dark={dark}          // light mode = white half, dark mode = black half
  onClick={() => setThemeOpen(true)}
/>

ThemeDialog

Bottom-sheet color picker + dark/light mode toggle.

<ThemeDialog
  open={themeOpen}
  onClose={() => setThemeOpen(false)}
  current={themeColor}
  onSelect={(color) => setThemeColor(color)}
  dark={dark}
  onDarkToggle={() => {
    setDark((v) => !v);
    document.documentElement.classList.toggle("dark");
  }}
/>

| Prop | Type | Description | |------|------|-------------| | open | boolean | Show/hide | | onClose | () => void | Called on backdrop click or Escape | | current | string | Currently selected color | | onSelect | (color: string) => void | Called when a color is picked (closes dialog) | | dark | boolean? | Current dark mode state | | onDarkToggle | () => void? | Called when light/dark is toggled | | palette | Record<string, string>? | Override color palette (default: built-in colors) |


Section + SectionHeader

<Section className="pt-5">
  <SectionHeader>Stats</SectionHeader>
  <p>Content with px-4 padding applied.</p>
</Section>

Divider

<Divider />

StatChip

Compact label + number display.

<div className="flex gap-3">
  <StatChip label="Users" value={1024} />
  <StatChip label="Revenue" value={9800} />
</div>

EmptyState

<EmptyState message="Nothing here yet" />

// Custom icon:
<EmptyState message="No results" icon={<SearchIcon />} />

Typewriter

Human-like typing effect with a blinking cursor.

<Typewriter
  words={["side project", "weekend build", "바이브코딩"]}
  color={themeColor}
  speed={80}        // ms per character
  pause={2000}      // ms between words
/>

colors

Curated palette. Use with Watermark, Tab, and ThemeDialog.

import { colors } from "@m1kapp/ui";

colors.blue    // #3b82f6
colors.purple  // #8b5cf6
colors.green   // #10b981
colors.orange  // #f97316
colors.pink    // #ec4899
colors.red     // #ef4444
colors.yellow  // #eab308
colors.cyan    // #06b6d4
colors.slate   // #0f172a
colors.zinc    // #27272a

fonts + fontFamily

CDN font references — no files bundled.

import { fonts, fontFamily } from "@m1kapp/ui";

// In HTML:
// <link rel="stylesheet" href={fonts.toss} />

// In JS:
document.body.style.fontFamily = fontFamily.toss;

// Available:
fonts.toss        // Toss Product Sans
fonts.pretendard  // Pretendard Variable
fonts.inter       // Inter

Dark Mode

Uses Tailwind's class-based dark mode. Add this to your CSS:

/* index.css */
@custom-variant dark (&:where(.dark, .dark *));

Then toggle with:

document.documentElement.classList.toggle("dark", isDark);

Philosophy

In the AI era, a side project takes a day to build.
But making it feel like an app still takes effort.

@m1kapp/ui gives you the shell — header, scrollable content, bottom tabs, floating on a colored background — so you can focus on the idea.

Built and battle-tested at m1k.app.

License

MIT