FeaturePriority

FeaturePriority runs structured feature research inside your product. Use Kano to classify expectations, MaxDiff to compare relative importance, or Bradley-Terry to model pairwise preference.

@sensefolks/featurepriorityWCAG 2.1 AA

What FeaturePriority Measures

FeaturePriority runs a multi-step survey that can combine up to three methods in one response flow. You choose the methods in your dashboard.

  • Kano analysis asks two questions per feature: "How would you feel if this feature existed?" and "How would you feel if it didn't?" The answers sort features into must-have, performance, attractive, indifferent, and reverse categories. For a deeper look, see the Kano model guide.
  • MaxDiff shows a set of features and asks respondents to pick the best and worst. Repeated tasks produce relative importance evidence across the set.
  • Bradley-Terry pairwise comparison presents two features side by side and asks which one they prefer. Repeated A-versus-B choices support a relative ranking.

The component handles step transitions and progress tracking in memory. Reloading the page starts a fresh Kano flow with no restored answers.

When FeaturePriority Is the Right Survey

Use it when your decision depends on comparing several potential features, not simply asking whether one idea sounds good.

  • Feature comparison pages — Understand what matters most
  • Onboarding research — Learn which capabilities matter to new users
  • Product configuration research — Compare preference between capabilities
  • Concept pages — Test feature expectations before committing to a roadmap

Installation

CDN (Recommended)

html
<script type="module" src="https://unpkg.com/@sensefolks/[email protected]/dist/sf-featurepriority/sf-featurepriority.esm.js"></script>

NPM

bash
npm install @sensefolks/featurepriority

Embed FeaturePriority

HTML

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

React

jsx
import '@sensefolks/featurepriority';

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

For Vue + Nuxt, Angular, Svelte, and Astro examples, see theEmbedding Tutorial.

API Reference

Properties

PropertyAttributeTypeDefaultDescription
surveyKeysurvey-keystringRequired. UUID of the survey from your dashboard
completionMessagecompletion-messagestring'Thank you for your response!'Message shown after submission
thankYouMessagethank-you-messagestringOverrides the saved survey thank-you message
sessionDataRecord<string, string | number | boolean>{}Complete typed Session Data matching every field in the schema defined during survey creation

Events

EventDescriptionDetail Properties
sfErrorFired on load, submit, or validation errorssurveyKey, errorType, errorMessage

CSS Custom Properties

Override these properties on the element or a parent to theme the component:

PropertyDefaultDescription
--sf-primary#005fccPrimary brand color
--sf-primary-hover#0047a3Primary hover color
--sf-text-primary#111827Primary text color
--sf-text-secondary#6b7280Secondary/muted text color
--sf-error-color#dc2626Error state color
--sf-card-bg#ffffffCard background color
--sf-card-border#d1d5dbCard border color
--sf-card-radius8pxCard border radius
--sf-button-radius6pxButton border radius
--sf-transition150ms easeTransition timing
--sf-progress-bg#e5e7ebProgress track color
--sf-progress-fillPrimary colorProgress fill color
css
sf-featurepriority {
  --sf-primary: #005fcc;
  --sf-primary-hover: #0047a3;
  --sf-text-primary: #111827;
  --sf-text-secondary: #6b7280;
  --sf-error-color: #dc2626;
  --sf-error-text: #991b1b;
  --sf-error-bg: #fef2f2;
  --sf-error-border: #fecaca;
  --sf-card-bg: #ffffff;
  --sf-card-border: var(--color__grey--450);
  --sf-card-radius: 8px;
  --sf-button-radius: 6px;
  --sf-progress-bg: #ede9fe;
  --sf-progress-fill: #7c3aed;
  --sf-feature-focus-bg: rgba(0, 95, 204, 0.1);
  --sf-feature-drag-bg: rgba(0, 95, 204, 0.2);
  --sf-transition: 150ms ease;
}

CSS Parts

FeaturePriority has a large number of CSS Parts because of its multi-step design (Kano, MaxDiff, pairwise). Parts are organized by step:

