npm package: @sensefolks/openfeedback
Description: Ask open-ended questions to understand user pain points
Author: SenseFolks
Latest version: 1.0.0

# sf-openfeedback

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

> Open-ended feedback survey web component for collecting qualitative user insights through free-text responses.

## Features

- 📝 Open-ended text feedback collection
- ✅ Built-in form validation with accessible error messages
- 👤 Optional respondent details collection (text, email, number, dropdown, radio, checkbox)
- ♿ Keyboard navigable with ARIA labels and live regions
- 🎨 Customizable via CSS Parts
- 📱 Responsive design
- 🌐 Works with any framework

## Installation

### NPM

```bash
npm install @sensefolks/openfeedback
```

### CDN (Script Tag)

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

<!-- Legacy browsers -->
<script nomodule src="https://unpkg.com/@sensefolks/openfeedback/dist/sf-openfeedback/sf-openfeedback.js"></script>
```

Or via jsDelivr:

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

## Usage

### HTML

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

### React

```jsx
import '@sensefolks/openfeedback';

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

### Vue

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

<script>
import '@sensefolks/openfeedback';
export default { name: 'App' };
</script>
```

### Angular

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

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

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

## API

### Properties

| Property            | Attribute            | Type      | Default                          | Description                                                     |
| ------------------- | -------------------- | --------- | -------------------------------- | --------------------------------------------------------------- |
| `surveyKey`         | `survey-key`         | `string`  | —                                | UUID of the survey to load (required)                           |
| `completionMessage` | `completion-message` | `string`  | `'Thank you for your response!'` | Message shown after successful submission                       |
| `enableEvents`      | `enable-events`      | `boolean` | `true`                           | Whether to emit custom events (`sfReady`, `sfStepChange`, etc.) |

### Events

All events are emitted when `enableEvents` is `true` (the default).

| Event          | Detail                                                       | Description                                                         |
| -------------- | ------------------------------------------------------------ | ------------------------------------------------------------------- |
| `sfReady`      | `{ surveyKey, question }`                                    | Survey loaded and ready to display                                  |
| `sfStepChange` | `{ surveyKey, previousStep, currentStep, currentStepIndex }` | User navigated between steps                                        |
| `sfInput`      | `{ surveyKey, value, characterCount }`                       | User typed in the feedback textarea                                 |
| `sfSubmit`     | `{ surveyKey, feedback, completionTimeSeconds }`             | Survey successfully submitted                                       |
| `sfError`      | `{ surveyKey, errorType, errorMessage }`                     | Error occurred (errorType: `'load'`, `'submit'`, or `'validation'`) |

### Survey Steps

The component progresses through steps based on the survey configuration:

1. **Question** — Open-ended textarea for feedback (always shown)
2. **Respondent details** — Collects user info like name, email, etc. (shown only when configured)
3. **Completion** — Displays the completion message

### CSS Parts

Style the component externally using `::part()`:

```css
/* Layout */
sf-openfeedback::part(container) {
} /* Main wrapper (Host) */
sf-openfeedback::part(step) {
} /* Any step container */
sf-openfeedback::part(question-step) {
} /* Question step */
sf-openfeedback::part(respondent-details-step) {
} /* Respondent details step */
sf-openfeedback::part(completion-step) {
} /* Completion step */
sf-openfeedback::part(button-container) {
} /* Button row wrapper */

/* Headings */
sf-openfeedback::part(heading) {
} /* All step headings */
sf-openfeedback::part(question-heading) {
}
sf-openfeedback::part(respondent-heading) {
}
sf-openfeedback::part(completion-heading) {
}

/* Textarea */
sf-openfeedback::part(input) {
} /* All input elements */
sf-openfeedback::part(textarea) {
} /* Feedback textarea */

/* Respondent details form */
sf-openfeedback::part(respondent-fields-container) {
}
sf-openfeedback::part(field) {
} /* Form field container */
sf-openfeedback::part(field-label) {
} /* Form field labels */
sf-openfeedback::part(required-indicator) {
} /* Required asterisk */
sf-openfeedback::part(form-input) {
} /* Text/email/number inputs */
sf-openfeedback::part(select) {
}
sf-openfeedback::part(form-select) {
} /* Dropdown selects */
sf-openfeedback::part(radio-group) {
}
sf-openfeedback::part(radio-option) {
}
sf-openfeedback::part(radio-input) {
}
sf-openfeedback::part(radio-label) {
}
sf-openfeedback::part(checkbox-group) {
}
sf-openfeedback::part(checkbox-option) {
}
sf-openfeedback::part(checkbox-input) {
}
sf-openfeedback::part(checkbox-label) {
}

/* Buttons */
sf-openfeedback::part(button) {
} /* All buttons */
sf-openfeedback::part(next-button) {
} /* Next / Submit on question step */
sf-openfeedback::part(back-button) {
} /* Back button */
sf-openfeedback::part(submit-button) {
} /* Submit on respondent details */
sf-openfeedback::part(retry-button) {
} /* Retry on error */

/* Messages & States */
sf-openfeedback::part(message) {
} /* All messages (loading, error) */
sf-openfeedback::part(loading-message) {
}
sf-openfeedback::part(error-message) {
} /* Error text (validation + load/submit) */
sf-openfeedback::part(error-container) {
} /* Error wrapper with retry button */
sf-openfeedback::part(empty-message) {
} /* No respondent details message */
sf-openfeedback::part(announcements) {
} /* Screen reader live region */

/* Branding */
sf-openfeedback::part(branding) {
} /* Branding container */
sf-openfeedback::part(branding-link) {
} /* Branding link */
sf-openfeedback::part(branding-logo) {
} /* Branding logo SVG */
```

## Accessibility

- Full keyboard navigation (Tab between fields, Enter to submit)
- ARIA `aria-invalid` and `aria-describedby` on fields with validation errors
- `aria-live="polite"` region for screen reader announcements on step changes and submissions
- Focus indicators (`outline`) on all interactive elements
- High contrast mode support via `@media (prefers-contrast: high)`
- Respects `prefers-reduced-motion`

## Browser Support

- Chrome 88+
- Firefox 85+
- Safari 14+
- Edge 88+
- IE11 (with ES5 build)

## License

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