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

# Placements overview

> Control where Swish buttons appear on your store.

Placements let you control where Swish UI elements appear on your store. Each placement targets elements using a CSS selector and defines how a Swish component is mounted relative to the matched elements.

There are three default locations where Swish places buttons. You can customize each one or add new placements entirely.

| Location | What gets placed | Description |
| - | - | - |
| [Header](https://swish.app/docs/guides/customize/placements/header.md) | Home button | Opens the Swish drawer. Shows saved item count. |
| [Collections](https://swish.app/docs/guides/customize/placements/collections.md) | Save button | Save/unsave toggle on product cards in collection grids. |
| [Product detail](https://swish.app/docs/guides/customize/placements/product-detail.md) | Save button | Save/unsave toggle on the product detail page. |

## How placements work

By default, Swish places buttons through the app embed. Use the placement API for custom positioning:

```js theme={null}
swish.placements.create({
  id: "collection-save-button",
  target: {
    selector: ".product-card",
  },
  mount: {
    mode: "append",
  },
  element: {
    name: "save-button",
  },
  layout: {
    type: "overlay",
    anchor: "top-right",
    offset: { x: 8, y: 8 },
  },
});
```

Swish watches the DOM using a mutation observer, so buttons are automatically mounted on elements that are added dynamically (e.g. infinite scroll, quick view modals).

Add the `swish-ignore` attribute to any target element that should be skipped:

```html theme={null}
<div class="product-card" swish-ignore>...</div>
```

## Passing placements at initialization

You can also pass placements under the `placements` option in `SwishOptions`:

```js theme={null}
window.SwishOptions = {
  placements: [
    {
      id: "collection-save-button",
      target: { selector: ".product-card" },
      mount: { mode: "append" },
      element: { name: "save-button" },
      layout: { type: "overlay", anchor: "top-right" },
    },
  ],
};
```

## Mount options

The `mount` object controls where the button is inserted relative to the target.

| Option | Type | Description |
| - | - | - |
| `mode` | `"before" \| "after" \| "append" \| "prepend" \| "replace"` | Where to insert the button relative to the resolved element. |
| `at` | `"target" \| { query } \| { closest } \| { nearest } \| { withinClosest } \| ".selector"` | Resolve a different element to mount against. Defaults to `"target"`. |

The `at` option lets you insert the button relative to a parent or child of the target element:

```js theme={null}
mount: {
  at: { closest: ".product-card__actions" },
  mode: "append",
}
```

| `at` value | Description |
| - | - |
| `"target"` | The matched element itself. |
| `{ query: ".selector" }` | First descendant matching the selector. |
| `{ closest: ".selector" }` | Nearest ancestor matching the selector. |
| `{ nearest: ".selector" }` | Walk up ancestors, querying each for the selector. Returns the closest match. |
| `{ withinClosest: { root, selector } }` | Find `root` ancestor, then query `selector` within it. |
| `".selector"` | Shorthand for `{ nearest: ".selector" }`. |

## Layout types

The layout determines how the button is positioned. Choose between overlay, anchored, and inline.

### Overlay

Position the button as an absolute overlay anchored to a corner of the target's parent. This is the typical layout for save buttons on collection cards.

```js theme={null}
layout: {
  type: "overlay",
  anchor: "top-right",
  zIndex: 10,
  offset: { x: 8, y: 8 },
},
```

| Option | Type | Default | Description |
| - | - | - | - |
| `anchor` | `"top-left" \| "top-right" \| "bottom-left" \| "bottom-right"` | `"bottom-right"` | Corner to anchor the button. |
| `zIndex` | `number \| string` | `10` | Stack order. An integer or `"auto"`; other values are ignored. |
| `offset` | `{ x?, y? }` | `{ x: 0, y: 0 }` | Pixel offset from the anchor corner. Supports responsive values. |

### Inline

Place the button in a flex row alongside the target element. This is the typical layout for save buttons on product detail pages.

```js theme={null}
layout: {
  type: "inline",
  rowSelector: ".product-form__actions",
},
```

| Option | Type | Description |
| - | - | - |
| `rowSelector` | `string` | Optional. CSS selector for an existing flex row to append to instead of creating a new wrapper. |

### Anchored

Position the button relative to any element in the DOM, even if it is a sibling or cousin of the mount target. Use this when the element you want to overlay is not a direct parent of where the button is inserted.

```js theme={null}
layout: {
  type: "anchored",
  to: { query: ".product-card__image" },
  placement: "top-end",
  inside: true,
  offset: 8,
},
```

| Option | Type | Default | Description |
| - | - | - | - |
| `to` | `MountAt` | mount target | Element to visually anchor to, resolved from the target. Accepts the same values as `mount.at`. |
| `placement` | `string` | `"bottom-end"` | Position relative to the anchor. Any [Floating UI placement](https://floating-ui.com/docs/computePosition#placement) such as `"top-start"`, `"top-end"`, `"bottom-start"`, or `"bottom-end"`. |
| `inside` | `boolean` | `false` | Position inside the anchor's bounds rather than adjacent to it. When `true`, `placement` maps to corners within the anchor. |
| `offset` | `number` | `0` | Pixel distance from the anchor edge. Acts as an inward inset when `inside` is `true`. |
| `zIndex` | `number \| string` | `10` | Stack order. An integer or `"auto"`; other values are ignored. |

## Styling

Pass design tokens under `design` to style a button per placement. All values support responsive breakpoints.

### Save button tokens

| Token | Type | Description |
| - | - | - |
| `background` | `string` | Background color in the default state. |
| `savedBackground` | `string` | Background color when the product is saved. |
| `color` | `string` | Icon/text color in the default state. |
| `savedColor` | `string` | Icon/text color when the product is saved. |
| `height` | `string \| number` | Button height. Numbers are treated as pixels. |
| `radius` | `string \| number` | Border radius. Numbers are treated as pixels. |

### Home button tokens

| Token | Type | Description |
| - | - | - |
| `background` | `string` | Button background color. |
| `color` | `string` | Icon color. |
| `height` | `string \| number` | Button height. Numbers are treated as pixels. |
| `radius` | `string \| number` | Border radius. Numbers are treated as pixels. |

### Responsive values

Every design token accepts a responsive object with named breakpoints. The default breakpoints are `sm` at 500px and `lg` at 700px.

```js theme={null}
design: {
  height: {
    base: 32,
    sm: 36,
    lg: 44,
  },
},
```

You can define custom breakpoints on the placement:

```js theme={null}
swish.placements.create({
  id: "custom-breakpoints",
  breakpoints: { tablet: 768, desktop: 1024 },
  design: {
    height: { base: 32, tablet: 40, desktop: 48 },
  },
  // ...
});
```

## Save button attributes

When creating save buttons, you can set these HTML attributes:

| Attribute | Type | Default | Description |
| - | - | - | - |
| `product-id` | `string` | `""` | Shopify product ID. Automatically resolved from the target element's context when using placements. |
| `variant-id` | `string` | `""` | Shopify variant ID. |
| `variant` | `"primary" \| "secondary" \| "neutral" \| "danger"` | `"primary"` | Button color variant. |
| `appearance` | `"solid" \| "glass" \| "plain"` | `"solid"` | Button style. |
| `icon-only` | `"true" \| "false"` | `"false"` | Show only the heart icon without text. |
| `size` | `"xs" \| "sm" \| "md" \| "lg"` | `"md"` | Button size. |
| `match` | `"variant" \| "product" \| "auto"` | `"auto"` | How saved state is interpreted and what a save targets. See [match options](https://swish.app/docs/references/sdk/state.md#options). |
| `accessibility-label` | `string` | `""` | Custom ARIA label. Defaults to "Save item" or "Unsave item" based on state. |
| `swish-save-animation` | `"none" \| "pulse" \| "jello" \| "particles"` | `""` | Animation played when a tap saves the item. Blank follows [`ui.saveButton.animation`](https://swish.app/docs/references/sdk/configuration#save-button), which placements set here. |

> [!NOTE]
>
> When using placements, the `product-id` and `variant-id` attributes are
> automatically resolved from the target element's data attributes
> (`data-product-id`, `data-variant-id`, `data-product-handle`) or from the page
> URL.

## Save button animation

Save buttons can play an animation when a customer taps one to save an item: **Pulse**, **Jello** or **Particles**, a burst of small copies of the icon. Pick it under **Global → Save button → Save animation**, or set [`ui.saveButton.animation`](https://swish.app/docs/references/sdk/configuration#save-button). Every save button plays the same one, placed buttons and the hearts in the drawer alike. It plays once the save goes through, never for an item that was already saved, and not for customers who turn on reduced motion.

## Home button attributes

| Attribute | Type | Default | Description |
| - | - | - | - |
| `icon-only` | `"true" \| "false"` | `"false"` | Show only the heart icon, without `text`. |
| `text` | `string` | `""` | Label shown beside the icon. Blank renders icon-only. |
| `hide-status` | `"true" \| "false"` | `"false"` | Hide the notification badge entirely. |
| `hide-count` | `"true" \| "false"` | `"false"` | Hide the numeric count but still show the dot indicator. |
| `show-zero-count` | `"true" \| "false"` | `"false"` | Show the badge even when the count is zero. |
| `accessibility-label` | `string` | `""` | Custom ARIA label for the button. |

## Programmatic creation

Create buttons outside of the placement system:

```js theme={null}
const saveButton = await swish.ui.createElement("save-button");
saveButton.setAttribute("product-id", "8234567890123");
document.querySelector(".my-container").appendChild(saveButton);

const homeButton = await swish.ui.createElement("header-button");
document.querySelector(".header__icons").prepend(homeButton);
```

## Further reading

* [Placements reference](https://swish.app/docs/references/sdk/placements.md) for the complete API
* [Intents](https://swish.app/docs/guides/customize/intents.md) to configure save behavior
* [Design](https://swish.app/docs/guides/customize/appearance/design.md) for global design tokens


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