css
/* Layout */
sf-featurepriority::part(survey-container) { /* Outer survey wrapper */ }
sf-featurepriority::part(container) { /* Host element wrapper */ }
sf-featurepriority::part(step) { /* Step container (shared by all steps) */ }
sf-featurepriority::part(step-indicator) { /* Step progress indicator */ }
sf-featurepriority::part(heading) { /* Step headings (shared) */ }
sf-featurepriority::part(instructions) { /* Step instructions (shared) */ }
sf-featurepriority::part(button-container) { /* Button row container */ }
sf-featurepriority::part(button) { /* All buttons (shared) */ }
sf-featurepriority::part(next-button) { /* Next/Continue button */ }
sf-featurepriority::part(back-button) { /* Back button */ }
sf-featurepriority::part(submit-button) { /* Submit button */ }

/* Progress */
sf-featurepriority::part(progress) { /* Progress wrapper */ }
sf-featurepriority::part(progress-indicator) { /* Progress wrapper alias */ }
sf-featurepriority::part(progress-label) { /* Progress label */ }
sf-featurepriority::part(progress-text) { /* Progress text alias */ }
sf-featurepriority::part(progress-track) { /* Progress track */ }
sf-featurepriority::part(progress-bar) { /* Progress bar alias */ }
sf-featurepriority::part(progress-fill) { /* Filled progress bar */ }

/* Messages */
sf-featurepriority::part(message) { /* General messages */ }
sf-featurepriority::part(error-message) { /* Error messages */ }
sf-featurepriority::part(loading-message) { /* Loading state message */ }
sf-featurepriority::part(error-container) { /* Error state container */ }
sf-featurepriority::part(troubleshooting-link) { /* Link to integration troubleshooting */ }
sf-featurepriority::part(announcements) { /* Screen reader live region */ }

/* Kano step */
sf-featurepriority::part(kano-step) { /* Kano step container */ }
sf-featurepriority::part(kano-heading) { /* Kano step heading */ }
sf-featurepriority::part(kano-description) { /* Kano item description */ }
sf-featurepriority::part(kano-question) { /* Kano question block */ }
sf-featurepriority::part(kano-functional) { /* Functional question block */ }
sf-featurepriority::part(kano-dysfunctional) { /* Dysfunctional question block */ }
sf-featurepriority::part(kano-question-label) { /* Kano question label */ }
sf-featurepriority::part(kano-option) { /* Kano radio option wrapper */ }
sf-featurepriority::part(radio-group) { /* Radio button group */ }
sf-featurepriority::part(radio-input) { /* Radio input element */ }
sf-featurepriority::part(radio-label) { /* Radio option label */ }

/* Transition screen */
sf-featurepriority::part(step-transition) { /* Transition screen container */ }
sf-featurepriority::part(transition-heading) { /* Transition heading */ }
sf-featurepriority::part(transition-summary) { /* Transition summary text */ }
sf-featurepriority::part(transition-description) { /* Transition description text */ }

/* MaxDiff step */
sf-featurepriority::part(maxdiff-step) { /* MaxDiff step container */ }
sf-featurepriority::part(maxdiff-heading) { /* MaxDiff heading */ }
sf-featurepriority::part(maxdiff-instructions) { /* MaxDiff instructions */ }
sf-featurepriority::part(maxdiff-trial) { /* MaxDiff trial container */ }
sf-featurepriority::part(maxdiff-columns) { /* MaxDiff column headers row */ }
sf-featurepriority::part(maxdiff-column-header) { /* MaxDiff column header cell */ }
sf-featurepriority::part(maxdiff-option) { /* MaxDiff option row */ }
sf-featurepriority::part(maxdiff-item-label) { /* MaxDiff item label */ }
sf-featurepriority::part(maxdiff-item-description) { /* MaxDiff item description */ }
sf-featurepriority::part(best-selector) { /* Best selector cell */ }
sf-featurepriority::part(worst-selector) { /* Worst selector cell */ }

