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

@clearlaunch/ink

v0.2.0

Published

Ink — a dark-first, flat, hairline-bordered design system for Vue 3. Tokens, CSS layer and components.

Readme

@ink/vue

Ink — a dark-first, flat, hairline-bordered design system for Vue 3.

Everything interactive is a full pill. Surfaces are defined by a 1px hairline, never a shadow. The brand gradient marks exactly one thing: the primary or active state.

Full spec: docs/STYLE-GUIDE.md in the ResponseFlow repo.


Install

pnpm add @ink/vue
import { createApp } from 'vue';
import InkVue from '@ink/vue';
import '@ink/vue/style.css';

createApp(App).use(InkVue).mount('#app');

All components register globally — no per-file imports. For TypeScript autocomplete on those global components, add to tsconfig.json:

{ "compilerOptions": { "types": ["@ink/vue/global"] } }

Prefer explicit imports? They're named exports:

import { InkButton, InkSegmented } from '@ink/vue';

Rebranding

The palette is CSS custom properties. Override --ink-accent* after the package stylesheet and everything follows:

:root {
  --ink-accent: #f97316;
  --ink-accent-2: #fbbf24;
  --ink-grad: linear-gradient(135deg, #f97316 0%, #fb923c 45%, #fbbf24 100%);
}

Already have a token system? Bridge it, so there's one source of truth:

:root, body.body--dark, body.body--light {
  --ink-accent: var(--my-accent);
  --ink-surface: var(--my-surface);
  /* … */
}

Or take the SCSS source and override at build time:

@use '@ink/vue/scss';

Components

| Component | Purpose | |---|---| | InkButton | Pill button — gradient | outline | flat | elevated | danger | | InkIconButton | 28px squircle. The one control that is not a pill | | InkSegmented | Pill track with segments — tabs and icon-only view toggles | | InkPageHeader | Sticky header that collapses on scroll | | InkSearch | Search pill; focus is a border change, never a ring | | InkStatus | Status chip — ok | error | warning | info | muted | | InkMeter | Usage meter — recessed track, gradient or status fill | | InkRow | Flat row. Use instead of a card per list item | | InkCard | Flat bordered panel. No shadow, no hover lift | | InkEmpty | Empty state |

Examples

<!-- One gradient per screen region; siblings are outline -->
<InkButton variant="outline">Cancel</InkButton>
<InkButton variant="gradient" :loading="saving" @click="save">Save</InkButton>

<!-- list | card toggle -->
<InkSegmented
  v-model="view"
  :options="[
    { value: 'list', ariaLabel: 'List view', icon: 'list' },
    { value: 'card', ariaLabel: 'Card view', icon: 'grid' },
  ]"
  icon-only
>
  <template #icon="{ option }"><MyIcon :name="option.icon" /></template>
</InkSegmented>

<!-- Tabs with counts -->
<InkSegmented v-model="tab" :options="[
  { value: 'all', label: 'All', count: 12 },
  { value: 'new', label: 'New' },
]" />

<!-- A list of things is rows, not a grid of cards -->
<InkRow v-for="c in items" :key="c.id" clickable @click="open(c)">
  <div class="name">{{ c.name }}</div>
  <template #trailing>
    <InkStatus :tone="c.live ? 'ok' : 'muted'" dot>
      {{ c.live ? 'Live' : 'Draft' }}
    </InkStatus>
  </template>
</InkRow>

<InkMeter :value="65" :max="100" tone="warning" />

Icons are slots, never bundled — the host app picks its own icon library.

Using Ink with a component framework

Ink is a styling layer, not a framework. It works three ways, and you can mix them in one app:

| Layer | What it is | Works with | |---|---|---| | Tokens | --ink-* CSS custom properties | anything, including plain HTML | | Components | the Ink* Vue components above | any Vue 3 app | | Adapters | thin wrappers over another framework's controls | that framework |

Quasar

import { InkQuasar } from '@clearlaunch/ink/quasar';
app.use(Quasar, { /* … */ }).use(InkQuasar);
<InkQInput v-model="email" label="Email" placeholder="[email protected]" />
<InkQInput v-model="key" label="API key" variant="gradient" />
<InkQBtn label="Save" />
<InkQBtn label="Cancel" variant="ghost" />

InkQInput renders a real q-input, so rules, mask, debounce, slots and refs all behave exactly as they do on a bare one. What it adds is the shape (outlined dense, 10px radius, border-colour focus) and — importantly — stack-label, which is a PROP and therefore not fixable with CSS. Without it, a dense field with a label floats that label into the input and it overlaps the placeholder until the user types.

variant="gradient" draws the brand gradient as the border itself. Use it for the one field on a page that should pull focus; two on a screen and neither reads as primary.

Both are optional. Quasar is an optional peer dependency and lives on a separate entry point, so a Tailwind or Nuxt app importing @clearlaunch/ink never pulls Quasar-shaped code into its bundle.

Why wrap rather than replace

InkInput (standalone) and InkQInput (Quasar) both exist on purpose. In a Quasar app that shipped both, q-input outnumbered the standalone InkInput roughly 4:1 — because q-input is wired into Quasar's form context and slot conventions that a from-scratch input cannot join. Buttons went the other way: the standalone InkButton won comfortably, since a button has no form context to join.

So: use the standalone components in apps with no component framework, and the adapters where one is already in play.

Design rules worth knowing

  1. One gradient per screen region. If everything is gradient, nothing is.
  2. A list of things is InkRow, not a grid of InkCard. Cards are for genuinely discrete objects.
  3. Hover changes colour, not position. The only lift is on pill buttons.
  4. Shadows are for overlays only — menus, dialogs, toasts. elevated is the deliberate exception for controls that genuinely float.
  5. No nested bordered boxes. Inside a panel, children are flat rows.

Accessibility

  • InkIconButton requires ariaLabel — an icon-only control needs a name.
  • InkSegmented renders role="tablist" / role="tab" with aria-selected.
  • InkRow with clickable renders a real <button>, so keyboard works.
  • InkMeter renders role="progressbar" with proper value attributes.
  • Motion respects prefers-reduced-motion; hover transforms drop at ≤480px.