# Buy X get Y styling

> Class names, state attributes and CSS custom properties for the Buy X get Y banner and the cart claim row.

Source: https://backtrackhq.com/docs/widgets/buy-x-get-y/styling/

This page is for theme developers. It lists the classes, states and CSS
custom properties of the Buy X get Y widget. It has two parts: the
product page banner (an `<andor-bundle>` host) and the cart claim row
(inside an `<andor-claim>` host).

The widget comes from the **AndOr Offer picker** block and the pasted
`<andor-offer-picker>` and `<andor-claim>` tags. The cart drawer copy
of the claim row comes from the app embed.

## What each token changes

The numbers on the diagram match the table. The diagram is a simplified drawing. The full list of tokens is below.

### Product page banner
(Diagram: The Buy X get Y banner on a product page with a part-filled progress bar)
1. `--andor-bundle-text-color`: the message
2. `--andor-bundle-track-background`: the empty track
3. `--andor-bundle-fill-color`: the fill
4. `--andor-bundle-bar-font-size`: the count
5. `--andor-bundle-note-color`: the note

### Cart claim row
(Diagram: The Buy X get Y claim row in the cart with a picture, prices, a variant menu and a Claim button)
1. `--andor-claim-title-color`: the title
2. `--andor-claim-image-radius`: picture corners
3. `--andor-claim-was-color`: the struck price
4. `--andor-claim-now-color`: the claim price
5. `--andor-claim-button-background`: the button fill
6. `--andor-claim-button-radius`: button corners
7. `--andor-claim-border-color`: the box border

## How precedence works