/* Pairwise step */
sf-featurepriority::part(pairwise-step) { /* Pairwise step container */ }
sf-featurepriority::part(pairwise-heading) { /* Pairwise heading */ }
sf-featurepriority::part(pairwise-instructions) { /* Pairwise instructions */ }
sf-featurepriority::part(pairwise-matchup) { /* Pairwise matchup container */ }
sf-featurepriority::part(pairwise-option) { /* Base pairwise option button */ }
sf-featurepriority::part(pairwise-option-left) { /* Left option button */ }
sf-featurepriority::part(pairwise-option-right) { /* Right option button */ }
sf-featurepriority::part(pairwise-option-title) { /* Option title text */ }
sf-featurepriority::part(pairwise-option-description) { /* Option description text */ }
sf-featurepriority::part(pairwise-vs) { /* "vs" divider */ }

/* Results / completion */
sf-featurepriority::part(funnel-complete-step) { /* Completion step container */ }
sf-featurepriority::part(funnel-complete-heading) { /* Completion heading */ }
sf-featurepriority::part(funnel-results) { /* Results container */ }
sf-featurepriority::part(ranking-summary) { /* Ranking summary block */ }
sf-featurepriority::part(summary-heading) { /* Summary heading */ }
sf-featurepriority::part(summary-list) { /* Summary list container */ }
sf-featurepriority::part(summary-item) { /* Individual summary item */ }
sf-featurepriority::part(summary-rank) { /* Rank number */ }
sf-featurepriority::part(summary-title) { /* Item title in summary */ }
sf-featurepriority::part(summary-kano-label) { /* Kano category label in summary */ }

/* Survey Fields step */
sf-featurepriority::part(survey-fields-step) { /* Survey Fields container */ }
sf-featurepriority::part(survey-fields-heading) { /* Survey Fields heading */ }
sf-featurepriority::part(form-container) { /* Form container */ }
sf-featurepriority::part(form-field) { /* Form field wrapper */ }
sf-featurepriority::part(form-label) { /* Form field label */ }
sf-featurepriority::part(form-input) { /* Text/email/number input */ }
sf-featurepriority::part(form-select) { /* Select dropdown */ }
sf-featurepriority::part(hcaptcha-container) { /* hCaptcha wrapper */ }
sf-featurepriority::part(form-radio-group) { /* Radio group in form */ }
sf-featurepriority::part(form-radio) { /* Radio input in form */ }
sf-featurepriority::part(form-radio-label) { /* Radio label in form */ }
sf-featurepriority::part(form-checkbox-group) { /* Checkbox group in form */ }
sf-featurepriority::part(form-checkbox) { /* Checkbox input in form */ }
sf-featurepriority::part(form-checkbox-label) { /* Checkbox label in form */ }
sf-featurepriority::part(field-error) { /* Field validation error */ }
sf-featurepriority::part(required-indicator) { /* Required field asterisk */ }

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

Styling Example

css
sf-featurepriority {
  --sf-primary: #7c3aed;
  --sf-text-primary: #1f2937;
  --sf-card-radius: 12px;
  --sf-button-radius: 8px;
}

sf-featurepriority::part(kano-step) {
  padding: 1.5rem;
  background: var(--sf-card-bg);
  border-radius: var(--sf-card-radius);
}

sf-featurepriority::part(pairwise-option-left),
sf-featurepriority::part(pairwise-option-right) {
  border: 2px solid var(--sf-card-border);
  border-radius: var(--sf-card-radius);
  padding: 1rem;
  transition: border-color var(--sf-transition);
}

sf-featurepriority::part(pairwise-option-selected) {
  border-color: var(--sf-primary);
  background-color: rgba(124, 58, 237, 0.05);
}

Accessibility

  • Full keyboard navigation with Enter/Space to select options
  • ARIA labels and live regions for screen readers
  • Step progress announcements for screen reader users
  • Focus indicators and high contrast mode support
  • Respects prefers-reduced-motion

Browser Support

BrowserVersion
Chrome88+
Firefox85+
Safari14+
Edge88+
IE11Not supported