@htmlbricks/hb-product-comparison
v0.76.5
Published
Product comparison matrix: columns per product (model, description, promotion, price, purchase button) and rows per feature header with icons or text from each product’s characteristics. Supports preferred product and sale styling; dispatches purchaseClic
Readme
hb-product-comparison (product-comparison)
Category: commerce
Tags: commerce, product
Package: @htmlbricks/hb-product-comparison
Overview
Product comparison matrix: one column per product (model, description, optional “preferred choice” / “on sale” callout, currency + price, purchase button) and one row per feature header. Each cell shows a Bootstrap Icon or plain text based on that product’s characteristics entry for the header’s id. Supports highlighting a preferred plan (preferredProductId) and sale pricing (promotion.originalPrice with strikethrough on the original amount). Dispatches purchaseClick with the chosen product’s id. Desktop: wide table-style grid with a corner slot. Mobile: one stacked card per product with the same headers rendered inline.
Custom element
<hb-product-comparison …></hb-product-comparison>Attributes
Attributes use snake_case. In plain HTML, headers, products, and options are passed as JSON strings (the component parses them at runtime). Invalid JSON for headers or products yields empty arrays; invalid options JSON falls back to {}. When options is parsed and currency is missing or empty, it defaults to €.
| Attribute | Required | Description |
|-----------|----------|-------------|
| headers | Yes | JSON array of Header objects: { id: string; label: string }[]. Each id is a row key; label is shown in the first column (desktop) or beside each value (mobile). |
| products | Yes | JSON array of Product objects. See Product. |
| options | Yes | JSON object Options: optional currency (string), optional preferredProductId (string). |
| id | No | Optional host id (typed on the component interface). |
| style | No | Optional inline host style (typed on the component interface). |
Header
| Field | Type | Description |
|-------|------|-------------|
| id | string | Key into each product’s characteristics for that comparison row. |
| label | string | Human-readable row title. |
Product
| Field | Type | Description |
|-------|------|-------------|
| id | string | Stable product identifier; emitted on purchaseClick. |
| model | string | Shown as the column/card title (uppercased styling in the template). |
| price | number | Current price shown next to options.currency. |
| characteristics | Record<string, string> | Map from header id to cell value; see Characteristic values. |
| description | string (optional) | Subtitle under the model (muted). |
| note | string (optional) | Present in typings; not read by the current template. |
| promotion | object (optional) | Sale block: originalPrice (number, required for strikethrough), optional type, optional note. Only originalPrice drives the “on sale” label and struck-through price in the UI. |
| columnColor | object (optional) | Optional per-column colors in typings (headerBackground, columnBackground, purchaseButton); not applied by the current markup. |
Options
| Field | Type | Description |
|-------|------|-------------|
| currency | string (optional) | Prefix/symbol before the price; defaults to € when omitted after parse. |
| preferredProductId | string (optional) | When it matches a product’s id, that column/card shows “preferred choice” in the badge area (desktop layout reserves badge height when any product has a promotion or a preferred id is set). |
Characteristic values
For each header id, the cell value product.characteristics[header.id] is interpreted as follows (string match):
| Value | UI |
|-------|-----|
| "valid" | Check icon (bi-check). |
| missing or "disabled" | Dash icon (bi-dash). |
| "blocked" | X icon (bi-x). |
| "star" | Filled star (bi-star-fill). |
| "plus" | Plus icon (bi-plus). |
| any other string | Raw text in the cell. |
Events
| Event | detail |
|-------|----------|
| purchaseClick | { id: string } — the id of the product whose Acquista button was clicked. |
Listen with addEventListener("purchaseClick", …) or your framework’s DOM event binding.
Slots
| Slot | Description |
|------|-------------|
| corner | Top-left cell of the desktop grid (first column of the header row). Empty by default; use for a label such as “Features” or branding. |
Styling (Bulma CSS variables)
The component uses Bulma inside the shadow root. Theme it with public --bulma-* variables (see Bulma CSS variables). Defaults below match extra/docs.ts.
| Variable | Type | Default | Role |
|----------|------|---------|------|
| --bulma-primary | color | #00d1b2 | Purchase button background (button is-primary). |
| --bulma-text | color | #363636 | Body text (models, prices, labels, non-icon cell text). |
| --bulma-column-gap | length | 0.75rem | Horizontal gap between comparison columns. |
| --bulma-background | color | #ffffff | Card / column surfaces. |
| --bulma-radius | length | 0.375rem | Radius for buttons and shells. |
| --bulma-border | color | #dbdbdb | Grid borders and dividers (desktop table and mobile cards). |
Icons: Bootstrap Icons are loaded for the feature cells (and the desktop layout also links the icon font in the component head). Ensure network access to the CDN or mirror the font if you bundle offline.
CSS ::part
| Part | Description |
|------|-------------|
| container | Root comparison wrapper (desktop columns layout or mobile stacked cards). |
| col | A single column cell (product header stack or a feature cell). |
| row | A horizontal strip of columns (desktop header row or one feature row). |
| mobile-card | One stacked mobile card per product (image header block plus feature rows). |
Layout and copy
- Desktop:
container/columns/columnwithis-hidden-touch; mobile: separate block withis-hidden-desktop, one card per product. - Purchase control label in the template is Acquista (Italian). Override visually via Bulma/CSS as needed; there is no i18n dictionary on this component in metadata.
Minimal HTML example
<hb-product-comparison
headers='[{"id":"f1","label":"Feature 1"}]'
products='[{"id":"p1","model":"Basic","price":10,"characteristics":{"f1":"valid"}}]'
options='{"currency":"€"}'
></hb-product-comparison>Example with preferred plan and promotion-shaped data
<hb-product-comparison
headers='[{"id":"cpu","label":"CPU"},{"id":"ram","label":"RAM"}]'
products='[
{"id":"s","model":"S","price":399,"characteristics":{"cpu":"4 cores","ram":"8 GB"}},
{"id":"l","model":"L","price":899,"characteristics":{"cpu":"8 cores","ram":"32 GB"}}
]'
options='{"currency":"€","preferredProductId":"l"}'
></hb-product-comparison><hb-product-comparison
headers='[{"id":"f1","label":"Included"}]'
products='[
{"id":"sale","model":"Promo","price":59,"description":"Limited","promotion":{"originalPrice":99,"type":"percent","note":"Spring sale"},"characteristics":{"f1":"valid"}},
{"id":"std","model":"Standard","price":99,"characteristics":{"f1":"valid"}}
]'
options='{"currency":"€"}'
></hb-product-comparison>TypeScript authoring types
Authoring interfaces for this element live in types/webcomponent.type.d.ts (Component, Header, Product, Options, Events). Keep JSON payloads in HTML aligned with those shapes.
License
Package metadata references Apache-2.0 (see LICENSE.md in the published package when consuming from npm).
