> ## Documentation Index
> Fetch the complete documentation index at: https://quickbutik.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# @quickbutik/kit/elements

> Generated type reference for the web components entry point of @quickbutik/kit 1.8.0: configure(), defineElements() and the element classes.

<Info>
  Generated from the typings of `@quickbutik/kit@1.8.0` as published on npm. Do not edit this page by hand: run `npm run generate` in `tools/kit-api-reference`. For guided, example-led documentation see the [API reference](/kit/reference/client).
</Info>

```ts theme={null}
import { … } from "@quickbutik/kit/elements"
```

## Classes

### ContextRequestEvent

The event itself. A class rather than a `CustomEvent` with a `detail`, because
that is what the protocol specifies and what other libraries (Lit's
`@lit/context`, notably) listen for — an app already using Lit contexts can
provide to, or consume from, these elements without an adapter.

#### Extends

* `Event`

#### Type Parameters

| Type Parameter |
| - |
| `T` |

#### Constructors

##### Constructor

```ts theme={null}
new ContextRequestEvent<T>(
   context, 
   callback, 
subscribe?): ContextRequestEvent<T>;
```

###### Parameters

| Parameter | Type |
| - | - |
| `context` | [`Context`](#context-1)\<`T`> |
| `callback` | [`ContextCallback`](#contextcallback)\<`T`> |
| `subscribe?` | `boolean` |

###### Returns

[`ContextRequestEvent`](#contextrequestevent)\<`T`>

###### Overrides

```ts theme={null}
Event.constructor
```

#### Properties

##### callback

```ts theme={null}
readonly callback: ContextCallback<T>;
```

##### context

```ts theme={null}
readonly context: Context<T>;
```

##### subscribe

```ts theme={null}
readonly subscribe: boolean;
```

***

### QbAddToCartElement

`<qb-add-to-cart>` — wraps the author's own button and makes it work.

```html theme={null}
<qb-add-to-cart>
  <button>Lägg i varukorg</button>
</qb-add-to-cart>
```

Adds no markup: the `<button>` is the page's, with its own classes and its own
label. What this contributes is the part every storefront otherwise rewrites
— a product with options must not be addable until exactly one variant is
pinned, and the cart needs the variant id rather than the product id.

Any click inside it adds, so a whole card can be the target. It keeps the
inner controls' `disabled` in step with whether adding is possible right now,
and reflects `disabled` / `pending` on itself for styling. Quantity comes from
`quantity="2"`, `data-qb-quantity` on the clicked element, or a nearby
`input[data-qb-quantity-input]`.

`data-qb-action="add-to-cart"` on a plain button inside `<qb-product>` does
the same thing without the disabled-state management — use that when the
button lives somewhere this element cannot wrap.

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbAddToCartElement(): QbAddToCartElement;
```

###### Returns

[`QbAddToCartElement`](#qbaddtocartelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### add()

```ts theme={null}
add(quantity?): Promise<void>;
```

Add the current selection.

A no-op while the selection is incomplete, an add is already in flight, or
the element carries `disabled` — the same guard the inner button's
`disabled` reflects, so a programmatic call cannot get past it either.

###### Parameters

| Parameter | Type |
| - | - |
| `quantity?` | `number` |

###### Returns

`Promise`\<`void`>

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbBuyNowElement

`<qb-buy-now>` — "Köp nu": add the selected variant and go straight to the
hosted checkout.

```html theme={null}
<qb-product slug="cotton-tee">
  <qb-options>…</qb-options>
  <qb-add-to-cart><button>Lägg i varukorgen</button></qb-add-to-cart>
  <qb-buy-now success-url="/order" back-url="/">
    <button>Köp nu</button>
  </qb-buy-now>
</qb-product>
```

The two halves of it are the two elements it is modelled on. From
`<qb-add-to-cart>`: it must sit inside a `<qb-product>`, it is blocked until
exactly one variant is pinned, quantity comes from `quantity="2"`,
`data-qb-quantity` on the clicked element or a nearby
`input[data-qb-quantity-input]`, and the inner controls' `disabled` is kept
in step. From `<qb-checkout-button>`: `success-url`, `back-url` and `theme`
mean the same thing and are resolved by the same code, `no-redirect` stops
short of navigating, and `qb:checkout-started` carries the handoff.

The item goes into the REMEMBERED cart, so a basket the shopper already
filled is checked out with it rather than replaced — "buy this too, now",
which is what the button means on every storefront that has one. The shared
cart store sees the add, so a `<qb-cart-count>` on a page the shopper comes
back to (the back button, `no-redirect`) is not one item short.

Adds no markup. `data-qb-action="buy-now"` on a plain button inside
`<qb-product>` does the same thing without the disabled-state management.

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbBuyNowElement(): QbBuyNowElement;
```

###### Returns

[`QbBuyNowElement`](#qbbuynowelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### buy()

```ts theme={null}
buy(quantity?): Promise<void>;
```

Add the current selection, start the checkout and navigate.

A no-op while the selection is incomplete (`<qb-product>` emits
`qb:add-to-cart-blocked` with the groups still missing a choice), while
one is already in flight, or while the element carries `disabled`. After
it navigates it stays busy until the page is restored from the
back/forward cache, so a click while the checkout loads buys nothing.
`no-redirect` emits `qb:checkout-started` and stays put — for a page that
opens the checkout in a new tab, or runs its own analytics first.

###### Parameters

| Parameter | Type |
| - | - |
| `quantity?` | `number` |

###### Returns

`Promise`\<`void`>

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbCartCountElement

`<qb-cart-count>` — the badge. Sets its own text to the number of items.

The one element here that writes its own content, because there is nothing
else it could be: a badge IS its number. `zero` is reflected as an attribute
so an empty badge can be hidden in CSS.

```html theme={null}
<a href="/cart">Varukorg (<qb-cart-count></qb-cart-count>)</a>
```

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbCartCountElement(): QbCartCountElement;
```

###### Returns

[`QbCartCountElement`](#qbcartcountelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbCartElement

`<qb-cart>` — the shopper's basket as a scope for the markup inside it.

```html theme={null}
<qb-cart>
  <p data-qb-show="cart.empty">Din varukorg är tom.</p>
  <qb-cart-items>
    <template>
      <li>
        <span data-qb-text="item.productTitle"></span>
        <span data-qb-text="item.variantName"></span>
        <input type="number" data-qb-quantity data-qb-value="item.quantity">
        <span data-qb-text="item.lineTotalFormatted"></span>
        <button data-qb-action="remove">Ta bort</button>
      </li>
    </template>
  </qb-cart-items>
  <strong data-qb-text="cart.totalFormatted"></strong>
</qb-cart>
```

Reflects `state`, `empty` and `pending` as attributes. Handles the `clear`
(alias `clear-cart`) and `refresh` actions. `clear` empties the basket —
deletes the remembered cart and forgets it — and does nothing when there is
no cart; a cart the server already dropped still ends up cleared.

#### Extends

* `CartAwareElement`

#### Constructors

##### Constructor

```ts theme={null}
new QbCartElement(): QbCartElement;
```

###### Returns

[`QbCartElement`](#qbcartelement)

###### Inherited from

```ts theme={null}
CartAwareElement.constructor
```

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

```ts theme={null}
CartAwareElement.attributeChangedCallback
```

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

```ts theme={null}
CartAwareElement.connectedCallback
```

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

```ts theme={null}
CartAwareElement.disconnectedCallback
```

***

### QbCartItemsElement

`<qb-cart-items>` — one `<template>` clone per cart line.

Scope inside a row: everything on the API's `CartItem`, plus the formatted
money fields (`item.lineTotalFormatted`, `item.unitPriceFormatted`, …) and
`item.onSale`.

Handles `remove`, `increment` and `decrement`, and binds any
`input[data-qb-quantity]` in the row to that line's quantity — with the
change committed on `change` rather than on every keystroke, so typing `12`
does not first send a quantity of `1`.

Rows are keyed by cart line id and reused across updates. Without that the
quantity input the shopper is typing in is replaced mid-keystroke and loses
both its value and the caret.

#### Extends

* `CartAwareElement`

#### Constructors

##### Constructor

```ts theme={null}
new QbCartItemsElement(): QbCartItemsElement;
```

###### Returns

[`QbCartItemsElement`](#qbcartitemselement)

###### Inherited from

```ts theme={null}
CartAwareElement.constructor
```

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

```ts theme={null}
CartAwareElement.attributeChangedCallback
```

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

```ts theme={null}
CartAwareElement.connectedCallback
```

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

```ts theme={null}
CartAwareElement.disconnectedCallback
```

***

### QbCheckoutButtonElement

`<qb-checkout-button>` — hands the shopper to the hosted Quickbutik checkout.

```html theme={null}
<qb-checkout-button success-url="/order" back-url="/">
  <button>Till kassan</button>
</qb-checkout-button>
```

Adds no markup. A click anywhere inside creates the checkout session and
navigates. Relative URLs are resolved against the current origin, so the
markup above works unchanged in a rig, in a preview and in production.

`theme="dark"` paints the hosted checkout dark for this shopper. Omit it and
the merchant's own choice applies — see checkoutTheme.

Payment is deliberately not part of this: the hosted checkout owns the PSP
integration, PCI scope, 3-D Secure and wallets. This creates the session,
builds the handoff URL and sends the shopper there.

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbCheckoutButtonElement(): QbCheckoutButtonElement;
```

###### Returns

[`QbCheckoutButtonElement`](#qbcheckoutbuttonelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### checkout()

```ts theme={null}
checkout(): Promise<void>;
```

Create the session and navigate.

`no-redirect` stops it short of navigating and emits `qb:checkout-started`
with the URL instead — for a page that wants to open the checkout in a new
tab, or to run its own analytics first.

###### Returns

`Promise`\<`void`>

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbCheckoutElement

`<qb-checkout>` — the Quickbutik checkout, inside your own page.

```html theme={null}
<qb-checkout success-url="/thanks" back-url="/cart" lang="sv"></qb-checkout>
```

Adds one `<iframe>` and nothing else. The document inside is served from the
checkout's own origin, so the payment integration, 3-D Secure and the wallet
domain registrations all work exactly as they do when the shopper is
redirected — what changes is that they never leave your page.

Not a replacement for `<qb-checkout-button>`: the button is still the right
element for a cart page that hands the shopper off, and it is what this
element degrades to when embedding is not possible (see below). Both create
the session the same way, from the same attributes.

`theme="light"` or `theme="dark"` paints the frame to match your own pages
rather than following the theme the merchant chose for their shop. Read once
with everything else, when the session is created — a page that toggles its
own dark mode under a live checkout does not repaint it. See
checkoutTheme.

Four things happen automatically, and each is a thing a hand-rolled iframe
would get wrong:

1. **The frame's height follows its content**, so there is never a scrollbar
   inside a scrollbar. `min-height` is the floor, and the height before the
   first measurement arrives.
2. **A redirect payment method breaks out to the top window.** Klarna, Swish,
   Vipps/MobilePay, iDEAL, Trustly and a full-page 3-D Secure all navigate
   the whole window to the PSP, come back to the checkout's own origin, and
   bounce to this page with `?qb_checkout_session=…&qb_checkout_shop=…`.
   This element picks both up on load, resumes the same session from the
   URL alone — the cart, and the store id remembered with it, are normally
   gone by then, because the order exists — and, once the frame has
   answered, cleans the parameters out of the address bar so a reload does
   not try to resume a consumed session. If the resume never gets that far,
   the parameters stay, so a reload resumes again instead of starting a new
   checkout.
3. **It degrades rather than strands the shopper.** A shop on the legacy
   checkout, a platform with no embed URL, or a page that cannot navigate its
   own top window all fall back to the redirect checkout and emit
   `qb:checkout-fallback` before any frame exists. A frame that is built but
   never answers — this page is not the origin the session was created for,
   most often — is taken down after 15 seconds and falls back the same way,
   with `detail.reason === "handshake-timeout"`; it never ends in
   `state="error"`.
4. **It follows the cart.** Change the cart from the page while the frame is
   up — `Quickbutik.cart.add()`, a quantity stepper of your own — and the
   checkout re-reads it and re-prices itself, so it never charges for a cart
   the shopper has already moved on from. Nothing is mounted while the cart
   is empty or still loading (`qb:checkout-empty` says so, once); the frame
   goes in when items exist. Empty the cart from the page — `Quickbutik.cart.clear()`, the
   last line removed — while a frame is up, and the frame comes down with it,
   because the session it shows names a cart that no longer exists; the next
   item added starts a fresh session. A frame that is resuming a return leg,
   or showing a completed order, is left alone: there the cart is SUPPOSED
   to be gone.

Events, all bubbling: `qb:checkout-ready`, `qb:checkout-step`,
`qb:checkout-event` (GA4-shaped commerce events for your own dataLayer),
`qb:checkout-complete`, `qb:checkout-error`, `qb:checkout-fallback`, and
`qb:checkout-empty` when there is nothing in the cart to check out.

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbCheckoutElement(): QbCheckoutElement;
```

###### Returns

[`QbCheckoutElement`](#qbcheckoutelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

Deliberately empty.

Every attribute here is read once, when the session is created. Re-reading
one later would mean tearing down a checkout the shopper is standing in —
possibly mid-payment — to build an identical one, so a changed attribute
is ignored rather than obeyed. Change them before the element connects.

#### Accessors

##### checkout

###### Get Signature

```ts theme={null}
get checkout(): EmbeddedCheckout | null;
```

The live embed handle, for a page that wants to drive it by hand.

###### Returns

[`EmbeddedCheckout`](/kit/api/core#embeddedcheckout) | `null`

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbConsentBannerElement

`<qb-consent-banner>` — the cookie banner, headless.

```html theme={null}
<qb-consent-banner lang="sv" privacy-policy-url="/integritet"></qb-consent-banner>
```

Given no children it renders a minimal, accessible dialog of its own — the
one exception to "the elements add no markup", because an empty consent
banner is a bug rather than a choice. The markup uses stable `qb-consent*`
class names (the same ones the React `<ConsentBanner>` renders) and reads
its copy from the built-in labels for `lang`, falling back to `<html lang>`
and then English. It comes styled: one stylesheet in `<head>`, in
`@layer qb-consent` so the page's own rules win, themed through
`--qb-consent-*` custom properties. `unstyled` leaves it out.

A page configured through `configure()`, `<qb-shop>` or the script tag
gets one of these appended to `<body>` automatically (consent is on by
default). Placing one yourself replaces that one, so there is never a
second dialog.

Given children, it binds them instead, so a shop with its own voice writes
its own banner:

```html theme={null}
<qb-consent-banner>
  <h2 data-qb-text="consent.labels.title"></h2>
  <button data-qb-action="reject-all">Only necessary</button>
  <button data-qb-action="accept-all">Accept all</button>
  <button data-qb-action="open-settings" data-qb-hide="consent.open">Settings</button>
  <fieldset data-qb-show="consent.open">
    <label><input type="checkbox" data-qb-consent="analytics"> Analytics</label>
    <label><input type="checkbox" data-qb-consent="marketing"> Marketing</label>
    <button data-qb-action="save">Save</button>
  </fieldset>
</qb-consent-banner>
```

Actions: `accept-all`, `reject-all`, `save` (reads every
`input[data-qb-consent]` inside the banner), `open-settings`,
`close-settings`. Scope: `consent.status`, `consent.undecided`,
`consent.decided`, `consent.open`, `consent.analytics`, `consent.marketing`,
`consent.labels.*`, `consent.privacyPolicyUrl`.

Reflected: `status="undecided|decided"`, `open` while the preferences panel
is shown, and the native `hidden` attribute once the shopper has decided —
unless `show-when-decided` is set, or the panel is open again (a
`<qb-consent-settings>` link in the footer reopens it). The decision itself
lives in the page's ambient `ConsentStore` (see `configureConsent`), which
writes the `qb_consent` cookie and dispatches `qb:consent` on `document`.

#### Extends

* [`QbScopeElement`](#qbscopeelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbConsentBannerElement(): QbConsentBannerElement;
```

###### Returns

[`QbConsentBannerElement`](#qbconsentbannerelement)

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`constructor`](#constructor-19)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`attributeChangedCallback`](#attributechangedcallback-36)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`connectedCallback`](#connectedcallback-36)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`disconnectedCallback`](#disconnectedcallback-36)

##### save()

```ts theme={null}
save(): void;
```

Record what the checkboxes say. Missing inputs read as "no".

###### Returns

`void`

***

### QbConsentGateElement

`<qb-consent-gate>` — markup that exists only with consent.

```html theme={null}
<qb-consent-gate category="marketing">
  <template>
    <iframe src="https://www.youtube.com/embed/…"></iframe>
  </template>
  <p>Accept marketing cookies to watch the video.</p>
</qb-consent-gate>
```

The direct `<template>` child is the gated content; every other child is
the placeholder. While the category is denied the placeholder shows and the
template's content is not in the document at all — an `<iframe>` or a
`<script>` inside it is never fetched. When the shopper grants it, the
content is cloned in after the placeholder and the placeholder is hidden;
when they withdraw it, the clones are removed again. Reflects
`state="granted|denied"`.

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbConsentGateElement(): QbConsentGateElement;
```

###### Returns

[`QbConsentGateElement`](#qbconsentgateelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbConsentSettingsElement

`<qb-consent-settings>` — a "Cookie settings" link, anywhere on the page.

```html theme={null}
<footer>
  <qb-consent-settings><button type="button">Cookie settings</button></qb-consent-settings>
</footer>
```

A click anywhere inside reopens the banner's preferences panel, however far
away the banner is in the DOM — the two share the page's consent store, and
`open` lives in it. Reflects `status="undecided|decided"` like the banner,
so the link can read "Cookie settings (analytics on)" through CSS or a
binding of the page's own.

Left empty, it renders a `<button type="button">` with the built-in label
for `lang` ("Cookie settings", "Cookie-inställningar", …), the same thing
React's `<ConsentSettingsButton>` renders: an empty element would be a
footer link nobody can click. Hidden when the page turned consent off.

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbConsentSettingsElement(): QbConsentSettingsElement;
```

###### Returns

[`QbConsentSettingsElement`](#qbconsentsettingselement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbCurrencySelectElement

`<qb-currency-select>` — let the shopper pick the currency the shop is
browsed in.

Three ways to write it, from least to most markup:

```html theme={null}
<!-- 1. Nothing inside: a native <select> of the shop's currencies is added -->
<qb-currency-select></qb-currency-select>

<!-- 2. Your own <select>: filled with the currencies (a value="" placeholder
     is kept), or left alone when you wrote the options yourself -->
<qb-currency-select>
  <label>Valuta <select class="my-select"></select></label>
</qb-currency-select>

<!-- 3. A <template>: one clone per currency, any markup at all -->
<qb-currency-select>
  <template>
    <button data-qb-action="set-currency" data-qb-text="option.code"></button>
  </template>
</qb-currency-select>
```

Choosing calls `client.setCurrency()`: the choice is remembered, every
`<qb-product>` and `<qb-product-list>` refetches its prices in it, and every
cart element re-reads the SAME cart in it. A `"display"` currency is shown
converted and charged in the shop's currency at checkout; a `"charge"`
currency is priced and charged in it.

Scope: `currency.code` (what prices are shown in), `currency.base`,
`currency.mode` (`base` / `display` / `charge`), `currency.chargeCurrency`,
`currency.count`; inside a template row, `option.code`, `option.label`,
`option.name`, `option.mode`, `option.rate`, `option.selected`,
`option.base`. Rows get `data-selected` and `data-currency`.

Reflects `state`, `data-currency`, `data-mode`, and `empty` when the shop
offers nothing but its own currency — `qb-currency-select[empty] { display:
none }` hides a switcher with nothing to switch. Emits
`qb:currency-change` with `{ currency }` after a choice. `label="name"` (or
`"code-name"`) labels the options with the currency's name in `locale`.

Reads the shop with `shop.get()`, so the key needs `checkout:read` — every
standard storefront key has it.

#### Extends

* [`QbScopeElement`](#qbscopeelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbCurrencySelectElement(): QbCurrencySelectElement;
```

###### Returns

[`QbCurrencySelectElement`](#qbcurrencyselectelement)

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`constructor`](#constructor-19)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Accessors

##### info

###### Get Signature

```ts theme={null}
get info(): CurrencyInfo | null;
```

What the current choice means for this shop, or null before it loads.

###### Returns

[`CurrencyInfo`](/kit/api/core#currencyinfo) | `null`

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`attributeChangedCallback`](#attributechangedcallback-36)

##### choose()

```ts theme={null}
choose(code): Promise<void>;
```

Switch currency, exactly as a shopper's choice does.

###### Parameters

| Parameter | Type |
| - | - |
| `code` | `string` |

###### Returns

`Promise`\<`void`>

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`connectedCallback`](#connectedcallback-36)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`disconnectedCallback`](#disconnectedcallback-36)

***

### `abstract` QbElement

Base for every element in this package.

It exists for three things that every one of them needs and that are easy to
get subtly wrong: not reading children before the parser has produced them,
unwinding subscriptions on disconnect, and finding the shop.

No shadow DOM anywhere. These are headless components: the merchant's own
stylesheet has to reach the markup, and a shadow root is precisely a wall
against that.

#### Extends

* `HTMLElement`

#### Extended by

* [`QbAddToCartElement`](#qbaddtocartelement)
* [`QbBuyNowElement`](#qbbuynowelement)
* [`QbCartCountElement`](#qbcartcountelement)
* [`QbCheckoutButtonElement`](#qbcheckoutbuttonelement)
* [`QbCheckoutElement`](#qbcheckoutelement)
* [`QbConsentGateElement`](#qbconsentgateelement)
* [`QbConsentSettingsElement`](#qbconsentsettingselement)
* [`QbProductImageElement`](#qbproductimageelement)
* [`QbScopeElement`](#qbscopeelement)
* [`QbSeoElement`](#qbseoelement)
* [`QbShopElement`](#qbshopelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbElement(): QbElement;
```

###### Returns

[`QbElement`](#qbelement)

###### Inherited from

```ts theme={null}
HTMLElement.constructor
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

***

### QbOptionsElement

`<qb-options>` — repeats its `<template>` once per option group the product
actually has, in the product's own display order.

Scope inside a row: `group.id`, `group.name`, `group.position`,
`group.values`, `group.selectedValueId`, `group.chosen`.

A product with no options renders nothing and the element is marked `empty`,
so `qb-options[empty] { display: none }` hides the surrounding chrome.

#### Extends

* `OptionsRepeatElement`

#### Constructors

##### Constructor

```ts theme={null}
new QbOptionsElement(): QbOptionsElement;
```

###### Returns

[`QbOptionsElement`](#qboptionselement)

###### Inherited from

```ts theme={null}
OptionsRepeatElement.constructor
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

```ts theme={null}
OptionsRepeatElement.attributeChangedCallback
```

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

```ts theme={null}
OptionsRepeatElement.connectedCallback
```

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

```ts theme={null}
OptionsRepeatElement.disconnectedCallback
```

***

### QbOptionValuesElement

`<qb-option-values>` — repeats its `<template>` once per value of the group
it sits in.

Nested inside a `<qb-options>` row it needs no attributes at all: the group
comes from the row's scope. Scope inside a value row: `value.id`,
`value.name`, `value.selected`, `value.available`, `value.unavailable`,
`value.variantIds` — plus everything the enclosing scopes offered.

Each row's top-level elements get `data-selected` and `data-available`
reflected, `aria-pressed` set, and — on a `<button>` or `<input>` — the
`disabled` property, so unavailable combinations are a stylesheet's concern.

`option="<id or name>"` is an escape hatch for the deliberate case: a layout
that wants swatches for one particular group and a plain row for the rest.
It is never required, and pinning it to a name means the picker stops working
on a product whose merchant named that option something else.

#### Extends

* `OptionsRepeatElement`

#### Constructors

##### Constructor

```ts theme={null}
new QbOptionValuesElement(): QbOptionValuesElement;
```

###### Returns

[`QbOptionValuesElement`](#qboptionvalueselement)

###### Inherited from

```ts theme={null}
OptionsRepeatElement.constructor
```

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

```ts theme={null}
OptionsRepeatElement.attributeChangedCallback
```

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

```ts theme={null}
OptionsRepeatElement.connectedCallback
```

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

```ts theme={null}
OptionsRepeatElement.disconnectedCallback
```

***

### QbOrderConfirmationElement

`<qb-order-confirmation>` — the thank-you page.

```html theme={null}
<qb-order-confirmation>
  <p data-qb-show="confirmation.loading">Skapar din order…</p>
  <p data-qb-show="confirmation.completed">
    Tack! Ordernummer <strong data-qb-text="confirmation.orderNumber"></strong>
  </p>
  <p data-qb-show="confirmation.failed">Betalningen gick inte igenom.</p>
</qb-order-confirmation>
```

Order creation is asynchronous after payment, so a shopper can land here
before the order is written. This polls with the platform's own backoff and
settles on `completed`, `failed` or `timeout`, reflecting each as both a scope
flag and a `status` attribute.

The session id comes from `session-id`, from a `?session_id=` query parameter,
or — with neither — from the session shopkit remembered when the checkout was
created.

With analytics on (`configure({ analytics })`, `<qb-shop analytics>`) a
completed order is reported as a `purchase` event, once per browser, built
from the session's own lines and total. The hosted checkout never fires it
in redirect mode — this page is the thank-you page, so this is where it
belongs. `no-track-purchase` turns that off for a page that reports the
order itself; in `inline` success mode the embedded frame already did.

#### Extends

* [`QbScopeElement`](#qbscopeelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbOrderConfirmationElement(): QbOrderConfirmationElement;
```

###### Returns

[`QbOrderConfirmationElement`](#qborderconfirmationelement)

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`constructor`](#constructor-19)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`attributeChangedCallback`](#attributechangedcallback-36)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`connectedCallback`](#connectedcallback-36)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`disconnectedCallback`](#disconnectedcallback-36)

***

### QbProductElement

`<qb-product>` — one product, its variant selection, and everything derived
from the two.

Resolves the product itself, so a page can name it by slug and write no
JavaScript at all:

```html theme={null}
<qb-product slug="cotton-tee">
  <h1 data-qb-text="product.name"></h1>
  <p data-qb-text="price.display"></p>
  <qb-options>…</qb-options>
  <qb-add-to-cart><button>Lägg i varukorg</button></qb-add-to-cart>
</qb-product>
```

Renders no markup of its own. State is reflected as attributes —
`state="loading|ready|error"`, plus `empty`, `complete` / `incomplete` — so a
skeleton and a "choose a size first" hint are pure CSS. The product can also
be handed in as a property (`el.product = product`) when the page fetched it
already.

A `slug` lookup costs a catalog walk, because the storefront API has no slug
filter. On a large catalog prefer resolving the product server-side and
assigning the property.

#### Extends

* [`QbScopeElement`](#qbscopeelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbProductElement(): QbProductElement;
```

###### Returns

[`QbProductElement`](#qbproductelement)

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`constructor`](#constructor-19)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Accessors

##### product

###### Get Signature

```ts theme={null}
get product(): Product | null;
```

A product you already have. Wins over `slug` / `product-id`.

###### Returns

[`Product`](/kit/api/core#product-1) | `null`

###### Set Signature

```ts theme={null}
set product(product): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `product` | [`Product`](/kit/api/core#product-1) \| `null` |

###### Returns

`void`

##### selection

###### Get Signature

```ts theme={null}
get selection(): ProductController | null;
```

The live selection state machine, for scripting a custom picker.

###### Returns

[`ProductController`](/kit/api/core#productcontroller) | `null`

#### Methods

##### addToCart()

```ts theme={null}
addToCart(quantity?): Promise<Cart | null>;
```

Add the selected variant to the cart.

A no-op resolving to null while the selection is incomplete: a product with
options must not be addable until exactly one variant is pinned, or the
cart gets a line the shopper never chose. The blocked attempt is emitted as
`qb:add-to-cart-blocked`, carrying the groups still missing a choice, so a
page can prompt for them.

###### Parameters

| Parameter | Type |
| - | - |
| `quantity?` | `number` |

###### Returns

`Promise`\<[`Cart`](/kit/api/core#cart-1) | `null`>

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(name): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `name` | `string` |

###### Returns

`void`

###### Overrides

[`QbScopeElement`](#qbscopeelement).[`attributeChangedCallback`](#attributechangedcallback-36)

##### buyNow()

```ts theme={null}
buyNow(input, quantity?): Promise<BuyNowResult | null>;
```

Add the selected variant and start the hosted checkout — "Köp nu".
Resolves to the handoff (`url` and all) and does NOT navigate; the
`buy-now` verb and `<qb-buy-now>` do that.

The guard is [addToCart](#addtocart)'s: while the selection is incomplete it
emits `qb:add-to-cart-blocked` and resolves to null, because buying is
adding. A second call while one is in flight also resolves to null — two
clicks would otherwise add the item twice before either navigation
lands. A failed handoff THROWS (see `CartStore.buyNow`); the verb and the
element turn that into a `qb:error`. `input` may be a function, called
only once the guard has passed — so a blocked click does not also warn
about a missing success-url.

###### Parameters

| Parameter | Type |
| - | - |
| `input` | \| [`StartCheckoutInput`](/kit/api/core#startcheckoutinput) \| () => [`StartCheckoutInput`](/kit/api/core#startcheckoutinput) |
| `quantity?` | `number` |

###### Returns

`Promise`\<[`BuyNowResult`](/kit/api/core#buynowresult) | `null`>

##### canAddToCart()

```ts theme={null}
canAddToCart(): boolean;
```

False while the selection is incomplete or a cart mutation is in flight.

###### Returns

`boolean`

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`connectedCallback`](#connectedcallback-36)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`disconnectedCallback`](#disconnectedcallback-36)

***

### QbProductImageElement

`<qb-product-image>` — a product image as a real `<img>`, with the URL
actually resolved.

The one element in this package that renders markup, for the same reason
React's `<ProductImage />` does: `product.images[0].path` is **not** a URL, it
is the bare storage filename, and the shop's storage prefix that completes it
is not something a storefront credential can read. Rendering `path` directly
is the single most common way a headless Quickbutik shop ships broken images.

```html theme={null}
<!-- inside <qb-product> or a <qb-product-list> row: no attributes needed -->
<qb-product-image width="800" height="800"></qb-product-image>

<!-- a gallery -->
<qb-product-image index="1" width="120"></qb-product-image>
```

It creates exactly one `<img>` and nothing around it, reusing the same node
across updates so a swapped variant does not flash. Anything you put on the
host as `img-*` is copied onto it (`img-class`, `img-sizes`, `img-style`), so
the page keeps control of the markup.

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbProductImageElement(): QbProductImageElement;
```

###### Returns

[`QbProductImageElement`](#qbproductimageelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Properties

##### image

```ts theme={null}
image: ProductImage | null;
```

A specific image record. Wins over every other way of choosing one.

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Accessors

##### product

###### Get Signature

```ts theme={null}
get product(): Product | null;
```

The product to take the image from. Set by `<qb-product-list>` per row.

###### Returns

[`Product`](/kit/api/core#product-1) | `null`

###### Set Signature

```ts theme={null}
set product(product): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `product` | [`Product`](/kit/api/core#product-1) \| `null` |

###### Returns

`void`

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbProductListElement

`<qb-product-list>` — a catalog grid, one `<template>` clone per product.

```html theme={null}
<qb-product-list limit="12" sort-by="price" sort-order="asc">
  <template>
    <a data-qb-attr="href:product.href">
      <qb-product-image width="400"></qb-product-image>
      <h3 data-qb-text="product.name"></h3>
      <span data-qb-text="product.priceFormatted"></span>
    </a>
  </template>
</qb-product-list>
```

Scope inside a row: the whole `Product`, plus `product.priceFormatted`,
`product.image` (the first image record) and `product.href` — the row's link,
built from the `href-template` attribute (`/products/:slug` by default).

Every filter is an attribute, so a search box is `list.setAttribute("search", q)`
and nothing else: the previous request is aborted and only the newest answer
lands.

#### Extends

* [`QbScopeElement`](#qbscopeelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbProductListElement(): QbProductListElement;
```

###### Returns

[`QbProductListElement`](#qbproductlistelement)

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`constructor`](#constructor-19)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Accessors

##### products

###### Get Signature

```ts theme={null}
get products(): Product[];
```

The products currently rendered.

###### Returns

[`Product`](/kit/api/core#product-1)\[]

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`attributeChangedCallback`](#attributechangedcallback-36)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`connectedCallback`](#connectedcallback-36)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbScopeElement`](#qbscopeelement).[`disconnectedCallback`](#disconnectedcallback-36)

##### reload()

```ts theme={null}
reload(): void;
```

Re-run the query. Useful after the catalog changed under a long session.

###### Returns

`void`

***

### `abstract` QbScopeElement

An element that contributes to the view model its subtree is bound against.

Scopes nest by extension, never by replacement: `<qb-option-values>` inside a
`<qb-options>` row sees `product`, `price`, `group` **and** `value`, so a
template can read anything an ancestor offered. That is what lets the variant
picker be written without naming a single option type — the group and the
value both arrive from the product, through the scope.

#### Extends

* [`QbElement`](#qbelement)

#### Extended by

* [`QbConsentBannerElement`](#qbconsentbannerelement)
* [`QbCurrencySelectElement`](#qbcurrencyselectelement)
* [`QbOrderConfirmationElement`](#qborderconfirmationelement)
* [`QbProductElement`](#qbproductelement)
* [`QbProductListElement`](#qbproductlistelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbScopeElement(): QbScopeElement;
```

###### Returns

[`QbScopeElement`](#qbscopeelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbSeoElement

`<qb-seo>` — the page's `<head>`, built from the product it sits next to.

```html theme={null}
<qb-product slug="cotton-tee">
  <qb-seo base-url="https://minbutik.se" product-path="/products/{slug}"></qb-seo>
  …
</qb-product>
```

Inside a `<qb-product>` it needs nothing else: the product comes from that
context, and the canonical from the `product-path` template. It writes the
title, the description, the canonical link, OpenGraph, the Twitter card and
the schema.org JSON-LD — the last being where the actual rich-result value
sits, price and availability included.

Every tag it writes is tagged `data-qb-seo`, and it removes its own on
update, so a soft navigation between products leaves no stale meta behind and
nothing the page itself put in `<head>` is touched.

A server-rendered page that already emits its head should not use this: two
sources competing for one `<title>` is worse than either alone. Use
`buildSeo()` from `@quickbutik/kit` in the renderer instead.

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbSeoElement(): QbSeoElement;
```

###### Returns

[`QbSeoElement`](#qbseoelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Accessors

##### tags

###### Get Signature

```ts theme={null}
get tags(): SeoTags;
```

The resolved payload, for a page that wants the data rather than tags.

###### Returns

[`SeoTags`](/kit/api/core#seotags)

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(_name?): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `_name?` | `string` |

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### QbShopElement

`<qb-shop>` — an explicit shop for one subtree. **Optional.**

The elements do not need this. A page configured once — by the script tag's
`data-publishable-key`, or by a `configure()` call — has an ambient shop that
every element finds on its own, and that is how a storefront should normally
be set up. Wrapping the whole page in a provider element is ceremony HTML
does not need.

What it is for is the handful of cases an ambient singleton genuinely cannot
serve:

```html theme={null}
<!-- two shops on one page -->
<qb-shop publishable-key="qb_pk_a_…"> … </qb-shop>
<qb-shop publishable-key="qb_pk_b_…"> … </qb-shop>

<!-- one section against a rig / preview gateway -->
<qb-shop publishable-key="qb_pk_…" api-url="https://gateway.platform.test"> … </qb-shop>

<!-- a campaign storefront (Säljplats): campaign prices on every product,
     cart and checkout inside -->
<qb-shop publishable-key="qb_pk_…" storefront-id="sf_…"> … </qb-shop>

<!-- browse in euro until the shopper picks something else -->
<qb-shop publishable-key="qb_pk_…" currency="EUR"> … </qb-shop>

<!-- a client you built yourself, cookies and all -->
<script>document.querySelector("qb-shop").client = myClient</script>

<!-- no analytics for this subtree's shop, or no consent at all -->
<qb-shop publishable-key="qb_pk_…" analytics="false"> … </qb-shop>
<qb-shop publishable-key="qb_pk_…" consent="false"> … </qb-shop>
```

Consent and analytics are on by default, as they are for `configure()`:
the merchant's GA4 / GTM / Meta ids load for this shop, gated on the
page's consent, and a default `<qb-consent-banner>` is appended to the page
when it has none (`privacy-policy-url` and `lang` here are handed to it).
Inside a page whose configured shop is the same shop (a campaign
`<qb-shop storefront-id>`), that shop's analytics is reused rather than
loaded twice, and this subtree's cart reports into it.

Renders nothing and adds no markup.

#### Extends

* [`QbElement`](#qbelement)

#### Constructors

##### Constructor

```ts theme={null}
new QbShopElement(): QbShopElement;
```

###### Returns

[`QbShopElement`](#qbshopelement)

###### Inherited from

[`QbElement`](#qbelement).[`constructor`](#constructor-12)

#### Properties

##### observedAttributes

```ts theme={null}
static observedAttributes: string[];
```

#### Accessors

##### cartStore

###### Get Signature

```ts theme={null}
get cartStore(): CartStore | null;
```

The shared cart store for this subtree.

###### Returns

[`CartStore`](/kit/api/react#cartstore) | `null`

##### client

###### Get Signature

```ts theme={null}
get client(): ShopkitClient | null;
```

A client built elsewhere. Wins over every attribute.

A property rather than an attribute because a client is an object — the
escape hatch for a server-rendered page, a custom `fetch`, or per-request
cookie storage.

###### Returns

[`ShopkitClient`](/kit/api/core#shopkitclient) | `null`

###### Set Signature

```ts theme={null}
set client(client): void;
```

###### Parameters

| Parameter | Type |
| - | - |
| `client` | [`ShopkitClient`](/kit/api/core#shopkitclient) \| `null` |

###### Returns

`void`

##### initialCart

###### Set Signature

```ts theme={null}
set initialCart(cart): void;
```

A cart already fetched on the server, so the first paint shows the real
basket instead of an empty one that fills in a moment later.

Stashed when it is assigned before the element starts — which is the
normal case, since a page sets it immediately after inserting the tag.

###### Parameters

| Parameter | Type |
| - | - |
| `cart` | [`Cart`](/kit/api/core#cart-1) \| `null` |

###### Returns

`void`

#### Methods

##### attributeChangedCallback()

```ts theme={null}
attributeChangedCallback(name?): void;
```

`currency` and `locale` never rebuild the client. A rebuilt client means
a new cart store, and a currency switch must keep the same cart — it is
priced per request, so the current client is simply told the new
currency (`setCurrency`) and everything below refetches.

###### Parameters

| Parameter | Type |
| - | - |
| `name?` | `string` |

###### Returns

`void`

###### Overrides

[`QbElement`](#qbelement).[`attributeChangedCallback`](#attributechangedcallback-22)

##### connectedCallback()

```ts theme={null}
connectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`connectedCallback`](#connectedcallback-22)

##### disconnectedCallback()

```ts theme={null}
disconnectedCallback(): void;
```

###### Returns

`void`

###### Inherited from

[`QbElement`](#qbelement).[`disconnectedCallback`](#disconnectedcallback-22)

***

### Repeater

Renders a `<template>` once per item, in place, keyed.

Keyed matters more than it looks: a cart line whose quantity changes must
keep its existing DOM, or the `<input>` the shopper is typing in is replaced
mid-keystroke and loses both its caret and its focus. Rows are matched by
key, moved rather than rebuilt, and only genuinely-gone rows are removed.

The `<template>` is left in the DOM where the author put it — template
content does not render — and rows are appended after it, so a heading or a
"your cart is empty" paragraph written before it stays where it was.

#### Constructors

##### Constructor

```ts theme={null}
new Repeater(host): Repeater;
```

###### Parameters

| Parameter | Type |
| - | - |
| `host` | `Element` |

###### Returns

[`Repeater`](#repeater)

#### Accessors

##### current

###### Get Signature

```ts theme={null}
get current(): RepeatRow[];
```

Every row currently rendered, in DOM order.

###### Returns

[`RepeatRow`](#repeatrow)\[]

##### hasTemplate

###### Get Signature

```ts theme={null}
get hasTemplate(): boolean;
```

True once the host actually has a `<template>` to clone.

###### Returns

`boolean`

#### Methods

##### clear()

```ts theme={null}
clear(): void;
```

Remove every row. The `<template>` itself stays.

###### Returns

`void`

##### render()

```ts theme={null}
render<T>(
   items, 
   key, 
   fill): void;
```

Reconcile the rows against `items`.

`fill` is called for every surviving row as well as every new one, because
the data behind a stable key changes too — a cart line keeps its id while
its quantity and total move.

###### Type Parameters

| Type Parameter |
| - |
| `T` |

###### Parameters

| Parameter | Type |
| - | - |
| `items` | readonly `T`\[] |
| `key` | (`item`, `index`) => `string` |
| `fill` | (`row`, `item`, `index`) => `void` |

###### Returns

`void`

##### rowFor()

```ts theme={null}
rowFor(node): RepeatRow | null;
```

The row a node sits inside, for resolving a per-row scope.

###### Parameters

| Parameter | Type |
| - | - |
| `node` | `Node` |

###### Returns

[`RepeatRow`](#repeatrow) | `null`

## Interfaces

### AutoBannerOptions

What the auto-mounted `<qb-consent-banner>` is given.

#### Properties

| Property | Type |
| - | - |
| <a id="lang" /> `lang?` | `string` |
| <a id="privacypolicyurl" /> `privacyPolicyUrl?` | `string` |
| <a id="unstyled" /> `unstyled?` | `boolean` |

***

### ConfigureOptions

#### Extends

* `Omit`\<[`ShopkitConfig`](/kit/api/core#shopkitconfig), `"consent"`>

#### Properties

| Property | Type | Description | Inherited from |
| - | - | - | - |
| <a id="analytics" /> `analytics?` | `boolean` \| [`ElementsAnalyticsOptions`](#elementsanalyticsoptions) | Analytics for the shop. **On by default** (unless `consent: false`): the merchant's own GA4, GTM and Meta Pixel from the admin, gated on the shopper's consent, so nothing from googletagmanager.com or connect.facebook.net is loaded before they agree (unless `loadBeforeConsent`). An object adds destinations of your own or changes the rules; `false` turns it off. | - |
| <a id="apiurl" /> `apiUrl?` | `string` | Commerce API origin. Defaults to [DEFAULT\_API\_URL](/kit/api/core#default_api_url); point it at your preview/rig gateway for local development. A trailing `/v2` is tolerated. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`apiUrl`](/kit/api/core#apiurl-2) |
| <a id="cartttlseconds" /> `cartTtlSeconds?` | `number` | How long a remembered cart id lives. Defaults to 30 days. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`cartTtlSeconds`](/kit/api/core#cartttlseconds-1) |
| <a id="checkouturl" /> `checkoutUrl?` | `string` | Origin of the checkout-v2 host the shopper is handed off to. Defaults to [DEFAULT\_CHECKOUT\_URL](/kit/api/core#default_checkout_url). Setting this explicitly makes it WIN over the URL the platform returns for a checkout-v2 handoff — which is what a rig, a preview environment or a checkout proxied onto the merchant's own domain needs. It has no effect on a shop running the legacy checkout: that URL is only ever the platform's, because it points at an order the platform just created. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`checkoutUrl`](/kit/api/core#checkouturl-2) |
| <a id="consent" /> `consent?` | \| `boolean` \| [`ConsentStore`](/kit/api/core#consentstore) \| [`ElementsConsentOptions`](#elementsconsentoptions) | Cookie consent for the page. **On by default**: a consent banner is appended to `<body>` (unless the page has its own `<qb-consent-banner>`) and the decision is forwarded to the hosted checkout. An object configures the store (a policy `revision`, a cookie `domain`) and the banner (`lang`, `privacyPolicyUrl`, `banner: false`); a store you already hold is used as is. `false` turns it all off: no banner, no analytics, nothing appended to checkout URLs. | - |
| <a id="cookieattributes" /> `cookieAttributes?` | [`CookieAttributes`](/kit/api/core#cookieattributes) | Attributes for cookies shopkit writes. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`cookieAttributes`](/kit/api/core#cookieattributes-2) |
| <a id="cookies" /> `cookies?` | [`CookieAccessor`](/kit/api/core#cookieaccessor) | Server-side cookie access. Supplying this is what makes an RSC, a route handler or a server function see the same cart as the browser. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`cookies`](/kit/api/core#cookies-2) |
| <a id="currency" /> `currency?` | `string` \| `null` | The currency to browse in — `"EUR"`, any case — until the shopper picks one with `client.setCurrency()`. Every product read and cart request then carries `?currency=EUR` and every checkout carries it in its body; the shop decides what it means. A currency the shop offers in `"display"` mode is shown converted and charged in the shop's currency at checkout, one offered in `"charge"` mode is priced and charged in it, and one it does not offer falls back to the shop's own currency without an error. Responses state the currency their amounts are in (`product.currency`, `cart.currency`), so format with those. A choice made with `setCurrency()` is remembered (under `${storageKeyPrefix}_currency`) and wins over this default on later visits. Omitted or `null`: the shop's own currency. A value that is not a three-letter code throws a `ShopkitConfigError`. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`currency`](/kit/api/core#currency-20) |
| <a id="fetch" /> `fetch?` | (`input`, `init?`) => `Promise`\<`Response`> | Injected fetch — for tests, or for a framework's instrumented fetch. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`fetch`](/kit/api/core#fetch-1) |
| <a id="headers" /> `headers?` | `Record`\<`string`, `string`> | Extra headers on every request (tracing, a custom user agent). | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`headers`](/kit/api/core#headers-1) |
| <a id="imagebaseurl" /> `imageBaseUrl?` | `string` | Fallback base URL for the shop's image storage. Normally unnecessary: every product image arrives with an absolute `url` already. Set this only to cover a shop on an older api-core that still sends the bare filename, or to route images through your own CDN. It is the shop-scoped base *without* the `products/` segment, e.g. `https://cdn.quickbutik.com/images/<shopPrefix>` — the prefix being the one in the key, `shopkit.shopPrefix`. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`imageBaseUrl`](/kit/api/core#imagebaseurl-2) |
| <a id="locale" /> `locale?` | `string` | BCP 47 locale for number formatting. Defaults to the runtime's. | - |
| <a id="maxretries" /> `maxRetries?` | `number` | Retries for transient failures (network error, 429, 5xx) on idempotent requests only. Defaults to 2. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`maxRetries`](/kit/api/core#maxretries-1) |
| <a id="onunauthorized" /> `onUnauthorized?` | () => `string` \| `Promise`\<`string` \| `null`> \| `null` | Called when a request comes back 401 — the key was rotated or revoked. Return a fresh key to adopt it and retry once; return null to let the 401 surface. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`onUnauthorized`](/kit/api/core#onunauthorized-1) |
| <a id="publishablekey" /> `publishableKey` | `string` | The shop's publishable key (`qb_pk_…`). Safe to ship to a browser: it is scope-limited, shop-scoped and grants no merchant data. A `qb_pat_…` personal access token is NOT interchangeable and is rejected. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`publishableKey`](/kit/api/core#publishablekey-1) |
| <a id="scopes" /> `scopes?` | \| readonly ( \| `"products:read"` \| `"cart:read"` \| `"cart:write"` \| `"checkout:read"` \| `"checkout:write"` \| `"storefront:read"`)\[] \| readonly `string`\[] \| `null` | The scopes the key carries. Used for a local pre-flight check that turns an opaque 403 into a named error before the request leaves the process. Defaults to the standard storefront set. Pass `null` to disable the check when the key's scopes are decided elsewhere. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`scopes`](/kit/api/core#scopes-2) |
| <a id="storage" /> `storage?` | [`StoragePreference`](/kit/api/core#storagepreference) | How to persist the cart / session ids. See [StoragePreference](/kit/api/core#storagepreference). | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`storage`](/kit/api/core#storage-3) |
| <a id="storagekeyprefix" /> `storageKeyPrefix?` | `string` | Prefix for stored keys. Change it to run two shops in one browser. Defaults to `"qb"`, or `qb_<storefront id lower-cased>` when a [storefront](/kit/api/core#storefront-2) is bound. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`storageKeyPrefix`](/kit/api/core#storagekeyprefix-1) |
| <a id="storefront" /> `storefront?` | [`ShopkitStorefrontConfig`](/kit/api/core#shopkitstorefrontconfig) \| `null` | Sell into one campaign storefront (a Säljplats in the Quickbutik admin) with this whole client. Every cart it creates is bound to the campaign — priced with the campaign's overrides on every read — and every checkout it starts carries the campaign's id and `surface` for the live-guard, the quantity limits and order attribution. `surface` defaults to `"shopkit"`. This is the binding the remembering calls (`cart.ensure()`, `cart.add()`, `checkout.start()` without a `cartId`) work against; a stateless call (`cart.create()`, a checkout given an explicit `cartId`) may name any campaign. Also changes the default [storageKeyPrefix](/kit/api/core#storagekeyprefix-1) to `qb_<id lower-cased>` (`qb_sf_01j…`), so the campaign's cart is remembered under its own cookie and never mixes with the shop's ordinary `qb_cart_id` cart or another campaign's — those carts are priced differently and must stay apart. Adding this to an EXISTING integration therefore stops it reading carts and sessions remembered under the old prefix; pass `storageKeyPrefix: "qb"` to keep them. The id is checked here and a malformed one throws a `ShopkitConfigError`; whether the campaign exists and is open is only known at checkout — see `storefrontRefusal()`. `null` means no binding, for a conditional config. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`storefront`](/kit/api/core#storefront-2) |
| <a id="storefrontid" /> `storefrontId?` | `string` \| `null` | Shorthand for `storefront: { id }` — the campaign storefront (`sf_…`) this client sells into, with the surface defaulted to `"shopkit"`. Everything [storefront](/kit/api/core#storefront-2) says applies: bound carts, campaign-priced product reads, the checkout attribution and the separate storage prefix. It exists because the id is very often the only thing a page has — a `storefront-id` attribute, a `data-storefront-id` on the script tag, a `<ShopkitProvider storefrontId>` prop, a route param — and wrapping it in an object at every one of those places is noise. Both may be set as long as they name the same campaign (case-insensitive, as the platform compares them); the surface then comes from `storefront`. Two different ids throw a `ShopkitConfigError` rather than silently choosing one. `null` and `""` mean "not set", for a conditional config. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`storefrontId`](/kit/api/core#storefrontid-8) |
| <a id="timeoutms" /> `timeoutMs?` | `number` | Per-request timeout in ms. Defaults to 15000. 0 disables it. | [`ShopkitConfig`](/kit/api/core#shopkitconfig).[`timeoutMs`](/kit/api/core#timeoutms-1) |

***

### Context

An opaque, comparable key for one kind of value.

#### Type Parameters

| Type Parameter |
| - |
| `T` |

#### Properties

| Property | Modifier | Type | Description |
| - | - | - | - |
| <a id="__type" /> `__type?` | `readonly` | `T` | Phantom — carries `T` so `requestContext` can infer the callback type. |
| <a id="name" /> `name` | `readonly` | `string` | - |

***

### ContextProvider

#### Methods

##### dispose()

```ts theme={null}
dispose(): void;
```

Stop answering and drop every subscriber.

###### Returns

`void`

##### update()

```ts theme={null}
update(): void;
```

Re-deliver the current value to every subscriber.

###### Returns

`void`

***

### DefinedElements

What `defineElements` registered, so a caller can see the real tag names.

#### Properties

| Property | Type | Description |
| - | - | - |
| <a id="prefix" /> `prefix` | `string` | - |
| <a id="tags-1" /> `tags` | `Record`\<`string`, `string`> | Bare name → the tag actually registered (`product` → `qb-product`). |

***

### DefineElementsOptions

#### Properties

| Property | Type | Description |
| - | - | - |
| <a id="prefix-1" /> `prefix?` | `string` | Tag-name prefix. Defaults to `"qb-"`, giving `<qb-product>`. Change it only for a genuine collision — a page already using `qb-` for something else, or two versions of this package on one document. Every example and every doc uses the default. |
| <a id="warnonunhandledactions" /> `warnOnUnhandledActions?` | `boolean` | Warn in the console about a `data-qb-action` nothing handled. On by default: a mistyped verb is otherwise a button that silently does nothing, which is the hardest kind of template bug to find. |

***

### ElementsConsentOptions

`configure({ consent })` as an object: the page's consent store options,
plus what the automatically mounted banner is given.

#### Extends

* [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions)

#### Properties

| Property | Type | Description | Inherited from |
| - | - | - | - |
| <a id="banner" /> `banner?` | `boolean` | Mount the default `<qb-consent-banner>` at the end of `<body>`. Default true. A banner the page places itself always replaces it. | - |
| <a id="cookiename" /> `cookieName?` | `string` | Defaults to `qb_consent`. | [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions).[`cookieName`](/kit/api/core#cookiename-3) |
| <a id="domain" /> `domain?` | `string` | Share the decision across subdomains (`.myshop.com`). Host-only by default. | [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions).[`domain`](/kit/api/core#domain-1) |
| <a id="initialstate" /> `initialState?` | [`ConsentState`](/kit/api/core#consentstate-1) \| `null` | The state to start from, instead of reading the cookie. For a server render: `readConsentCookie(cookieHeader)` on the server, passed down, so the first paint already knows the decision. Also what a test uses. `null` means "start undecided, do not read the cookie". | [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions).[`initialState`](/kit/api/core#initialstate) |
| <a id="lang-1" /> `lang?` | `string` | Language of the banner's built-in copy. Defaults to `<html lang>`. | - |
| <a id="maxagedays" /> `maxAgeDays?` | `number` | Defaults to 180 days. | [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions).[`maxAgeDays`](/kit/api/core#maxagedays-1) |
| <a id="persist" /> `persist?` | `boolean` | Write the decision to the cookie. Default true. Set false when another system owns persistence — a third-party consent platform whose callback calls `save()` on this store, or a test. | [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions).[`persist`](/kit/api/core#persist) |
| <a id="privacypolicyurl-1" /> `privacyPolicyUrl?` | `string` | Adds a "Privacy policy" link to the banner. | - |
| <a id="revision" /> `revision?` | `number` | The revision a stored decision must match to count. Defaults to 1. | [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions).[`revision`](/kit/api/core#revision-6) |
| <a id="samesite" /> `sameSite?` | `"lax"` \| `"strict"` \| `"none"` | Defaults to `lax`. | [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions).[`sameSite`](/kit/api/core#samesite-1) |
| <a id="secure" /> `secure?` | `boolean` | Defaults to true on https origins, false otherwise (localhost works). | [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions).[`secure`](/kit/api/core#secure-1) |
| <a id="unstyled-1" /> `unstyled?` | `boolean` | Leave out the banner's built-in stylesheet. | - |

***

### MoneyView

Turning state into the plain objects `data-qb-text="…"` reads.

The one thing added over what the SDK already returns is **formatted money**.
React's hooks deliberately hand back `{ amount, currency }` and let the app's
design system format it; HTML bindings have no such escape, so every money
field appears twice: the raw minor-unit integer, and a display string.

#### Extended by

* [`PriceView`](#priceview)

#### Properties

| Property | Type | Description |
| - | - | - |
| <a id="amount" /> `amount` | `number` \| `null` | Minor units, exactly as the API states them. |
| <a id="currency-1" /> `currency` | `string` \| `null` | - |
| <a id="formatted" /> `formatted` | `string` | Localised display string, e.g. `1 299,00 kr`. |

***

### PriceView

Turning state into the plain objects `data-qb-text="…"` reads.

The one thing added over what the SDK already returns is **formatted money**.
React's hooks deliberately hand back `{ amount, currency }` and let the app's
design system format it; HTML bindings have no such escape, so every money
field appears twice: the raw minor-unit integer, and a display string.

#### Extends

* [`MoneyView`](#moneyview)

#### Properties

| Property | Type | Description | Inherited from |
| - | - | - | - |
| <a id="amount-1" /> `amount` | `number` \| `null` | Minor units, exactly as the API states them. | [`MoneyView`](#moneyview).[`amount`](#amount) |
| <a id="compareat" /> `compareAt` | [`MoneyView`](#moneyview) | The struck-through "before" price, when the variant is on sale. | - |
| <a id="currency-2" /> `currency` | `string` \| `null` | - | [`MoneyView`](#moneyview).[`currency`](#currency-1) |
| <a id="display" /> `display` | `string` | What a price label should actually say: the exact figure once a variant is pinned, the range (`99,00 kr – 149,00 kr`) before that, and the single figure when every reachable variant costs the same. | - |
| <a id="formatted-1" /> `formatted` | `string` | Localised display string, e.g. `1 299,00 kr`. | [`MoneyView`](#moneyview).[`formatted`](#formatted) |
| <a id="isrange" /> `isRange` | `boolean` | True when the reachable variants do not all cost the same. | - |
| <a id="max" /> `max` | [`MoneyView`](#moneyview) | - | - |
| <a id="min" /> `min` | [`MoneyView`](#moneyview) | - | - |
| <a id="onsale" /> `onSale` | `boolean` | True when the variant is cheaper than its compare-at price. | - |

***

### ProductContextValue

What the picker elements need from the product above them.

Separate from the scope: the scope is read-only data for bindings, this is
the handle `<qb-option-values>` uses to actually change the selection.

`controller` is nullable because the product is usually still loading when
the elements below first ask. They subscribe, get null, render nothing, and
are called again the moment it resolves — which is why they must be given a
value now rather than left unanswered.

#### Properties

| Property | Type |
| - | - |
| <a id="controller" /> `controller` | [`ProductController`](/kit/api/core#productcontroller) \| `null` |
| <a id="currency-3" /> `currency?` | `string` |
| <a id="locale-1" /> `locale?` | `string` |

#### Methods

##### addToCart()

```ts theme={null}
addToCart(quantity?): Promise<Cart | null>;
```

###### Parameters

| Parameter | Type |
| - | - |
| `quantity?` | `number` |

###### Returns

`Promise`\<[`Cart`](/kit/api/core#cart-1) | `null`>

##### buyNow()

```ts theme={null}
buyNow(input, quantity?): Promise<BuyNowResult | null>;
```

Add the selected variant and start the checkout, WITHOUT navigating —
`<qb-buy-now>` decides that. Null when nothing was started (incomplete
selection, one already in flight); throws when the handoff fails.
`input` may be a function, called only once the guard has passed.

###### Parameters

| Parameter | Type |
| - | - |
| `input` | \| [`StartCheckoutInput`](/kit/api/core#startcheckoutinput) \| () => [`StartCheckoutInput`](/kit/api/core#startcheckoutinput) |
| `quantity?` | `number` |

###### Returns

`Promise`\<[`BuyNowResult`](/kit/api/core#buynowresult) | `null`>

##### canAddToCart()

```ts theme={null}
canAddToCart(): boolean;
```

###### Returns

`boolean`

***

### RepeatRow

One rendered row: the top-level elements one `<template>` clone produced.

Plural because a template may legitimately have several roots (`<dt>` and
`<dd>`, two `<td>`s), and all of them belong to the same item.

#### Properties

| Property | Type |
| - | - |
| <a id="key" /> `key` | `string` |
| <a id="roots" /> `roots` | `Element`\[] |

***

### ShopContextValue

The shop every element works against: one client, one shared cart store, and
the display preferences the bindings need in order to render money as a
string.

#### Properties

| Property | Type | Description |
| - | - | - |
| <a id="analytics-1" /> `analytics?` | [`Analytics`](/kit/api/core#analytics) \| `null` | The analytics hub for this shop, when the page opted in (`configure({ analytics })`, `<qb-shop analytics>`). Null or absent otherwise — the elements then track nothing. |
| <a id="cartstore-1" /> `cartStore` | [`CartStore`](/kit/api/react#cartstore) | - |
| <a id="client-1" /> `client` | [`ShopkitClient`](/kit/api/core#shopkitclient) | - |
| <a id="currency-4" /> `currency?` | `string` | Fallback ISO 4217 code for price formatting, used only when a response does not state its own (`product.currency`, `cart.currency` win). The currency the shop is BROWSED in is the client's: `client.currency`. |
| <a id="locale-2" /> `locale?` | `string` | BCP 47 locale for number formatting. Defaults to the browser's. |

***

### VariantView

#### Properties

| Property | Type | Description |
| - | - | - |
| <a id="id" /> `id` | `number` \| `null` | - |
| <a id="preorder" /> `preorder` | `boolean` | - |
| <a id="selected" /> `selected` | `boolean` | True once the selection pins exactly one variant. |
| <a id="sku" /> `sku` | `string` \| `null` | - |
| <a id="soldout" /> `soldOut` | `boolean` | True only when stock is tracked AND exhausted — never for untracked. |
| <a id="stock" /> `stock` | `number` \| `null` | Stock level, or null for a shop that does not track inventory. |

## Type Aliases

### ActionHandler()

```ts theme={null}
type ActionHandler = (target, event) => void;
```

#### Parameters

| Parameter | Type |
| - | - |
| `target` | `HTMLElement` |
| `event` | `Event` |

#### Returns

`void`

***

### ContextCallback()

```ts theme={null}
type ContextCallback<T> = (value, unsubscribe?) => void;
```

#### Type Parameters

| Type Parameter |
| - |
| `T` |

#### Parameters

| Parameter | Type |
| - | - |
| `value` | `T` |
| `unsubscribe?` | () => `void` |

#### Returns

`void`

***

### ElementsAnalyticsOptions

```ts theme={null}
type ElementsAnalyticsOptions = Omit<AnalyticsOptions, "consent"> & object;
```

What `configure({ analytics })` and `<qb-shop analytics>` build the hub
from. The consent store is not an option here: the elements always use the
page's ambient one (see `consent-context.ts`), unless `requireConsent` is
false.

#### Type Declaration

| Name | Type | Description |
| - | - | - |
| `auto?` | `boolean` | Load the destinations the shop's tracking configuration asks for (GA4, GTM, the Meta Pixel — `destinationsFromShop`), fetched from the platform once at startup. Default true. False for a page that passes its own `destinations` and wants nothing else. |
| `loadBeforeConsent?` | `boolean` | Load GTM / gtag.js before a decision, in Consent Mode denied. Default false: nothing from Google loads until `analytics` is granted. A GTM container loaded this way runs all of its tags. |

***

### Scope

```ts theme={null}
type Scope = Record<string, unknown>;
```

The view model a subtree's bindings are resolved against.

Plain data, deliberately: `data-qb-text="price.formatted"` is a **path**, not
an expression, so there is no evaluator, no `new Function`, and nothing a
merchant's template can execute.

## Variables

### elementConstructors

```ts theme={null}
const elementConstructors: Record<string, CustomElementConstructor>;
```

The element classes, for registering a subset by hand.

***

### productContext

```ts theme={null}
const productContext: Context<ProductContextValue>;
```

***

### scopeContext

```ts theme={null}
const scopeContext: Context<Scope>;
```

***

### shopContext

```ts theme={null}
const shopContext: Context<ShopContextValue>;
```

## Functions

### bindRow()

```ts theme={null}
function bindRow(roots, scope): void;
```

Bind a repeated row: each top-level element the template produced, and
everything under it.

Unlike [bindSubtree](#bindsubtree) the roots themselves are bound — the row's markup
is the author's, and `<li data-qb-text="item.productTitle">` is the obvious
way to write a one-element row. A root that is itself a scope owner is left
alone entirely; it will ask for its scope through the context protocol.

#### Parameters

| Parameter | Type |
| - | - |
| `roots` | readonly `Element`\[] |
| `scope` | [`Scope`](#scope) |

#### Returns

`void`

***

### bindSubtree()

```ts theme={null}
function bindSubtree(host, scope): void;
```

Apply every binding **inside** `host`, not on `host` itself.

What a scope-owning element calls on itself: `<qb-product>` binds the markup
the author put in it, and its own attributes are configuration, not bindings.

The walk stops at nested scope owners — they run their own pass with their
own, extended scope. That is what makes `<qb-option-values>` inside a
`<qb-options>` row see both `group` and `value`.

#### Parameters

| Parameter | Type |
| - | - |
| `host` | `Element` |
| `scope` | [`Scope`](#scope) |

#### Returns

`void`

***

### buildShopAnalytics()

```ts theme={null}
function buildShopAnalytics(
   client, 
   cartStore, 
   options): Analytics | null;
```

The analytics hub for one shop, started and wired the way every element
expects: consent from the ambient store (unless `requireConsent` is false),
cart mutations turned into `add_to_cart` / `remove_from_cart`, and — unless
`auto` is false — the merchant's own GA4 / GTM / Meta ids fetched from the
platform and added as destinations once they arrive. Events tracked before
then are remembered by the hub and delivered when they do.

Shared by `configure()` and `<qb-shop analytics>`, so a hub built for a
subtree behaves exactly like the page's. Null when `options` is falsy.

#### Parameters

| Parameter | Type |
| - | - |
| `client` | [`ShopkitClient`](/kit/api/core#shopkitclient) |
| `cartStore` | [`CartStore`](/kit/api/react#cartstore) |
| `options` | \| `boolean` \| [`ElementsAnalyticsOptions`](#elementsanalyticsoptions) \| `undefined` |

#### Returns

[`Analytics`](/kit/api/core#analytics) | `null`

***

### cancelAutoBanner()

```ts theme={null}
function cancelAutoBanner(): void;
```

Withdraw the request, and take down the banner it mounted.

#### Returns

`void`

***

### cartItemScope()

```ts theme={null}
function cartItemScope(
   item, 
   currency, 
   locale): Scope;
```

#### Parameters

| Parameter | Type |
| - | - |
| `item` | [`CartItem`](/kit/api/core#cartitem) |
| `currency` | `string` \| `undefined` |
| `locale` | `string` \| `undefined` |

#### Returns

[`Scope`](#scope)

***

### cartScope()

```ts theme={null}
function cartScope(
   cart, 
   itemCount, 
   locale, 
   fallbackCurrency): Scope;
```

The scope `<qb-cart>` contributes.

#### Parameters

| Parameter | Type |
| - | - |
| `cart` | [`Cart`](/kit/api/core#cart-1) \| `null` |
| `itemCount` | `number` |
| `locale` | `string` \| `undefined` |
| `fallbackCurrency` | `string` \| `undefined` |

#### Returns

[`Scope`](#scope)

***

### configure()

```ts theme={null}
function configure(options): ShopContextValue;
```

Configure the shop the elements use, once, for the whole page.

This is what makes `<qb-shop>` unnecessary. The script-tag build calls it
automatically from its own `data-*` attributes, so a plain HTML page never
writes any JavaScript at all:

```html theme={null}
<script defer src="…/quickbutik-kit.global.js"
        data-publishable-key="qb_pk_…" data-currency="SEK"></script>
```

A bundler consumer calls it directly, once, near their entry point:

```ts theme={null}
import { configure, defineElements } from "@quickbutik/kit/elements"

configure({ publishableKey: import.meta.env.VITE_QB_KEY, currency: "SEK" })
defineElements()
```

Calling it again replaces the client **and the cart store**, which discards
whatever the current store had loaded — so call it once, at startup, not per
navigation. The remembered cart id itself lives in a cookie / localStorage and
survives, so the new store re-reads the same cart. The analytics hub, when
there is one, is destroyed and rebuilt with it; the consent store is only
replaced when `consent` is given again.

#### Parameters

| Parameter | Type |
| - | - |
| `options` | [`ConfigureOptions`](#configureoptions) |

#### Returns

[`ShopContextValue`](#shopcontextvalue)

***

### configureConsent()

```ts theme={null}
function configureConsent(options?): ConsentStore;
```

Configure the consent store the elements share. Pass the options for a new
store, or a store you already hold (one a React tree also uses, say). Call
it before `defineElements()`: an element that has already started keeps
the store it found.

#### Parameters

| Parameter | Type |
| - | - |
| `options?` | \| [`ConsentStore`](/kit/api/core#consentstore) \| [`ConsentStoreOptions`](/kit/api/core#consentstoreoptions) |

#### Returns

[`ConsentStore`](/kit/api/core#consentstore)

***

### createContext()

```ts theme={null}
function createContext<T>(name): Context<T>;
```

#### Type Parameters

| Type Parameter |
| - |
| `T` |

#### Parameters

| Parameter | Type |
| - | - |
| `name` | `string` |

#### Returns

[`Context`](#context-1)\<`T`>

***

### decorateHandoff()

```ts theme={null}
function decorateHandoff<T>(result, shop): T;
```

A checkout handoff with the shopper's consent on its URL.

Every element that sends a shopper to the hosted checkout —
`<qb-checkout-button>`, `<qb-buy-now>`, the `buy-now` verb — passes its
result through here before it emits `qb:checkout-started` or navigates, so
the URL a page sees in the event is the URL the shopper actually lands on.

The consent comes from the page's ambient store, the `requireConsent` rule
from the shop's hub; a page that configured neither, or turned consent off
(`configure({ consent: false })`), keeps the URL untouched (see
`checkoutHandoffUrl`).

#### Type Parameters

| Type Parameter |
| - |
| `T` *extends* `object` |

#### Parameters

| Parameter | Type |
| - | - |
| `result` | `T` |
| `shop` | [`ShopContextValue`](#shopcontextvalue) \| `null` \| `undefined` |

#### Returns

`T`

***

### defineElements()

```ts theme={null}
function defineElements(options?): DefinedElements;
```

Register the custom elements.

Explicit rather than a side effect of importing, so `@quickbutik/kit/elements`
stays tree-shakeable and a bundler can drop what an app does not use. The
script-tag build calls it for you.

```ts theme={null}
import { configure, defineElements } from "@quickbutik/kit/elements"

configure({ publishableKey: "qb_pk_…" })
defineElements()
```

Idempotent: a name already registered is left alone, so calling it twice — or
calling it after the script tag already did — is harmless rather than the
`NotSupportedError` `customElements.define` would otherwise throw.

#### Parameters

| Parameter | Type |
| - | - |
| `options?` | [`DefineElementsOptions`](#defineelementsoptions) |

#### Returns

[`DefinedElements`](#definedelements)

***

### delegateActions()

```ts theme={null}
function delegateActions(host, handlers): ActionDelegate;
```

Handle the verbs in `handlers` for anything inside `host`.

`click` and `change` are both listened for: `change` is what a `<select>` of
option values and a quantity `<input>` fire, and wiring them through the same
attribute keeps one concept in the markup instead of two.

#### Parameters

| Parameter | Type |
| - | - |
| `host` | `Element` |
| `handlers` | `Record`\<`string`, [`ActionHandler`](#actionhandler)> |

#### Returns

`ActionDelegate`

***

### disableAutoBanner()

```ts theme={null}
function disableAutoBanner(): void;
```

The page wants no automatic banner (`configure({ consent: { banner: false } })`,
`data-consent-banner="false"`): take down the one mounted, and refuse a
`<qb-shop>`'s request for another until the page asks again.

#### Returns

`void`

***

### getAmbientConsent()

```ts theme={null}
function getAmbientConsent(): ConsentStore | null;
```

The ambient consent store, or null when nothing has created one yet.

#### Returns

[`ConsentStore`](/kit/api/core#consentstore) | `null`

***

### getAmbientShop()

```ts theme={null}
function getAmbientShop(): ShopContextValue | null;
```

The ambient shop, or null when nothing has configured one yet.

#### Returns

[`ShopContextValue`](#shopcontextvalue) | `null`

***

### hasShop()

```ts theme={null}
function hasShop(): boolean;
```

True when a shop is configured at all — for a soft check before throwing.

#### Returns

`boolean`

***

### isConsentDisabled()

```ts theme={null}
function isConsentDisabled(): boolean;
```

#### Returns

`boolean`

***

### isScopeOwner()

```ts theme={null}
function isScopeOwner(element): boolean;
```

#### Parameters

| Parameter | Type |
| - | - |
| `element` | `Element` |

#### Returns

`boolean`

***

### markScopeOwner()

```ts theme={null}
function markScopeOwner(tagName): void;
```

#### Parameters

| Parameter | Type |
| - | - |
| `tagName` | `string` |

#### Returns

`void`

***

### money()

```ts theme={null}
function money(
   amount, 
   currency, 
   locale): MoneyView;
```

Format one amount, tolerating an unknown currency.

Products and carts state the currency their amounts are in, and that is
what callers pass. Only a platform older than `product.currency` leaves a
product page with nothing to go on but the configured currency, and with
none of those the honest options are a blank price or an unsymbolled
number. It formats the number and says so once in the console, because a
shopper seeing `1 299,00` understands it and a shopper seeing nothing does
not.

#### Parameters

| Parameter | Type |
| - | - |
| `amount` | `number` \| `null` \| `undefined` |
| `currency` | `string` \| `null` \| `undefined` |
| `locale` | `string` \| `undefined` |

#### Returns

[`MoneyView`](#moneyview)

***

### optionGroupScope()

```ts theme={null}
function optionGroupScope(group): Scope;
```

#### Parameters

| Parameter | Type |
| - | - |
| `group` | [`VariantOptionGroupState`](/kit/api/core#variantoptiongroupstate) |

#### Returns

[`Scope`](#scope)

***

### optionValueScope()

```ts theme={null}
function optionValueScope(value): Scope;
```

#### Parameters

| Parameter | Type |
| - | - |
| `value` | [`VariantOptionValueState`](/kit/api/core#variantoptionvaluestate) |

#### Returns

[`Scope`](#scope)

***

### priceView()

```ts theme={null}
function priceView(
   snapshot, 
   currency, 
   locale): PriceView;
```

#### Parameters

| Parameter | Type |
| - | - |
| `snapshot` | [`ProductSnapshot`](/kit/api/core#productsnapshot) |
| `currency` | `string` \| `undefined` |
| `locale` | `string` \| `undefined` |

#### Returns

[`PriceView`](#priceview)

***

### productScope()

```ts theme={null}
function productScope(
   snapshot, 
   currency, 
   locale): Scope;
```

The scope `<qb-product>` contributes.

#### Parameters

| Parameter | Type |
| - | - |
| `snapshot` | [`ProductSnapshot`](/kit/api/core#productsnapshot) |
| `currency` | `string` \| `undefined` |
| `locale` | `string` \| `undefined` |

#### Returns

[`Scope`](#scope)

***

### provideContext()

```ts theme={null}
function provideContext<T>(
   host, 
   context, 
   produce): ContextProvider;
```

Answer `context-request` events raised inside `host`.

`produce` receives the element that asked, so one provider can hand different
values to different parts of its subtree — that is how a repeated row gives
its own scope to the elements cloned into it without wrapping them in
anything. Returning `undefined` declines the request and lets it keep
bubbling to an outer provider.

#### Type Parameters

| Type Parameter |
| - |
| `T` |

#### Parameters

| Parameter | Type |
| - | - |
| `host` | `EventTarget` |
| `context` | [`Context`](#context-1)\<`T`> |
| `produce` | (`requester`) => `T` \| `undefined` |

#### Returns

[`ContextProvider`](#contextprovider)

***

### requestAutoBanner()

```ts theme={null}
function requestAutoBanner(options?, __namedParameters?): void;
```

Ask for a default banner on the page: a `<qb-consent-banner>` appended to
`<body>` once the document is parsed, unless the page already has one.
A banner the page places itself, then or later, always wins: the
auto-mounted one never duplicates it (see `QbConsentBannerElement`).

Requests merge rather than replace, and the page's own configuration
outranks a `<qb-shop>`'s: `scope: "page"` (what `configure()` and the
script tag pass) overrides what is already set, while a shop's request only
fills in what the page left out, and never brings back a banner the page
turned off with `disableAutoBanner()`.

#### Parameters

| Parameter | Type |
| - | - |
| `options?` | [`AutoBannerOptions`](#autobanneroptions) |
| `__namedParameters?` | \{ `scope?`: `"page"` \| `"shop"`; } |
| `__namedParameters.scope?` | `"page"` \| `"shop"` |

#### Returns

`void`

***

### requestContext()

```ts theme={null}
function requestContext<T>(
   requester, 
   context, 
   callback, 
   subscribe?): boolean;
```

Ask for a context value.

Returns whether anyone answered, which is what lets the elements fall back to
the ambient shop when the page has no `<qb-shop>` at all — the common case,
and the one the script tag is built around.

#### Type Parameters

| Type Parameter |
| - |
| `T` |

#### Parameters

| Parameter | Type |
| - | - |
| `requester` | `Element` |
| `context` | [`Context`](#context-1)\<`T`> |
| `callback` | [`ContextCallback`](#contextcallback)\<`T`> |
| `subscribe?` | `boolean` |

#### Returns

`boolean`

***

### requireAmbientConsent()

```ts theme={null}
function requireAmbientConsent(): ConsentStore;
```

The ambient consent store, created with the defaults if there is none.

#### Returns

[`ConsentStore`](/kit/api/core#consentstore)

***

### resetAmbientConsent()

```ts theme={null}
function resetAmbientConsent(): void;
```

Forget the ambient store. For tests, and for a hard teardown.

#### Returns

`void`

***

### resetAmbientShop()

```ts theme={null}
function resetAmbientShop(): void;
```

Forget the ambient shop. For tests, and for a hard teardown.

#### Returns

`void`

***

### resolvePath()

```ts theme={null}
function resolvePath(scope, path): unknown;
```

`"product.images.0.url"` against the scope. Missing anywhere → `undefined`,
never a throw: a template written for a product with images must not break
the page on the one product without any.

#### Parameters

| Parameter | Type |
| - | - |
| `scope` | [`Scope`](#scope) |
| `path` | `string` |

#### Returns

`unknown`

***

### setAmbientClient()

```ts theme={null}
function setAmbientClient(client): ShopContextValue;
```

Adopt a client you built yourself — a rig-pointed one, or one sharing a
server-rendered cart. Replaces the ambient shop, keeping the display
preferences and the analytics choice already configured.

#### Parameters

| Parameter | Type |
| - | - |
| `client` | [`ShopkitClient`](/kit/api/core#shopkitclient) |

#### Returns

[`ShopContextValue`](#shopcontextvalue)

***

### setCurrency()

```ts theme={null}
function setCurrency(currency): Promise<void>;
```

Browse the ambient shop in another currency — `client.setCurrency()` on the
client every element uses. Every `<qb-product>`, `<qb-product-list>` and
cart element reprices; the choice is remembered. `null` returns to the
configured default. Throws when nothing is configured.

```ts theme={null}
await setCurrency("EUR")
```

#### Parameters

| Parameter | Type |
| - | - |
| `currency` | `string` \| `null` |

#### Returns

`Promise`\<`void`>

***

### stopActionWarnings()

```ts theme={null}
function stopActionWarnings(): void;
```

Stop warning about unhandled actions. For tests, and for a hard teardown.

#### Returns

`void`

***

### variantView()

```ts theme={null}
function variantView(variant): VariantView;
```

#### Parameters

| Parameter | Type |
| - | - |
| `variant` | [`ProductVariant`](/kit/api/core#productvariant) \| `null` |

#### Returns

[`VariantView`](#variantview)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.