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

# Styling

> Theme the Live Components with global brand tokens and per-component tokens

All component styling is driven by `--dkl-*` **CSS styling tokens** (CSS custom properties),
each with a built-in fallback. There are two layers:

* **Global brand tokens** (`--dkl-color-*`, `--dkl-radius`, `--dkl-font-family`) — shared by
  every component, so setting one re-themes all of them at once.
* **Per-component tokens** (`--dkl-price-*`, `--dkl-vp-*`, `--dkl-badge-*`, `--dkl-og-*`) — fine
  control over a single component; documented on each component's **Styling & data** page.

Because tokens inherit through the shadow boundary, you can set them anywhere above a
component — the embed's **Custom CSS** field, a `:root` rule in your theme stylesheet, an
inline `style` on the tag, or the scoped rule an app block emits.

```css theme={null}
/* e.g. in the embed's Custom CSS field, or your theme stylesheet */
:root {
  --dkl-color-accent: #6d28d9;
  --dkl-radius: 12px;
  --dkl-price-sale-color: #c0392b;
}
```

## Global brand tokens

The embed emits these on `:root`. They back every component, and each component's own
`--dkl-<widget>-*` tokens take precedence over them for fine-grained control.

<ParamField body="--dkl-font-family" default="inherit">
  Component font family. Custom font families must be installed on the theme.
</ParamField>

<ParamField body="--dkl-color-text">
  Default text colour.
</ParamField>

<ParamField body="--dkl-color-accent">
  Accent / selected state.
</ParamField>

<ParamField body="--dkl-color-success">
  Success / savings colour.
</ParamField>

<ParamField body="--dkl-color-border">
  Border colour.
</ParamField>

<ParamField body="--dkl-radius">
  Corner radius for all components.
</ParamField>

## Per-component style tokens

Each component adds its own tokens (and, where relevant, CSS parts and light-DOM classes) on
top of the global ones. See its **Styling & data** page for the full list:

<CardGroup cols={2}>
  <Card title="Discount Price" icon="tag" href="/components/price/styling-and-data">
    `--dkl-price-*` tokens and `::part()` targets.
  </Card>

  <Card title="Volume Picker" icon="layer-group" href="/components/volume-picker/styling-and-data">
    `--dkl-vp-*` tokens, CSS parts, and light-DOM classes.
  </Card>

  <Card title="Discount Badge" icon="badge-percent" href="/components/discount-badge/styling-and-data">
    `--dkl-badge-*` tokens (light-DOM, host-only).
  </Card>

  <Card title="Order Goal" icon="bullseye-arrow" href="/components/order-goal/styling-and-data">
    `--dkl-og-*` tokens and light-DOM classes.
  </Card>
</CardGroup>

## App block vs. web component

<Info>
  Inside an **app block**, the editor's styling settings are written into a scoped `<style>`
  rule the block emits — so merchants style it entirely from the theme editor. A \*\*web component
  \*\* has no block context, so you style it by setting the same `--dkl-*` tokens in your own
  CSS. Every token has the same default in both places, so a web component with no
  overrides looks identical to a default app block.
</Info>

## Targeting structural elements

Tokens cover colours, sizes, and spacing. For anything structural, components expose two
mechanisms depending on where the element lives:

* **Shadow-DOM elements** are reached with [`::part(name)`](https://developer.mozilla.org/docs/Web/CSS/::part)
  from any rule — parts pierce the shadow boundary. You can add states (`::part(price):hover`)
  but cannot descend into a part (`::part(price) span` does not work).
* **Light-DOM elements** are reached with their **class**, like any other markup. They're
  already styled by the component's stylesheet, so override by matching the class and winning
  on specificity (or, preferably, use a token where one exists).

Each component's **Styling & data** page lists the parts and classes it exposes.

<Info>
  **Money formatting.** The price and volume picker reformat prices in JavaScript as tiers and
  variants change. To stay byte-identical to your server-rendered Liquid `money` filter, each
  carrier passes `shop.money_format` down as `data-money-format`, which the components render with
  — avoiding the locale-dependent currency prefixes (e.g. `US$`) that `Intl` would otherwise
  produce.
</Info>
