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

@waves-counter/vue

v0.2.2

Published

Accessible Vue component and composable for Wave Counter

Readme

@waves-counter/vue

Accessible Vue component and composable for Wave Counter.

Use it to drop an optimistic counter button into a Vue app, backed by your own Wave Counter API. The component includes keyboard and pointer interactions, selectable analytics windows, default styles, and customization hooks.

Install

npm install @waves-counter/vue @waves-counter/client vue

vue is a peer dependency. The package includes TypeScript declarations and a CSS file.

Quick start

<script setup lang="ts">
import { WaveCounter } from '@waves-counter/vue'
import '@waves-counter/vue/styles.css'
</script>

<template>
  <WaveCounter
    counter-key="coffee"
    endpoint="/api/waves"
    @error="console.error"
  />
</template>

endpoint should point at a Wave Counter backend mounted with this HTTP contract:

GET  /counters/{key}
POST /counters/{key}/events
GET  /counters/{key}/analytics?window=7d|1M|all

Component behavior

  • Click or tap records one event optimistically, then reconciles with the backend total.
  • Right click, long press, the Context Menu key, or Shift+F10 opens analytics.
  • Escape, the close button, or outside pointer interaction closes the analytics popover.
  • Analytics are requested only when stats are enabled and opened.
  • Loading, unavailable, and analytics error states are announced through accessible status text.

Props

| Prop | Type | Default | Description | | --- | --- | --- | --- | | counterKey | string | Required | Counter key sent to the API. | | endpoint | string | Required | Base URL where Wave Counter routes are mounted. | | theme | 'auto' \| 'light' \| 'dark' | 'auto' | Color mode. Auto follows prefers-color-scheme; explicit light and dark ignore the OS setting. | | icon | Component | Coffee icon | Component rendered inside the trigger. | | showStats | boolean | true | Enables analytics interactions and popover. | | analyticsWindow | '7d' \| '1M' \| 'all' | '7d' | Initial analytics window. Visitors can switch it in the default popover. | | longPressMs | number | 550 | Touch long-press delay before opening analytics. | | transport | WaveCounterTransport | Generated client | Custom transport for tests or advanced integrations. |

Events

| Event | Payload | Description | | --- | --- | --- | | error | Error | Emitted when initial loading or incrementing fails. Analytics errors stay in the popover so users can retry. |

Slots

<WaveCounter counter-key="coffee" endpoint="/api/waves">
  <template #icon>
    <span aria-hidden="true">🌊</span>
  </template>

  <template #default="{ total, pending, unavailable }">
    {{ unavailable ? 'Offline' : total }}
    <small v-if="pending">syncing…</small>
  </template>

  <template #analytics="{ analytics, window, loading, error, setWindow, retry }">
    <p v-if="loading">Loading…</p>
    <button v-else-if="error" type="button" @click="retry">Retry</button>
    <button v-else type="button" @click="setWindow(window === 'all' ? '7d' : 'all')">
      {{ analytics?.total ?? 0 }} events
    </button>
  </template>
</WaveCounter>

useWaveCounter

Use the composable when you want your own presentation.

import { useWaveCounter } from '@waves-counter/vue'

const wave = useWaveCounter({
  counterKey: 'coffee',
  endpoint: '/api/waves',
  showStats: true,
})

await wave.load()
await wave.increment()
await wave.openStats()
wave.closeStats()

Returned refs and methods:

wave.state
wave.counter
wave.analytics
wave.analyticsWindow
wave.loading
wave.pendingIncrements
wave.analyticsLoading
wave.statsEnabled
wave.statsOpen
wave.error
wave.analyticsError
wave.load()
wave.increment()
wave.enableStats(false)
wave.openStats()
wave.closeStats()
wave.toggleStats()
wave.setAnalyticsWindow('all')
wave.loadAnalytics()

Styling

Import the default stylesheet once:

import '@waves-counter/vue/styles.css'

Choose a color mode with theme. The default is auto, which follows the visitor's OS preference.

<WaveCounter counter-key="coffee" endpoint="/api/waves" theme="auto" />
<WaveCounter counter-key="coffee" endpoint="/api/waves" theme="light" />
<WaveCounter counter-key="coffee" endpoint="/api/waves" theme="dark" />

Theme with CSS custom properties on the component or a parent element:

.my-counter {
  --wave-counter-ink: oklch(24% 0.02 250);
  --wave-counter-muted: oklch(54% 0.03 250);
  --wave-counter-surface: oklch(98% 0.006 250);
  --wave-counter-raised: oklch(99% 0.004 250);
  --wave-counter-border: oklch(88% 0.02 250);
  --wave-counter-color: oklch(68% 0.16 205);
  --wave-counter-color-strong: oklch(48% 0.15 205);
  --wave-counter-radius: 999px;
  --wave-counter-popover-radius: 1rem;
  --wave-counter-popover-duration: 220ms;
}

For theme-specific overrides, set the base variables for both modes or use the *-dark fallbacks:

.my-counter {
  --wave-counter-surface: oklch(98% 0.006 250);
  --wave-counter-surface-dark: oklch(25% 0.018 250);
  --wave-counter-color: oklch(62% 0.14 205);
  --wave-counter-color-dark: oklch(74% 0.11 205);
}

Production notes

  • Keep API authorization, CORS, abuse prevention, and rate limiting in your host app.
  • The package does not collect user identity or browser fingerprints.
  • Counter increments are idempotent at the transport layer, so transient network retries should not double-count.
  • For SSR, render the component normally but call counter actions only in the browser.

Related packages

  • @waves-counter/client: framework-neutral browser client and controller.
  • @waves-counter/react: accessible React component and hook.
  • @waves-counter/node: native Node and Express backend integration.
  • wave-counter: Python bindings and FastAPI integration.