FeaturePriority

FeaturePriority is an embeddable feature prioritization survey. It starts with Kano, then uses MaxDiff and Bradley-Terry pairwise comparisons when enough features advance.

@sensefolks/featurepriorityWCAG 2.1 AA

What FeaturePriority Measures

Configure 4–7 features in the dashboard. The respondent's Kano answers determine which stages follow:

  • Kano asks about each feature's presence and absence. Must-be, performance, and attractive features advance. See the Kano guide.
  • MaxDiff compares best and worst choices to narrow the qualifying features.
  • Bradley-Terry estimates relative preference from the final pairwise choices.

After Kano, 0–2 qualifying features end the research flow; 3 go straight to pairwise comparison; 4 or more continue to MaxDiff, then pairwise. Refreshing clears progress.

When should I use FeaturePriority?

Use FeaturePriority to compare a shortlist of potential features.

  • 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

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-keystring—Required. UUID of the survey from your dashboard
completionMessagecompletion-messagestring'Thank you for your response!'Message shown after submission
thankYouMessagethank-you-messagestring—Overrides the saved survey thank-you message
sessionData—Record<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

Set these variables on the component or a parent:

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

CSS Parts are grouped by Kano, MaxDiff, pairwise, and shared UI:

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);
}

Completion and validation styles

CSS PartElement
success-messageSubmission confirmation
survey-field-error-messageRespondent-field validation feedback
completion-headingCompletion message heading

Use --sf-success-bg, --sf-success-border, and --sf-success-text for the confirmation background, border, and text.

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