UserChoice

UserChoice runs conjoint analysis directly on your site so you can measure which product trade-offs users prefer.

@sensefolks/userchoice WCAG 2.1 AA

How It Works

UserChoice presents a series of product configurations ("concepts") and asks respondents to choose among them. Across tasks, this reveals which attributes and combinations drive preference.

For a deeper methodology overview, see the conjoint analysis guide.

When to Use UserChoice

Embed UserChoice where users can evaluate product options:

  • Product roadmap pages — Let users vote on feature combinations
  • Feature request pages — Understand which options people actually prefer
  • Upcoming features sections — Validate concepts before building
  • Beta and early-access pages — Gather preference data from engaged users

Installation

CDN (Recommended)

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

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

NPM

bash
npm install @sensefolks/userchoice

Framework Integration

HTML

html
<sf-userchoice 
  survey-key="your-survey-uuid" 
  completion-message="Thank you for your feedback!">
</sf-userchoice>

React

jsx
import '@sensefolks/userchoice';

function App() {
  return (
    <sf-userchoice 
      survey-key="your-survey-uuid" 
      completion-message="Thank you!">
    </sf-userchoice>
  );
}

Vue

vue
<template>
  <sf-userchoice 
    survey-key="your-survey-uuid" 
    completion-message="Thank you!">
  </sf-userchoice>
</template>

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

Angular

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

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

For Next.js and Svelte examples, see the Embedding Tutorial.

API Reference

Properties

Property Attribute Type Default Description
surveyKey survey-key string Required. UUID of the survey from your dashboard
completionMessage completion-message string 'Thank you for your response!' Message shown after submission

Events

This component does not emit custom DOM events.

CSS Custom Properties

Override these custom properties to theme the component:

Property Default Description
--sf-primary #005fcc Primary accent color
--sf-primary-hover #0047a3 Primary color hover state
--sf-text-primary #111827 Primary text color
--sf-text-secondary #6b7280 Secondary/muted text color
--sf-error-color #dc2626 Error state color
--sf-card-bg #ffffff Card/container background
--sf-card-border #d1d5db Card/container border color
--sf-card-radius 8px Card/container border radius
--sf-button-radius 6px Button border radius
--sf-transition 150ms ease Default transition timing
--sf-selected-bg #e8f0fe Selected card background
--sf-selected-border #005fcc Selected card border color
--sf-card-shadow 0 1px 3px rgba(0,0,0,0.1) Card shadow
--sf-card-shadow-hover 0 4px 12px rgba(0,0,0,0.15) Card hover shadow
--sf-none-bg #f9fafb "None" option background
--sf-none-border #9ca3af "None" option border
--sf-error-bg #fef2f2 Error state background
--sf-error-border #fca5a5 Error state border
--sf-progress-bg #e5e7eb Progress bar track background
--sf-progress-fill #005fcc Progress bar fill color
css
/* Override theme tokens on the component */
sf-userchoice {
  --sf-primary: #7c3aed;
  --sf-primary-hover: #6d28d9;
  --sf-card-radius: 12px;
  --sf-button-radius: 8px;
  --sf-selected-bg: #ede9fe;
  --sf-selected-border: #7c3aed;
}

CSS Parts

Style individual elements inside the shadow DOM using ::part(). UserChoice has a large number of parts because of its multi-step, card-based UI:

css
/* Survey container */
sf-userchoice::part(survey-container) { }

/* Steps */
sf-userchoice::part(step) { }
sf-userchoice::part(choice-task-step) { }
sf-userchoice::part(respondent-details-step) { }
sf-userchoice::part(completion-step) { }

/* Headings */
sf-userchoice::part(heading) { }
sf-userchoice::part(task-heading) { }
sf-userchoice::part(respondent-details-heading) { }
sf-userchoice::part(completion-heading) { }

/* Task header & progress */
sf-userchoice::part(task-header) { }
sf-userchoice::part(task-instructions) { }
sf-userchoice::part(progress-indicator) { }
sf-userchoice::part(progress-bar) { }
sf-userchoice::part(progress-fill) { }
sf-userchoice::part(progress-text) { }

/* Concepts */
sf-userchoice::part(concepts-container) { }
sf-userchoice::part(concept) { }
sf-userchoice::part(concept-selected) { }
sf-userchoice::part(concept-unselected) { }
sf-userchoice::part(none-option) { }
sf-userchoice::part(concept-header) { }
sf-userchoice::part(concept-radio) { }
sf-userchoice::part(concept-title) { }
sf-userchoice::part(concept-attributes) { }
sf-userchoice::part(concept-attribute) { }
sf-userchoice::part(attribute-label) { }
sf-userchoice::part(attribute-value) { }

/* Buttons */
sf-userchoice::part(button-container) { }
sf-userchoice::part(button) { }
sf-userchoice::part(back-button) { }
sf-userchoice::part(next-button) { }
sf-userchoice::part(submit-button) { }
sf-userchoice::part(retry-button) { }

/* Respondent details form */
sf-userchoice::part(form-container) { }
sf-userchoice::part(form-field) { }
sf-userchoice::part(form-label) { }
sf-userchoice::part(form-input) { }
sf-userchoice::part(form-select) { }
sf-userchoice::part(hcaptcha-container) { }
sf-userchoice::part(input) { }
sf-userchoice::part(select) { }
sf-userchoice::part(required-indicator) { }
sf-userchoice::part(radio-group) { }
sf-userchoice::part(radio-option) { }
sf-userchoice::part(radio-input) { }
sf-userchoice::part(radio-label) { }
sf-userchoice::part(checkbox-group) { }
sf-userchoice::part(checkbox-option) { }
sf-userchoice::part(checkbox-input) { }
sf-userchoice::part(checkbox-label) { }

/* Completion */
sf-userchoice::part(completion-summary) { }
sf-userchoice::part(summary-text) { }

/* Messages & errors */
sf-userchoice::part(message) { }
sf-userchoice::part(error-message) { }
sf-userchoice::part(loading-message) { }
sf-userchoice::part(error-container) { }

/* Branding */
sf-userchoice::part(branding) { }
sf-userchoice::part(branding-link) { }
sf-userchoice::part(branding-logo) { }

/* Accessibility */
sf-userchoice::part(announcements) { }

Accessibility

  • Full keyboard navigation (Tab, Arrow keys, Enter/Space)
  • ARIA labels and live regions for screen readers
  • Progress announcements ("Choice Task 2 of 5")
  • Focus indicators and high contrast mode support
  • Respects prefers-reduced-motion

Browser Support

Browser Version
Chrome88+
Firefox85+
Safari14+
Edge88+
IE11 Supported (ES5 build)