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

@sensefolks/pricepoint

v1.0.0

Published

Find the right price that your users are willing to pay

Readme

sf-pricepoint

npm version npm downloads License: MIT

Price sensitivity survey web component for Van Westendorp and Gabor-Granger pricing research. WCAG 2.1 AA accessible.

Features

  • 💰 Van Westendorp Price Sensitivity Meter + Gabor-Granger demand analysis
  • 🔄 Automatic two-phase pipeline (VW → GG) based on response volume
  • 💱 Multi-currency support with auto-detection from respondent locale
  • ♿ WCAG 2.1 AA accessible (keyboard, screen reader, reduced motion)
  • 🎨 Customizable via CSS Parts and Custom Properties
  • 📱 Responsive design
  • 🌐 Works with any framework (React, Vue, Angular, vanilla HTML)

Installation

NPM

npm install @sensefolks/pricepoint

Yarn

yarn add @sensefolks/pricepoint

CDN (Script Tag)

<!-- Modern browsers (ES modules) -->
<script type="module" src="https://unpkg.com/@sensefolks/pricepoint/dist/sf-pricepoint/sf-pricepoint.esm.js"></script>

<!-- Legacy browsers -->
<script nomodule src="https://unpkg.com/@sensefolks/pricepoint/dist/sf-pricepoint/sf-pricepoint.js"></script>

Or via jsDelivr:

<script type="module" src="https://cdn.jsdelivr.net/npm/@sensefolks/pricepoint/dist/sf-pricepoint/sf-pricepoint.esm.js"></script>

Quick Start

<sf-pricepoint survey-key="your-survey-uuid"></sf-pricepoint>

React

import '@sensefolks/pricepoint';

function App() {
  return <sf-pricepoint survey-key="your-survey-uuid"></sf-pricepoint>;
}

Vue

<template>
  <sf-pricepoint survey-key="your-survey-uuid"></sf-pricepoint>
</template>

<script>
import '@sensefolks/pricepoint';
export default { name: 'App' };
</script>

Angular

// app.module.ts
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import '@sensefolks/pricepoint';