Put your CSS in your theme's stylesheet or in the app embed's
**Custom CSS** box. To see which value wins, read
[Which value wins](https://backtrackhq.com/docs/developers/tokens/#which-value-wins).

What this means for Buy X get Y:

- **Button color** is written inline on each host, but only when the
  merchant changed it from `#c0392b`. Left on the default, the inline
  value is `var(--andor-color-accent)`, so a `:root` rule or the brand
  color reaches it.
- Both parts read the global AndOr tokens first, so most stores only
  need a few custom properties.

> **Tip:** Change a custom property rather than a part's own `padding`,
> `height` or `font-size`. The widgets print a skeleton at the finished
> size, and the custom properties size both.

## Product page banner

Host:
`<andor-bundle class="andor-bundle" data-andor-widget="bxgy">`.

### Parts

| Class | What it is |
|---|---|
| `.andor-bundle` | the host |
| `.andor-bundle__text` | the offer message, for example "Buy 2, get 1 at 50% off" |
| `.andor-bundle__bar` | the bar and its count |
| `.andor-bundle__track` | the track (`role="progressbar"`) |
| `.andor-bundle__fill` | the filled part |
| `.andor-bundle__count` | "1 of 2 in your cart" |
| `.andor-bundle__note` | "Add 1 more to unlock it." |

### States and data attributes

| Attribute | On | Values and meaning |
|---|---|---|
| `data-andor-widget` | host | `bxgy` for this widget |
| `data-andor-progress` | host | `locked`: not enough X items yet |
| | | `unlocked`: earned, claim it in the cart |
| | | `won`: checkout applied the deal |
| | | `off`: earned, but another discount won |
| `data-andor-ready` | host | drawn. Before that, the skeleton shimmers |
| `data-andor-state` | host | `active` or `hidden` (written on every widget host) |
| `hidden` | bar or note | not needed in this state |

### Custom properties

"Accent" is the block's **Button color**, written inline on the host as
`--andor-bundle-accent`, then `--andor-color-accent`, then `#c0392b`.

| Property | Default | Controls |
|---|---|---|
| `--andor-bundle-accent` | block setting, inline | accent |
| `--andor-bundle-space-block-start` | `0.75em` | margin above |
| `--andor-bundle-space-block-end` | `0.75em` | margin below |
| `--andor-bundle-color` | `inherit` | text color |
| `--andor-bundle-font-size` | `inherit` | base size |
| `--andor-bundle-text-color` | `inherit` | message color |
| `--andor-bundle-text-font-weight` | `inherit` | message weight |
| `--andor-bundle-bar-font-size` | `0.9em` | the bar's count |
| `--andor-bundle-bar-font-weight` | `600` | the bar's count weight |
| `--andor-bundle-track-height` | `0.5em` | track thickness |
| `--andor-bundle-track-radius` | `0.25em` | track corners |
| `--andor-bundle-track-background` | `--andor-color-border`, 8% ink | empty track |
| `--andor-bundle-fill-color` | accent | the fill |
| `--andor-bundle-note-color` | `inherit` | the note |
| `--andor-bundle-note-font-size` | `0.9em` | the note's size |

## Cart claim row

Host:
`<andor-claim class="andor-claim">`, on the cart page (the block) and in
the cart drawer (the app embed's copy). A Buy X get Y box is
`.andor-claim__box:not(.andor-claim__box--gift)`.

### Parts

| Class | What it is |
|---|---|
| `.andor-claim` | the host |
| `.andor-claim__box` | one offer's box |
| `.andor-claim__title` | "Add it for 50% off" |
| `.andor-claim__list` | the Y products |
| `.andor-claim__row` | one Y product |
| `.andor-claim__img` | its picture (a background image) |
| `.andor-claim__body` | name, price and variant choice |
| `.andor-claim__name` | the product name |
| `.andor-claim__price` | the price line |
| `.andor-claim__was` | the struck price |
| `.andor-claim__now` | the claim price |
| `.andor-claim__option` | the variant choice: a label and a `select` |
| `.andor-claim__go` | the **Claim** button |
| `.andor-claim__live` | the screen reader status line, hidden |

### States and data attributes

| Attribute or class | On | Meaning |
|---|---|---|
| `data-andor-state="unlocked"` | box | the claim row: X is met |
| `data-andor-state="off"` | box | claimed, another discount won |
| `.andor-claim__box--note` | box | a one line box, no rows |
| `data-andor-ready` | host | drawn from the cart. Before that, boxes shimmer |
| `data-andor-place` | host | `cart_page` or `cart_drawer` |
| `:disabled` | `.andor-claim__go` | the claim is being added |
| `[tabindex="-1"]:focus` | `.andor-claim__title` | focus kept after a redraw |

> **Caution:** Style states with color, not size. A state attribute is
> written only on the drawn box, so a size change per state makes the
> cart move when the box is drawn.

### Custom properties

"Accent" is the block's **Button color**, written inline as
`--andor-claim-accent`, then `--andor-color-accent`, then `#c0392b`.

| Property | Default | Controls |
|---|---|---|
| `--andor-claim-accent` | block setting, inline | accent |
| `--andor-claim-space-block-start` | `0.75em` | margin above |
| `--andor-claim-space-block-end` | `0.75em` | margin below |
| `--andor-claim-color` | `inherit` | text color |
| `--andor-claim-font-size` | `inherit` | base size |
| `--andor-claim-box-space` | `0.75em` | space under each box |
| `--andor-claim-gap` | `0.6em` | space inside a box |
| `--andor-claim-padding` | `0.9em` | box padding |
| `--andor-claim-note-padding-block` | `0.6em` | a one line box |
| `--andor-claim-border-width` | `2px` | box border |
| `--andor-claim-border-style` | `dashed` | box border |
| `--andor-claim-border-color` | accent | box border |
| `--andor-claim-radius` | twice `--andor-radius`, `4px` | box corners |
| `--andor-claim-background` | `transparent` | box fill |
| `--andor-claim-title-color` | `inherit` | title color |
| `--andor-claim-title-font-size` | `inherit` | title size |
| `--andor-claim-title-font-weight` | `700` | title weight |
| `--andor-claim-row-gap` | `0.75em` | space between rows |
| `--andor-claim-image-size` | `4em` | a row's picture |
| `--andor-claim-image-radius` | `--andor-radius`, `4px` | picture corners |
| `--andor-claim-image-background` | `#f3f3f3` | empty picture |
| `--andor-claim-name-color` | `inherit` | product name |
| `--andor-claim-name-font-weight` | `inherit` | product name |
| `--andor-claim-was-color` | `--andor-color-muted`, `#6d7175` | struck price |
| `--andor-claim-now-color` | accent | claim price |
| `--andor-claim-button-padding` | `0.6em 1.1em` | button padding |
| `--andor-claim-button-border-color` | accent | button border |
| `--andor-claim-button-background` | accent | button fill |
| `--andor-claim-button-color` | `--andor-color-on-accent`, `#ffffff` | button text |
| `--andor-claim-button-radius` | `--andor-radius`, `4px` | button corners |
| `--andor-claim-button-font-weight` | `600` | button weight |

## Examples

A green banner once checkout applied the deal:

```css
andor-bundle[data-andor-progress="won"] {
  --andor-bundle-text-color: #1f5f3a;
  --andor-bundle-fill-color: #1f5f3a;
}
```

Dawn: match the theme's buttons and color scheme.

<p class="docs-code-file">assets/base.css</p>

```css
andor-bundle:is([data-andor-widget="bxgy"], [data-andor-widget="gift"]) {
  --andor-bundle-track-background: rgba(var(--color-foreground), 0.08);
  --andor-bundle-fill-color: rgb(var(--color-foreground));
  --andor-bundle-text-font-weight: 600;
}

andor-claim {
  --andor-claim-border-color: rgba(var(--color-foreground), 0.3);
  --andor-claim-radius: var(--popup-corner-radius, 0);
  --andor-claim-button-background: rgb(var(--color-button));
  --andor-claim-button-border-color: rgb(var(--color-button));
  --andor-claim-button-color: rgb(var(--color-button-text));
  --andor-claim-button-radius: var(--buttons-radius, 0);
}
```

Horizon:

<p class="docs-code-file">assets/base.css</p>

```css
andor-bundle:is([data-andor-widget="bxgy"], [data-andor-widget="gift"]) {
  --andor-bundle-track-background: var(--color-border);
  --andor-bundle-fill-color: var(--color-foreground);
  --andor-bundle-text-font-weight: 600;
}

andor-claim {
  --button: var(--color-primary-button-background);
  --andor-claim-border-color: var(--color-border);
  --andor-claim-button-background: var(--button);
  --andor-claim-button-border-color: var(--button);
  --andor-claim-button-color: var(--color-primary-button-text);
  --andor-claim-button-radius: var(--style-border-radius-buttons-primary);
}
```

> **Note:** The theme variable names were checked against Dawn 16.0.0 and
> Horizon 4.2.0. A later theme version may rename them.

> **Caution:** Never force a hidden part back on. Hidden parts carry
> `hidden` and an inline `display: none`. A rule like
> `andor-bundle { display: block }` can show a deal that ended.
