npm package: @sensefolks/featurepriority
Description: Identify high-impact features that drive user satisfaction and retention
Author: SenseFolks
Latest version: 1.0.0

# sf-featurepriority

[![npm version](https://img.shields.io/npm/v/@sensefolks/featurepriority.svg)](https://www.npmjs.com/package/@sensefolks/featurepriority)
[![npm downloads](https://img.shields.io/npm/dm/@sensefolks/featurepriority.svg)](https://www.npmjs.com/package/@sensefolks/featurepriority)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
[![version](https://img.shields.io/badge/version-2.0.0-blue.svg)](https://www.npmjs.com/package/@sensefolks/featurepriority)

> Identify high-impact features that drive user satisfaction and retention. Supports drag-and-drop ranking and a funneled Kano → MaxDiff → Pairwise methodology.

## Features

- 🎯 Two survey modes: drag-and-drop **ranking** and guided **funneled** (Kano → MaxDiff → Pairwise)
- 🔬 Per-respondent real-time filtering — only features that matter advance to the next step
- 📊 Client-side scoring: Kano classification, MaxDiff utility scores, Bradley-Terry rankings
- 💾 Session persistence via localStorage — respondents can resume where they left off
- ⌨️ Keyboard-accessible throughout
- ♿ Screen reader optimized with ARIA live regions
- 🎨 Customizable via CSS Parts
- 📱 Responsive design
- 🌐 Works with any framework

## How It Works (Funneled Mode)

```
All Features
     │
     ▼
┌─────────────┐
│  Kano Step  │  Respondent rates each feature as functional/dysfunctional
│             │  → Must-Be, Performance, Attractive features advance
└──────┬──────┘
       │ survivors (≥3 features)
       ▼
┌─────────────┐
│ MaxDiff Step│  Best/Worst comparisons across balanced trials
│             │  → Top-N features by utility score advance
└──────┬──────┘
       │ top-N features
       ▼
┌─────────────┐
│Pairwise Step│  Head-to-head matchups → Bradley-Terry ranking
└──────┬──────┘
       │
       ▼
  Final Ranking
```

If fewer than 3 features survive Kano, the survey ends early with a Kano-only result. If fewer than 4 survive MaxDiff, the survey skips to Pairwise directly.

## Survey Modes

### Ranking Mode (default)

Respondents drag and drop features into priority order.

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

Config:

```json
{
  "question": "Which features matter most to you?",
  "surveyMode": "ranking",
  "items": [
    { "title": "Dark mode", "value": "dark-mode" },
    { "title": "Offline support", "value": "offline" }
  ]
}
```

### Funneled Mode

Guided Kano → MaxDiff → Pairwise sequence.

Config:

```json
{
  "question": "Which features matter most to you?",
  "surveyMode": "funneled",
  "itemsPerTrial": 4,
  "topNThreshold": 3,
  "items": [
    { "title": "Dark mode", "description": "Switch to a dark color scheme", "value": "dark-mode" },
    { "title": "Offline support", "value": "offline" },
    { "title": "Collaboration", "value": "collab" },
    { "title": "API access", "value": "api" }
  ]
}
```

- `itemsPerTrial`: features shown per MaxDiff trial (3–5, default 4)
- `topNThreshold`: features advancing from MaxDiff to Pairwise (min 2, < item count)
- `items`: 4–7 features for funneled mode, 2–7 for ranking mode

## Installation

### NPM

```bash
npm install @sensefolks/featurepriority
```

### CDN

```html
<!-- ES modules -->
<script type="module" src="https://unpkg.com/@sensefolks/featurepriority/dist/sf-featurepriority/sf-featurepriority.esm.js"></script>
<!-- Legacy -->
<script nomodule src="https://unpkg.com/@sensefolks/featurepriority/dist/sf-featurepriority/sf-featurepriority.js"></script>
```

jsDelivr:

```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@sensefolks/featurepriority/dist/sf-featurepriority/sf-featurepriority.esm.js"></script>
```

## Usage

### 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>;
}
```

### Vue

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

<script>
import '@sensefolks/featurepriority';
export default {};
</script>
```

### Angular

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

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

```html
<!-- template -->
<sf-featurepriority survey-key="your-survey-uuid"></sf-featurepriority>
```

## API Properties

| Property            | Attribute            | Type     | Default | Description                    |
| ------------------- | -------------------- | -------- | ------- | ------------------------------ |
| `surveyKey`         | `survey-key`         | `string` | —       | Survey public UUID (required)  |
| `completionMessage` | `completion-message` | `string` | `""`    | Message shown after submission |

## Events

| Event          | Detail                                                      | Description                                        |
| -------------- | ----------------------------------------------------------- | -------------------------------------------------- |
| `sfReady`      | `{ surveyKey, mode }`                                       | Fired when the component loads and config is ready |
| `sfStepChange` | `{ step: 'kano' \| 'maxdiff' \| 'pairwise' \| 'complete' }` | Fired on funneled step transitions                 |
| `sfSubmit`     | `{ surveyKey, responseData }`                               | Fired after successful submission                  |
| `sfError`      | `{ message, code }`                                         | Fired on load or submission errors                 |

## CSS Parts

All interactive elements expose CSS parts for custom styling.

### Shared parts

| Part                 | Description                                  |
| -------------------- | -------------------------------------------- |
| `survey-container`   | Outer wrapper                                |
| `heading`            | Survey question heading                      |
| `instructions`       | Helper text / instructions                   |
| `step-indicator`     | Step progress indicator (e.g. "Step 1 of 3") |
| `progress-indicator` | Item/trial/matchup progress (e.g. "2 of 5")  |
| `nav-button`         | Previous / Next / Submit navigation buttons  |
| `error-message`      | Inline error messages                        |
| `sr-region`          | Screen reader live region                    |
| `step-transition`    | Interstitial transition screen between steps |

### Kano parts

| Part                 | Description                          |
| -------------------- | ------------------------------------ |
| `kano-question`      | Container for one Kano question pair |
| `kano-functional`    | Functional question radio group      |
| `kano-dysfunctional` | Dysfunctional question radio group   |
| `kano-option`        | Individual Kano radio option         |

### MaxDiff parts

| Part             | Description                          |
| ---------------- | ------------------------------------ |
| `maxdiff-trial`  | Container for one MaxDiff trial      |
| `maxdiff-option` | Individual feature option in a trial |
| `best-selector`  | "Most Important" radio selector      |
| `worst-selector` | "Least Important" radio selector     |

### Pairwise parts

| Part                    | Description                            |
| ----------------------- | -------------------------------------- |
| `pairwise-matchup`      | Container for one head-to-head matchup |
| `pairwise-option-left`  | Left option in a matchup               |
| `pairwise-option-right` | Right option in a matchup              |

### Ranking parts

| Part           | Description                |
| -------------- | -------------------------- |
| `ranking-list` | Drag-and-drop ranking list |
| `ranking-item` | Individual ranking item    |

## Accessibility

- All interactive elements are keyboard-navigable
- ARIA live regions announce step transitions, filtering results, and selection confirmations
- Radio groups use descriptive `aria-label` attributes
- Pairwise matchups use `role="group"` with `aria-describedby`
- Step indicators include full context in `aria-label`
- Respects `prefers-reduced-motion` — all animations are disabled when set

## Browser Support

| Browser | Version |
| ------- | ------- |
| Chrome  | 67+     |
| Firefox | 63+     |
| Safari  | 10.1+   |
| Edge    | 79+     |

## License

MIT © [SenseFolks](https://sensefolks.com)