FeaturePriority

FeaturePriority helps you rank backlog items using structured methods. It combines Kano, MaxDiff, and Bradley-Terry to separate must-haves from lower-priority features.

@sensefolks/featurepriority WCAG 2.1 AA

How It Works

FeaturePriority runs a multi-step survey that can combine up to three methods in one session. 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. Over multiple rounds, this produces a clean ranking that's more reliable than simple rating scales.
  • Bradley-Terry pairwise comparison presents two features side by side and asks "which do you prefer?" Simple A-vs-B choices turn into a statistically sound ranking.

The component handles step transitions, progress tracking, and session state.

When to Use FeaturePriority

Embed FeaturePriority where users evaluate and prioritize options:

  • Feature comparison pages — Understand what matters most
  • Onboarding preference steps — Personalize the experience
  • Product configuration flows — Capture priority order
  • Landing pages — Validate feature importance before building

Installation

CDN (Recommended)

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

<!-- Legacy browsers -->
<script nomodule src="https://unpkg.com/@sensefolks/[email protected]/dist/sf-featurepriority/sf-featurepriority.js"></script>

NPM

bash
npm install @sensefolks/featurepriority

Framework Integration

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 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
thankYouMessage thank-you-message string Overrides the saved survey thank-you message
sessionData Record<string, string | number | boolean> {} Typed Session Data matching the schema declared in Response Context

Events

Event Description Detail Properties
sfError Fired on load, submit, or validation errors surveyKey, errorType, errorMessage

CSS Custom Properties

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

Property Default Description
--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-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-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 */ }
sf-featurepriority::part(retry-button) { /* Retry button on error */ }

/* 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(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

Browser Version
Chrome88+
Firefox85+
Safari14+
Edge88+
IE11Supported (ES5 build)