# Selection Guide UI Plugin

The Selection Guide UI is a lightweight, zero-dependency JavaScript widget that renders size recommendations directly on your product detail pages. It fetches data from the [Size Recommender API](https://docs.parcellab.com/docs/developers/size-recommender/size-recommender/api) and displays fit category, a visual scale, confidence score, and an AI-generated customer feedback summary. The widget also supports localized default strings, configurable fallbacks for missing data, and per-section visibility controls.

This is an **open-source reference implementation** in the [parcelLab Embedded UI Snippets repository](https://github.com/parcelLab/parcellab-embedded-ui-snippets/tree/main/packages/selection-guide-ui). Use it as-is, or as a starting point for building your own custom integration against the API.

**Sample with short info text**

|                                                                                                                                                                                                                                                                                                   |                                                                                                                                                                                                                                                                                 |
| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| ![Short text, neutral color, comfortable spacing, inline display](https://1242524681-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Few6OnyRUe6InzC96WnMr%2Fuploads%2Fgit-blob-c6ea2c3334a7d14c42c783a0b4a2eb0e13489d1f%2Fshorttext_neutral_comfortable_inline.png?alt=media) | ![Short text, colored, compact spacing, card display](https://1242524681-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Few6OnyRUe6InzC96WnMr%2Fuploads%2Fgit-blob-c235af9ef18f0b0e0faf614f49ea25d31bd957d9%2Fshorttext_colored_compact_card.png?alt=media) |
|                                                                                                                         neutral color, comfortable spacing, inline display                                                                                                                        |                                                                                                                      colored, compact spacing, card display                                                                                                                     |

**Sample with longer info text**

|                                                                                                                                                                                                                                                                                         |                                                                                                                                                                                                                                                                               |
| :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| ![Long text, neutral color, compact spacing, inline display](https://1242524681-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Few6OnyRUe6InzC96WnMr%2Fuploads%2Fgit-blob-0b342c5284a465ac4ec6d1fbbb554dd2a32cfdb8%2Flongtext_neutral_compact_inline.png?alt=media) | ![Long text, colored, compact spacing, card display](https://1242524681-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Few6OnyRUe6InzC96WnMr%2Fuploads%2Fgit-blob-7a1cc45b1634640b641a12351b99debcc6101b75%2Flongtext_colored_compact_card.png?alt=media) |
|                                                                                                                      neutral color, compact spacing, inline display                                                                                                                     |                                                                                                                     colored, compact spacing, card display                                                                                                                    |

**Missing data**

|                                                                                                                                                                                                                                                         |                                                                                                                                                                                                                                                            |
| :-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: |
| ![Empty state with default fit info](https://1242524681-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Few6OnyRUe6InzC96WnMr%2Fuploads%2Fgit-blob-50b3204c3bd323273649f11d7a853074574ba58f%2Femptystate_show-default.png?alt=media) | ![Empty state with missing data notice](https://1242524681-files.gitbook.io/~/files/v0/b/gitbook-x-prod.appspot.com/o/spaces%2Few6OnyRUe6InzC96WnMr%2Fuploads%2Fgit-blob-6e1ca7c02d03912e1613fcb7af70121844f6e4bf%2Femptystate_show-missing.png?alt=media) |
|                                                                                                 `notFoundMode: 'true-to-size'` — shows default fit info                                                                                                 |                                                                                                     `notFoundMode: 'empty'` — shows missing data notice                                                                                                    |

## Installation

The widget ships as a standalone JavaScript bundle. No build tools or package managers are required.

Available bundle files:

* `https://cdn.parcellab.com/js/selection-guide-ui/v1/size-recommender.iife.js`
* `https://cdn.parcellab.com/js/selection-guide-ui/v1/size-recommender.esm.js`

{% tabs %}
{% tab title="Script tag (recommended)" %}
Add a single `<script>` tag to your page. The IIFE build auto-initializes any element with the `data-size-recommender` attribute:

```html
<div
  data-size-recommender
  data-account-id="YOUR_ACCOUNT_ID"
  data-product-id="YOUR_PRODUCT_ID"
  data-not-found-mode="true-to-size"
></div>

<script
  src="https://cdn.parcellab.com/js/selection-guide-ui/v1/size-recommender.iife.js"
  defer
></script>
```

{% endtab %}

{% tab title="JavaScript API" %}
For more control, use the JavaScript API to initialize the widget programmatically:

```html
<div id="size-recommender"></div>

<script src="https://cdn.parcellab.com/js/selection-guide-ui/v1/size-recommender.iife.js"></script>
<script>
  const widget = window.SizeRecommender.init({
    target: '#size-recommender',
    accountId: 'YOUR_ACCOUNT_ID',
    productId: 'YOUR_PRODUCT_ID',
    notFoundMode: 'true-to-size'
  });
</script>
```

{% endtab %}

{% tab title="ES module" %}
If you want to load the ESM build directly in the browser, import it from the CDN:

```html
<div id="size-recommender"></div>

<script type="module">
  import { init } from 'https://cdn.parcellab.com/js/selection-guide-ui/v1/size-recommender.esm.js';

  const widget = init({
    target: '#size-recommender',
    accountId: 'YOUR_ACCOUNT_ID',
    productId: 'YOUR_PRODUCT_ID',
    notFoundMode: 'true-to-size'
  });
</script>
```

{% endtab %}
{% endtabs %}

## Live demo

Try the interactive demo to preview different configurations and copy embed code:

{% embed url="<https://cdn.parcellab.com/playground/selection-guide-ui/index.html>" %}

The demo page is maintained in the [Selection Guide UI module](https://github.com/parcelLab/parcellab-embedded-ui-snippets/tree/main/packages/selection-guide-ui) in the parcelLab Embedded UI Snippets repository.

## Configuration

All options can be set via JavaScript or as HTML `data-*` attributes.

| Option               | Data attribute             | Type                                    | Default                       | Description                                                                                                            |
| -------------------- | -------------------------- | --------------------------------------- | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| `target`             | —                          | `string \| HTMLElement`                 | —                             | **Required** (JS only). CSS selector or DOM element to mount the widget.                                               |
| `accountId`          | `data-account-id`          | `number \| string`                      | —                             | **Required**. Your parcelLab account ID.                                                                               |
| `productId`          | `data-product-id`          | `string`                                | —                             | **Required**. Product identifier passed to the API.                                                                    |
| `articleName`        | `data-article-name`        | `string`                                | —                             | Legacy alias for `productId`. Still accepted for backwards compatibility.                                              |
| `notFoundMode`       | `data-not-found-mode`      | `'empty' \| 'true-to-size' \| 'hidden'` | `'true-to-size'`              | Behavior when no recommendation exists (see [404 handling](#404-handling)).                                            |
| `appearance`         | `data-appearance`          | `'neutral' \| 'colored' \| 'alert'`     | `'colored'`                   | Visual style. `neutral` is grayscale, `colored` uses a gradient track, and `alert` uses stronger fit-sensitive colors. |
| `density`            | `data-density`             | `'compact' \| 'comfortable'`            | `'comfortable'`               | Spacing. `compact` is ideal for tighter PDP layouts.                                                                   |
| `surface`            | `data-surface`             | `'subtle' \| 'plain'`                   | `'plain'`                     | `subtle` renders a light card background; `plain` renders inline.                                                      |
| `locale`             | `data-locale`              | `string`                                | `'en'`                        | Locale for default message strings. Supported locales: `en`, `de`, `fr`, `it`, `es`.                                   |
| `showPill`           | `data-show-pill`           | `boolean`                               | `true`                        | Show or hide the fit category pill badge.                                                                              |
| `showScale`          | `data-show-scale`          | `boolean`                               | `true`                        | Show or hide the fit scale.                                                                                            |
| `showRecommendation` | `data-show-recommendation` | `boolean`                               | `true`                        | Show or hide the recommendation callout block.                                                                         |
| `showSummary`        | `data-show-summary`        | `boolean`                               | `true`                        | Show or hide the AI-generated summary inside the recommendation block.                                                 |
| `messages`           | `data-messages`            | `Partial<WidgetMessages>`               | —                             | Override any default text (title, labels, headings). Pass as JSON string in HTML.                                      |
| `theme`              | `data-theme`               | `Partial<WidgetTheme>`                  | —                             | Override CSS tokens (colors, radius). Pass as JSON string in HTML.                                                     |
| `className`          | `data-class-name`          | `string`                                | —                             | Extra CSS classes added to the root element.                                                                           |
| `apiBaseUrl`         | `data-api-base-url`        | `string`                                | `'https://api.parcellab.com'` | Override the API base URL (for testing or staging).                                                                    |

{% hint style="info" %}
The legacy `articleName` / `data-article-name` option is still accepted as an alias for `productId`, but `productId` is recommended for new integrations.
{% endhint %}

### HTML attribute example

When using the auto-initializing script tag, configure the widget directly on the target element:

```html
<div
  data-size-recommender
  data-account-id="YOUR_ACCOUNT_ID"
  data-product-id="YOUR_PRODUCT_ID"
  data-not-found-mode="hidden"
  data-appearance="alert"
  data-density="comfortable"
  data-surface="plain"
  data-locale="de"
  data-show-summary="false"
></div>

<script
  src="https://cdn.parcellab.com/js/selection-guide-ui/v1/size-recommender.iife.js"
  defer
></script>
```

### Messages customization

Override any text label using the `messages` option:

```javascript
window.SizeRecommender.init({
  target: '#size-recommender',
  accountId: 'YOUR_ACCOUNT_ID',
  productId: 'YOUR_PRODUCT_ID',
  messages: {
    title: 'How It Fits',
    recommendationHeadingSmall: 'Consider sizing up',
    recommendationHeadingLarge: 'Consider sizing down',
    recommendationHeadingTrue: 'Should fit as expected',
    confidenceText: '{value}% confident based on store feedback'
  }
});
```

### Theme customization

Override visual tokens to match your brand:

```javascript
window.SizeRecommender.init({
  target: '#size-recommender',
  accountId: 'YOUR_ACCOUNT_ID',
  productId: 'YOUR_PRODUCT_ID',
  theme: {
    backgroundColor: '#f6f6f6',
    textColor: '#222222',
    mutedTextColor: '#666666',
    borderColor: '#e4e4e4',
    radius: '12px'
  }
});
```

## Appearance modes

* **`neutral`** — grayscale style that blends with most page designs.
* **`colored`** (default) — uses a gradient on the fit scale and tinted badge for a more visual indication.
* **`alert`** — uses stronger fit-sensitive colors to make likely size mismatch more prominent.

Both modes are shown in the comparison table above.

## 404 handling

When the API returns no recommendation for a product, the widget supports three modes:

* **`true-to-size`** (default) — shows a "likely true to size" fallback without confidence or AI summary. Useful as a safe default assumption.
* **`empty`** — shows a "no data available" message.
* **`hidden`** — hides the widget entirely until valid data is returned.

If you use `hidden`, the widget becomes visible again automatically when a later `update()` or `refresh()` call receives valid data.

The `true-to-size` and `empty` modes are shown in the first row of the comparison table above.

## Widget instance API

The `init()` call returns a widget instance with methods for dynamic updates:

| Method                  | Description                                                                              |
| ----------------------- | ---------------------------------------------------------------------------------------- |
| `widget.update(config)` | Update configuration (e.g., switch product) and re-fetch. Cancels any in-flight request. |
| `widget.refresh()`      | Re-fetch the recommendation with current configuration.                                  |
| `widget.destroy()`      | Remove the widget from the DOM and clean up event listeners.                             |

### Example: switching products

On single-page applications (SPAs) or pages with product variant selectors, update the widget when the product changes:

```javascript
const widget = window.SizeRecommender.init({
  target: '#size-recommender',
  accountId: 'YOUR_ACCOUNT_ID',
  productId: 'Original Product'
});

// When the user selects a different variant:
document.querySelector('#variant-select').addEventListener('change', (e) => {
  widget.update({ productId: e.target.value });
});
```

## Styling

The widget renders in the **light DOM** (not Shadow DOM), so your page's typography inherits naturally. You can target the widget's elements directly with CSS.

### CSS classes

The widget uses [BEM-style](https://getbem.com/) class names under the `.pl-size-recommender` namespace. All classes are applied in the light DOM, so you can target them directly from your stylesheet.

#### Root modifier classes

These are applied to the root element and reflect the current configuration and state:

| Class                                       | Description                                         |
| ------------------------------------------- | --------------------------------------------------- |
| `.pl-size-recommender`                      | Root element (always present)                       |
| `.pl-size-recommender--neutral`             | Neutral (grayscale) appearance                      |
| `.pl-size-recommender--colored`             | Colored appearance with gradient track              |
| `.pl-size-recommender--alert`               | Alert appearance with stronger fit-sensitive colors |
| `.pl-size-recommender--density-compact`     | Compact spacing                                     |
| `.pl-size-recommender--density-comfortable` | Comfortable spacing                                 |
| `.pl-size-recommender--surface-subtle`      | Card background with border                         |
| `.pl-size-recommender--surface-plain`       | Inline, no card background                          |
| `.pl-size-recommender--state-loading`       | Widget is loading data                              |
| `.pl-size-recommender--state-ready`         | Data loaded successfully                            |
| `.pl-size-recommender--state-fallback-true` | Showing true-to-size fallback (no data)             |
| `.pl-size-recommender--state-empty`         | Showing empty/no-data state                         |
| `.pl-size-recommender--state-error`         | API error occurred                                  |
| `.pl-size-recommender--state-hidden`        | Widget is hidden because `notFoundMode` is `hidden` |
| `.pl-size-recommender--fit-small`           | Product runs small                                  |
| `.pl-size-recommender--fit-true`            | Product is true to size                             |
| `.pl-size-recommender--fit-large`           | Product runs large                                  |
| `.pl-size-recommender--fit-unknown`         | Fit category unknown                                |

#### Element classes

These target individual parts of the widget:

| Class                                          | Description                                                             |
| ---------------------------------------------- | ----------------------------------------------------------------------- |
| `.pl-size-recommender__header`                 | Header row containing title and pill                                    |
| `.pl-size-recommender__title`                  | "How It Fits" heading                                                   |
| `.pl-size-recommender__pill`                   | Fit category badge (e.g., "True to size")                               |
| `.pl-size-recommender__scale`                  | Scale container (labels + track)                                        |
| `.pl-size-recommender__scale-labels`           | "Runs Small / True to Size / Runs Large" labels                         |
| `.pl-size-recommender__track`                  | The horizontal fit bar                                                  |
| `.pl-size-recommender__marker`                 | Position dot on the track                                               |
| `.pl-size-recommender__recommendation`         | Recommendation callout card                                             |
| `.pl-size-recommender__recommendation-header`  | Header row inside the recommendation                                    |
| `.pl-size-recommender__recommendation-icon`    | Fit direction icon (arrow up/down/dash)                                 |
| `.pl-size-recommender__recommendation-title`   | Recommendation heading (e.g., "Consider sizing up")                     |
| `.pl-size-recommender__recommendation-meta`    | Confidence text (e.g., "89% confident based on real customer feedback") |
| `.pl-size-recommender__recommendation-summary` | AI-generated customer feedback summary                                  |

#### Hiding elements with CSS

If you want to hide specific parts of the widget, prefer the built-in visibility options (`showPill`, `showScale`, `showRecommendation`, `showSummary`). You can also use CSS and `display: none` on the relevant class:

```css
/* Hide the confidence score */
.pl-size-recommender__recommendation-meta {
  display: none;
}

/* Hide the AI summary text */
.pl-size-recommender__recommendation-summary {
  display: none;
}

/* Hide the fit category pill badge */
.pl-size-recommender__pill {
  display: none;
}

/* Hide the entire scale bar */
.pl-size-recommender__scale {
  display: none;
}

/* Hide the widget entirely when it enters the hidden state */
.pl-size-recommender--state-hidden {
  display: none;
}
```

### CSS variables

Override these on `.pl-size-recommender` or any ancestor element:

| Variable                                  | Description                                     |
| ----------------------------------------- | ----------------------------------------------- |
| `--plsr-background`                       | Widget background color                         |
| `--plsr-recommendation-background`        | Recommendation callout background               |
| `--plsr-border`                           | Border color                                    |
| `--plsr-text`                             | Primary text color                              |
| `--plsr-muted-text`                       | Secondary text color                            |
| `--plsr-accent`                           | Accent color for highlights                     |
| `--plsr-badge-background`                 | Fit category badge background                   |
| `--plsr-badge-text`                       | Fit category badge text color                   |
| `--plsr-track`                            | Scale track color (neutral mode)                |
| `--plsr-track-start` / `--plsr-track-end` | Track gradient colors (colored and alert modes) |
| `--plsr-radius`                           | Border radius                                   |

### Example: custom styling

```css
.pl-size-recommender {
  --plsr-background: #fafafa;
  --plsr-text: #1a1a1a;
  --plsr-accent: #3D3AD3;
  --plsr-radius: 16px;
  font-family: 'Your Brand Font', sans-serif;
}
```

## Building your own integration

This widget is an **open-source reference implementation**. If you need deeper customization beyond what the configuration options offer, you have several paths:

1. **Use the API directly** — Call the [Size Recommender API](https://docs.parcellab.com/docs/developers/size-recommender/size-recommender/api) from your own frontend code and render the response however you like.
2. **Fork the embedded UI snippets repository** — Start from the Selection Guide UI module and customize the rendering, styling, and behavior to match your exact requirements.
3. **Use as a library** — Import the ESM build and override messages, theme, and CSS to fit your design system.

The [source code](https://github.com/parcelLab/parcellab-embedded-ui-snippets/tree/main/packages/selection-guide-ui) is organized into clear modules (API client, config resolution, model transformation, rendering) that you can reference or reuse.

## Source code

* **Repository:** [github.com/parcelLab/parcellab-embedded-ui-snippets/tree/main/packages/selection-guide-ui](https://github.com/parcelLab/parcellab-embedded-ui-snippets/tree/main/packages/selection-guide-ui)
* **Live demo:** [cdn.parcellab.com/playground/selection-guide-ui](https://cdn.parcellab.com/playground/selection-guide-ui/index.html)
* **License:** MIT
