# Chrome stable classes (/themes/chrome-classes)

The platform supplies default parts for the brand, cart, user menu, promo bar, chat, locale banner
and product cards. Themes render them through the SDK or through the platform blocks in their
header and footer. Restyle these parts through the stable classes below. The classes exist only
on the platform's parts, not on markup a theme or merchant writes.

Each class is a bare kebab-case token on the element's `className`. It carries no styling of its
own and is part of the theme contract: the platform may change the Tailwind utilities around it,
never the token.

## The override pattern [#the-override-pattern]

Write the rules in your theme's `src/styles/theme.css`, **unlayered**, with `:where()`:

```css title="src/styles/theme.css"
:where(.header-brand) {
  letter-spacing: 0.08em;
}

:where(.cart-count) {
  background: var(--color-surface);
  border-color: var(--color-line);
}
```

Two things make this work:

- **Unlayered CSS beats Tailwind.** The platform's utilities live in `@layer utilities`. An
  unlayered rule wins against every layered one regardless of specificity, so you need neither
  `!important` nor a more specific selector.
- **`:where()` keeps specificity at 0.** Your rule stays the easiest thing in the cascade to
  override, so a later rule of your own, or a state class on the same element, still wins.
  Source order becomes the override mechanism.

> Chrome mounts **outside** `.theme-shell`. A `.theme-shell .header-brand { … }` rule never matches
> the header. Scope chrome rules to `:root` or leave them unscoped.

`header-brand` sits on the link drawn by `Brand` and `Logo` from `@poltura/theme-sdk/chrome` and
by the `logo` block. Those also render in most theme **footers**, so a bare `.header-brand` rule
reaches the footer logo too. Scope footer-only rules through your own footer class.

## The classes [#the-classes]

| Class                      | Surface           | Notes                                                                 |
| -------------------------- | ----------------- | --------------------------------------------------------------------- |
| `header-brand`             | brand             | the brand link: `Brand`, `Logo` and the `logo` block                  |
| `header-brand-mark`        | brand             | square initial badge drawn when the store has no logo                 |
| `header-brand-label`       | brand             | the store name drawn when the store has no logo                       |
| `cart-button`              | cart              | `CartButton` trigger `<button>` (also carries `data-cart='header'`)   |
| `cart-count`               | cart              | item count badge                                                      |
| `cart-button-label`        | cart              | the trigger's text label                                              |
| `cart-panel`               | cart              | desktop popover and mobile sheet content                              |
| `cart-panel-title`         | cart              | panel heading                                                         |
| `user-menu-trigger`        | user menu         | `UserMenu` (and the `account` block) trigger `<button>`              |
| `user-menu-content`        | user menu         | dropdown content root                                                 |
| `user-menu-label`          | user menu         | every label in the menu                                               |
| `promo-bar`                | promo bar         | `PromoBar` strip root                                                 |
| `promo-bar-inner`          | promo bar         | centred container                                                     |
| `promo-bar-link`           | promo bar         | the announcement link                                                 |
| `inbox-root`               | chat widget       | fixed-position wrapper (`data-poltura-inbox`)                         |
| `inbox-launcher`           | chat widget       | launcher button                                                       |
| `inbox-panel`              | chat widget       | dialog root                                                           |
| `inbox-panel__header`      | chat widget       | title and close row                                                   |
| `inbox-messages`           | chat widget       | scroll region (`role="log"`)                                          |
| `inbox-message-row`        | chat widget       | wrapper for author, bubble and metadata; `--system` for system text   |
| `inbox-message`            | chat widget       | actual bubble only; plus `--customer`, `--staff`, `--ai`              |
| `inbox-composer`           | chat widget       | text field, attach and send                                           |
| `inbox-status`             | chat widget       | offline / reconnecting line                                           |
| `inbox-offline`            | chat widget       | offline capture form                                                  |
| `locale-banner`            | locale banner     | `LocaleBanner` suggestion dialog root                                 |
| `locale-banner-inner`      | locale banner     | padded body                                                           |
| `locale-banner-switch`     | locale banner     | primary "switch language" button                                      |
| `locale-banner-stay`       | locale banner     | secondary "stay" button                                               |
| `product-card-body`        | product card      | `ProductCard` content column                                          |
| `product-card-title`       | product card      | product title                                                         |
| `product-card-price`       | product card      | price block                                                           |
| `featured-view-all-mobile` | product rail      | the "view all" link under a `ProductRail` on small screens            |
| `theme-section-band`       | built-in sections | a full-bleed band section root                                        |
| `theme-section-centered`   | built-in sections | a centred content column inside a section                             |