@NgModule({
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
<!-- component.html -->
<sf-pricepoint survey-key="your-survey-uuid"></sf-pricepoint>

API Reference

Properties

| Property | Attribute | Type | Default | Description | | ----------- | ------------ | -------- | ----------- | -------------------------------------------------- | | surveyKey | survey-key | string | undefined | Public key (UUID) of the survey to load (required) |

Events

| Event | Detail Type | Description | | --------------- | -------------------------- | ------------------------------------------------- | | sfReady | SfReadyEventDetail | Survey config loaded and component ready | | sfSubmit | SfSubmitEventDetail | Response successfully submitted | | sfError | SfErrorEventDetail | Error occurred (load, submit, validation, config) | | sfPhaseChange | SfPhaseChangeEventDetail | Phase transition (loading → vw → gg → complete) |

Event Payload Shapes

interface SfReadyEventDetail {
  surveyKey: string;
  productType: string;
  phase: string;
}

interface SfSubmitEventDetail {
  surveyKey: string;
  phase: string;
  completionTimeSeconds: number;
}

interface SfErrorEventDetail {
  surveyKey: string;
  errorType: 'load' | 'submit' | 'validation' | 'config';
  errorMessage: string;
}

interface SfPhaseChangeEventDetail {
  phase: string; // 'loading' | 'vw' | 'gg' | 'complete' | 'error'
}

Configuration Schema

The survey is configured server-side. The component fetches config via the surveyKey.

{
  "productContext": {
    "productName": "My SaaS Product",
    "productCategory": "Software",
    "industry": "Technology"
  },
  "targetMarkets": ["US", "GB"],
  "productType": "subscription",
  "billingInterval": "monthly",
  "priceRange": { "min": 10, "max": 200 },
  "currencyMode": "USD",
  "costAnalysis": {
    "annualCostPerCustomer": 50,
    "annualFixedExpenses": 100000,
    "expectedAnnualCustomers": 5000,
    "desiredProfitMargin": 30
  }
}

CSS Parts

Style the component externally using ::part():

/* Container & Layout */
sf-pricepoint::part(survey-container) {
  /* Main wrapper */
}
sf-pricepoint::part(heading) {
  /* Step headings */
}
sf-pricepoint::part(question-text) {
  /* Question text */
}
sf-pricepoint::part(progress-indicator) {
  /* Step/phase progress */
}

/* Van Westendorp Phase */
sf-pricepoint::part(vw-container) {
  /* VW phase wrapper */
}
sf-pricepoint::part(slider-track) {
  /* Slider track */
}
sf-pricepoint::part(slider-thumb) {
  /* Slider thumb */
}
sf-pricepoint::part(text-input) {
  /* Price text input fields */
}
sf-pricepoint::part(validation-message) {
  /* Validation error text */
}

/* Gabor-Granger Phase */
sf-pricepoint::part(gg-container) {
  /* GG phase wrapper */
}
sf-pricepoint::part(price-display) {
  /* Price label in GG */
}
sf-pricepoint::part(radio-group) {
  /* Purchase intent radio group */
}
sf-pricepoint::part(radio-option) {
  /* Individual radio option */
}
sf-pricepoint::part(radio-option-selected) {
  /* Selected radio option */
}

/* Navigation & Actions */
sf-pricepoint::part(nav-button) {
  /* Navigation buttons */
}
sf-pricepoint::part(submit-button) {
  /* Submit button */
}

/* States */
sf-pricepoint::part(loading-message) {
  /* Loading state text */
}
sf-pricepoint::part(error-message) {
  /* Error message text */
}
sf-pricepoint::part(error-container) {
  /* Error wrapper with retry */
}
sf-pricepoint::part(retry-button) {
  /* Retry button */
}
sf-pricepoint::part(completion-screen) {
  /* Completion state */
}

/* Accessibility */
sf-pricepoint::part(announcements) {
  /* Screen reader live region */
}

CSS Custom Properties

sf-pricepoint {
  --sf-primary-color: #4f46e5; /* Primary accent color */
  --sf-background-color: #ffffff; /* Background color */
  --sf-text-color: #1f2937; /* Text color */
  --sf-border-radius: 8px; /* Border radius for inputs and cards */
  --sf-font-family: system-ui, sans-serif; /* Font family */
  --sf-spacing-scale: 1; /* Spacing multiplier (1 = default) */
}

Accessibility

  • Full keyboard navigation (Tab, Enter, Space, Arrow keys)
  • ARIA role="slider" with aria-valuemin, aria-valuemax, aria-valuenow, aria-label on hybrid slider inputs
  • ARIA role="radiogroup" on Gabor-Granger purchase intent scale
  • aria-live="polite" region for screen reader announcements on phase/question transitions
  • Visible focus indicators on all interactive elements
  • Respects prefers-reduced-motion: reduce
  • High contrast mode support via @media (prefers-contrast: high)

Browser Support

  • Chrome 88+
  • Firefox 85+
  • Safari 14+
  • Edge 88+
  • IE11 (with ES5 build)

Agent Integration

This section is designed for AI code generation agents integrating the sf-pricepoint component.

Machine-Readable Config Example

{
  "component": "sf-pricepoint",
  "package": "@sensefolks/pricepoint",
  "attributes": [{ "name": "survey-key", "type": "string", "required": true, "description": "Public key UUID of the survey" }],
  "events": [
    { "name": "sfReady", "detail": "SfReadyEventDetail", "description": "Component loaded and ready" },
    { "name": "sfSubmit", "detail": "SfSubmitEventDetail", "description": "Response submitted" },
    { "name": "sfError", "detail": "SfErrorEventDetail", "description": "Error occurred" },
    { "name": "sfPhaseChange", "detail": "SfPhaseChangeEventDetail", "description": "Phase changed" }
  ],
  "phases": ["loading", "vw", "gg", "complete", "error"],
  "cssCustomProperties": ["--sf-primary-color", "--sf-background-color", "--sf-text-color", "--sf-border-radius", "--sf-font-family", "--sf-spacing-scale"]
}

Emitted Events with Payload Shapes

| Event | Payload Fields | When Emitted | | --------------- | ----------------------------------------------------------------- | --------------------------- | | sfReady | surveyKey: string, productType: string, phase: string | After config fetch and init | | sfSubmit | surveyKey: string, phase: string, completionTimeSeconds: number | After successful POST | | sfError | surveyKey: string, errorType: string, errorMessage: string | On any error | | sfPhaseChange | phase: string | On phase transition |

Integration Checklist

  1. Install the package: npm install @sensefolks/pricepoint
  2. Import the component: import '@sensefolks/pricepoint';
  3. Add the element: <sf-pricepoint survey-key="YOUR_KEY"></sf-pricepoint>
  4. Listen for events: el.addEventListener('sfReady', (e) => { ... })
  5. Apply custom styles via CSS custom properties or ::part() selectors
  6. Ensure the survey is created and configured via the API before embedding

TypeScript Types

All types are exported from the package entry point:

import type {
  PricepointSurveyConfig,
  PriceLadderResponse,
  CurrencyResolution,
  VWAnswers,
  SfReadyEventDetail,
  SfSubmitEventDetail,
  SfErrorEventDetail,
  SfPhaseChangeEventDetail,
} from '@sensefolks/pricepoint';

Changelog

See CHANGELOG.md.

License

MIT © SenseFolks