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/userchoice

v1.0.0

Published

Deeply understand user preferences. Design better offerings or refine existing ones

Readme

@sensefolks/userchoice

A web component for running Choice-Based Conjoint (CBC) surveys. CBC analysis helps you understand which product attributes matter most to your respondents by presenting them with a series of trade-off tasks.

How it works

Each respondent sees a series of choice tasks. Each task presents three product alternatives (each with a different combination of attribute values) plus a "None of these options" choice. After completing all tasks, the response data is used to compute attribute importance scores — revealing which attributes drive preference.

Installation

CDN

<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module" src="https://unpkg.com/@sensefolks/userchoice/dist/sf-userchoice/sf-userchoice.esm.js"></script>
  </head>
  <body>
    <sf-userchoice survey-key="your-survey-uuid-here"></sf-userchoice>
  </body>
</html>

NPM

npm install @sensefolks/userchoice

Usage

Vanilla JS

<script type="module">
  import { defineCustomElements } from '@sensefolks/userchoice/loader';
  defineCustomElements();
</script>

<sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" completion-message="Thanks for your feedback!"></sf-userchoice>

React

import { defineCustomElements } from '@sensefolks/userchoice/loader';
defineCustomElements();

export function SurveyPage() {
  return <sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" completion-message="Thanks for your feedback!" />;
}

Vue

<template>
  <sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" completion-message="Thanks for your feedback!" />
</template>

<script setup>
import { defineCustomElements } from '@sensefolks/userchoice/loader';
defineCustomElements();
</script>

Angular

In main.ts:

import { defineCustomElements } from '@sensefolks/userchoice/loader';
defineCustomElements();

In your component template:

<sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" completion-message="Thanks for your feedback!"></sf-userchoice>

In your AppModule:

import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@NgModule({
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}

API Reference

Properties

| Property | Attribute | Type | Default | Required | Description | | ------------------- | -------------------- | -------- | -------------------------------- | -------- | ----------------------------------------------------------------------- | | surveyKey | survey-key | string | — | Yes | The survey's public key (UUID). Obtained from the SenseFolks dashboard. | | completionMessage | completion-message | string | "Thank you for your response!" | No | Message displayed to the respondent after they submit their responses. |

Events

The component does not emit custom DOM events. Survey submission is handled internally.

CSS Shadow Parts

The component uses shadow DOM and exposes the following CSS parts for styling:

| Part | Description | | -------------------- | ------------------------------------------------------------------- | | survey-container | Outer wrapper for the entire survey | | step | Wrapper for each step (choice task, respondent details, completion) | | task-heading | The <h2> heading for each choice task | | task-instructions | Instruction text above the alternatives | | concepts-container | Wrapper for all alternative cards in a task | | concept | An individual alternative card | | concept-selected | Applied to the currently selected alternative card | | concept-unselected | Applied to unselected alternative cards | | none-option | The "None of these options" card | | concept-header | Header area of an alternative card | | concept-title | Title text within an alternative card | | concept-attributes | Attribute list within an alternative card | | concept-attribute | A single attribute row within a card | | attribute-label | The attribute name label | | attribute-value | The attribute variant value | | progress-indicator | Wrapper for the progress bar and text | | progress-bar | The progress bar track | | progress-fill | The filled portion of the progress bar | | progress-text | "Task X of Y" text | | button | All buttons | | next-button | The "Next Task" / "Submit" button | | back-button | The "Previous" button | | retry-button | The retry button shown on error | | error-container | Wrapper shown when an error occurs | | error-message | The error message text | | loading-message | The loading state text | | branding | SenseFolks branding footer (shown when enabled) |

Styling example

sf-userchoice::part(concept) {
  border: 2px solid #e2e8f0;
  border-radius: 8px;
  padding: 16px;
  cursor: pointer;
}

sf-userchoice::part(concept-selected) {
  border-color: #3b82f6;
  background-color: #eff6ff;
}

sf-userchoice::part(next-button) {
  background-color: #3b82f6;
  color: white;
  border-radius: 6px;
  padding: 10px 24px;
}

Minimal working example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>CBC Survey</title>
    <script type="module" src="https://unpkg.com/@sensefolks/userchoice/dist/sf-userchoice/sf-userchoice.esm.js"></script>
  </head>
  <body>
    <sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"></sf-userchoice>
  </body>
</html>

Accessibility

The component includes keyboard navigation support:

  • Arrow keys — move focus between alternative cards
  • Enter / Space — select the focused alternative
  • All interactive elements have appropriate role, aria-checked, aria-label, and tabindex attributes
  • A live region announces selections to screen readers

Browser support

Supports all modern browsers (Chrome, Firefox, Safari, Edge). Requires custom elements support.