The full list of chat widget classes is on [Chat widget chrome](/themes/chat-widget).

## Header states [#header-states]

The header is a section group (`layout/header.json`), so its pieces are elements that carry the
merchant's own style settings rather than class hooks. Its scroll state is exposed as attributes
for CSS:

| Attribute                         | On                         | Meaning                                                    |
| --------------------------------- | -------------------------- | ---------------------------------------------------------- |
| `data-theme-chrome='header'`      | the pinned header box      | the promo bar and header together                          |
| `data-header-behavior`            | the pinned header box      | `static`, `sticky` or `reveal`                             |
| `data-header-overlay`             | the pinned header box      | the header lies over the first section on this page        |
| `data-header-scrolled`            | the pinned header box      | the page has scrolled past the header                      |
| `data-header-hidden`              | the pinned header box      | a revealing header is hidden while the shopper scrolls down |
| `data-header-shrink`, `-shrunk`   | the pinned header box      | the header shrinks once scrolled, and has shrunk           |
| `data-header-section`             | the `header` section root  | the header bar                                             |
| `data-header-mobile-from`         | the `header` section root  | `mobile`, `tablet` or `desktop`: where the drawer takes over |

```css
:where([data-theme-chrome='header'][data-header-scrolled]) [data-header-section] {
  box-shadow: 0 1px 0 var(--border);
}
```

`useHeaderVisibility()` from `@poltura/theme-sdk/chrome` reads the same `scrolled` and `hidden`
states in code.

## Floating elements [#floating-elements]

Anything a theme pins to the bottom of the viewport (a sticky buy bar, a floating button, its own
banner) adds three offsets to its `bottom`, each `0px` when unset:

| Variable                        | Set while                                                                         |
| ------------------------------- | --------------------------------------------------------------------------------- |
| `--banner-bottom-offset`        | a bottom banner bar is showing (its height)                                       |
| `--preview-bar-offset`          | the private-preview bar is showing (its height)                                   |
| `--poltura-editor-inset-bottom` | the page is on the Customizer canvas and the Customizer covers its bottom (on phones) |

```css
:where(.my-sticky-buy) {
  position: fixed;
  bottom: calc(
    var(--banner-bottom-offset, 0px) + var(--preview-bar-offset, 0px) +
      var(--poltura-editor-inset-bottom, 0px)
  );
}
```

The platform's chat launcher, back-to-top button, banners and cookie bar already do this.
`--poltura-editor-inset-bottom` exists only inside the Customizer, so the live storefront is
unchanged. Without it, an element pinned to the bottom sits under the Customizer's tab bar or
settings sheet on a phone.

## What is not hooked [#what-is-not-hooked]

The header's menu, search and language blocks, `MainNav`, `MobileNav`, `MegaMenu`,
`MobileMenu`, `HeaderSearch` and `LocaleSwitcher` carry no class hooks. Style them through the
element's style settings, the `className` and `classes` props the SDK parts take (see the
[chrome](/themes/sdk/reference/chrome), [navigation](/themes/sdk/reference/navigation) and
[search](/themes/sdk/reference/search) references), the header attributes above, or their
containers.

## Next [#next]

- [Header mega-menus](/themes/mega-menus): wide header dropdowns built from blocks.
- [Chat widget chrome](/themes/chat-widget): every class and part of the chat widget.
