npm package: @sensefolks/userchoice
Description: Deeply understand user preferences. Design better offerings or refine existing ones
Author: SenseFolks
Latest version: 1.0.0

# @sensefolks/userchoice

A web component for running Choice-Based Conjoint (CBC) surveys. CBC analysis helps you understand which product attributes matter most to your respondents by presenting them with a series of trade-off tasks.

## How it works

Each respondent sees a series of choice tasks. Each task presents three product alternatives (each with a different combination of attribute values) plus a "None of these options" choice. After completing all tasks, the response data is used to compute attribute importance scores — revealing which attributes drive preference.

## Installation

### CDN

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <script type="module" src="https://unpkg.com/@sensefolks/userchoice/dist/sf-userchoice/sf-userchoice.esm.js"></script>
  </head>
  <body>
    <sf-userchoice survey-key="your-survey-uuid-here"></sf-userchoice>
  </body>
</html>
```

### NPM

```bash
npm install @sensefolks/userchoice
```

## Usage

### Vanilla JS

```html
<script type="module">
  import { defineCustomElements } from '@sensefolks/userchoice/loader';
  defineCustomElements();
</script>

<sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" completion-message="Thanks for your feedback!"></sf-userchoice>
```

### React

```tsx
import { defineCustomElements } from '@sensefolks/userchoice/loader';
defineCustomElements();

export function SurveyPage() {
  return <sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" completion-message="Thanks for your feedback!" />;
}
```

### Vue

```vue
<template>
  <sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" completion-message="Thanks for your feedback!" />
</template>

<script setup>
import { defineCustomElements } from '@sensefolks/userchoice/loader';
defineCustomElements();
</script>
```

### Angular

In `main.ts`:

```ts
import { defineCustomElements } from '@sensefolks/userchoice/loader';
defineCustomElements();
```

In your component template:

```html
<sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d" completion-message="Thanks for your feedback!"></sf-userchoice>
```

In your `AppModule`:

```ts
import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';

@NgModule({
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
})
export class AppModule {}
```

## API Reference

### Properties

| Property            | Attribute            | Type     | Default                          | Required | Description                                                             |
| ------------------- | -------------------- | -------- | -------------------------------- | -------- | ----------------------------------------------------------------------- |
| `surveyKey`         | `survey-key`         | `string` | —                                | Yes      | The survey's public key (UUID). Obtained from the SenseFolks dashboard. |
| `completionMessage` | `completion-message` | `string` | `"Thank you for your response!"` | No       | Message displayed to the respondent after they submit their responses.  |

### Events

The component does not emit custom DOM events. Survey submission is handled internally.

### CSS Shadow Parts

The component uses shadow DOM and exposes the following CSS parts for styling:

| Part                 | Description                                                         |
| -------------------- | ------------------------------------------------------------------- |
| `survey-container`   | Outer wrapper for the entire survey                                 |
| `step`               | Wrapper for each step (choice task, respondent details, completion) |
| `task-heading`       | The `<h2>` heading for each choice task                             |
| `task-instructions`  | Instruction text above the alternatives                             |
| `concepts-container` | Wrapper for all alternative cards in a task                         |
| `concept`            | An individual alternative card                                      |
| `concept-selected`   | Applied to the currently selected alternative card                  |
| `concept-unselected` | Applied to unselected alternative cards                             |
| `none-option`        | The "None of these options" card                                    |
| `concept-header`     | Header area of an alternative card                                  |
| `concept-title`      | Title text within an alternative card                               |
| `concept-attributes` | Attribute list within an alternative card                           |
| `concept-attribute`  | A single attribute row within a card                                |
| `attribute-label`    | The attribute name label                                            |
| `attribute-value`    | The attribute variant value                                         |
| `progress-indicator` | Wrapper for the progress bar and text                               |
| `progress-bar`       | The progress bar track                                              |
| `progress-fill`      | The filled portion of the progress bar                              |
| `progress-text`      | "Task X of Y" text                                                  |
| `button`             | All buttons                                                         |
| `next-button`        | The "Next Task" / "Submit" button                                   |
| `back-button`        | The "Previous" button                                               |
| `retry-button`       | The retry button shown on error                                     |
| `error-container`    | Wrapper shown when an error occurs                                  |
| `error-message`      | The error message text                                              |
| `loading-message`    | The loading state text                                              |
| `branding`           | SenseFolks branding footer (shown when enabled)                     |

### Styling example

```css
sf-userchoice::part(concept) {
  border: 2px solid #e2e8f0;
  border-radius: 8px;
  padding: 16px;
  cursor: pointer;
}

sf-userchoice::part(concept-selected) {
  border-color: #3b82f6;
  background-color: #eff6ff;
}

sf-userchoice::part(next-button) {
  background-color: #3b82f6;
  color: white;
  border-radius: 6px;
  padding: 10px 24px;
}
```

## Minimal working example

```html
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>CBC Survey</title>
    <script type="module" src="https://unpkg.com/@sensefolks/userchoice/dist/sf-userchoice/sf-userchoice.esm.js"></script>
  </head>
  <body>
    <sf-userchoice survey-key="a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"></sf-userchoice>
  </body>
</html>
```

## Accessibility

The component includes keyboard navigation support:

- `Arrow keys` — move focus between alternative cards
- `Enter` / `Space` — select the focused alternative
- All interactive elements have appropriate `role`, `aria-checked`, `aria-label`, and `tabindex` attributes
- A live region announces selections to screen readers

## Browser support

Supports all modern browsers (Chrome, Firefox, Safari, Edge). Requires custom elements support.