> For the complete documentation index, see [llms.txt](https://analog.groupthought.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://analog.groupthought.com/developer-platform/custom-code.md).

# Custom code

How to load and use custom.css, custom.events.js, and custom.alpine.js.

The theme includes `assets/custom.css`, `assets/custom.events.js`, and `assets/custom.alpine.js` for custom code. Each file contains comments and examples but doesn't load by default. Uncomment its matching line in the head snippets to load it. Theme updates leave these custom files alone.

## Pick a file

Pick the file by the kind of code, then uncomment one line to load it:

| File                      | Loads as                      | Use for                                                         |
| ------------------------- | ----------------------------- | --------------------------------------------------------------- |
| `assets/custom.css`       | Stylesheet, after `theme.css` | Your own CSS                                                    |
| `assets/custom.events.js` | Classic script, `defer`       | Listeners for [theme events](/developer-platform/events.md)     |
| `assets/custom.alpine.js` | Module script, `defer`        | Your own Alpine directives, data components, magics, and stores |

Edit the files and their loading lines in the theme code editor or your local checkout.

Use a [Custom liquid block](/developer-platform/blocks.md) for an embed, section-specific `<style>`, or structured data. Use these files for code that applies across the store.

## Custom CSS

Uncomment this line in `snippets/head.liquid`:

```liquid
{{ 'custom.css' | asset_url | stylesheet_tag }}
```

The line is directly after the `theme.css` tag. Your rules load later and win when specificity is equal. Before adding CSS, check whether a setting or CSS variable already covers the change. See [global settings & local overrides](/core-concepts/settings-cascade.md).

## Listening for theme events

Uncomment this line in `snippets/head-scripts.liquid`:

```liquid
<script src="{{ 'custom.events.js' | asset_url }}" defer="defer"></script>
```

The file includes commented listeners for variant changes, cart changes, and sections entering the viewport. The full event list with payloads is on the [theme events](/developer-platform/events.md) page. A listener looks like this:

```js
window.addEventListener('shopify:product:select', (event) => {
  const {variantId, productId, sectionId} = event.detail;
  // React to the change.
});
```

Listeners run when the event fires, which is after the theme's JavaScript started, so `window.Alpine` and the Alpine stores are available inside them:

```js
window.addEventListener('shopify:cart:lines-update', async (event) => {
  const {cart, detail} = await event.promise;

  if (Alpine.store('drawer').isOpen && Alpine.store('drawer').type === 'cart') {
    // The cart drawer is open. cart is the cart after the change.
  }
});
```

## Extending Alpine

Uncomment this line in `snippets/head-scripts.liquid`:

```liquid
<script src="{{ 'custom.alpine.js' | asset_url }}" type="module" defer="defer"></script>
```

Keep the script as a module. The import map resolves `alpinejs` to the Alpine instance already used by the theme. Import it instead of loading another copy:

```js
import Alpine from 'alpinejs';

document.addEventListener('alpine:init', () => {
  Alpine.data('myComponent', () => ({isCool: true}));

  Alpine.directive('my-directive', ($el, {expression, modifiers, evaluate}) => {
    // Add behaviour to $el.
  });

  Alpine.magic('myMagic', ($el) => () => {
    // Callable from x-init, @click, and other expressions.
  });

  Alpine.store('myStore', {isCool: true});
});
```

Register directives, data components, magics, and stores inside the `alpine:init` listener so they exist when Alpine initializes.

Once registered, use them from markup. For example, add `x-data="myComponent"` inside a [Custom liquid block](/developer-platform/blocks.md). The import map also exposes the theme's other modules (`@theme/utils`, `motion`, `@floating-ui/dom`, `tabbable`) under the names listed in `snippets/head-scripts.liquid`.

## Theme globals

`snippets/head-scripts.liquid` defines a `window.theme` object your code can read:

| Key                 | What it holds                                                                                   |
| ------------------- | ----------------------------------------------------------------------------------------------- |
| `theme.routes`      | Localized URLs for the cart endpoints, search, predictive search, and product recommendations   |
| `theme.currency`    | The locale, currency code, and the shop's money format string                                   |
| `theme.cart.action` | The "Add to cart action" setting value (`show_drawer`, `show_notification`, or `navigate_page`) |
| `theme.screen`      | The theme's breakpoint widths (`sm` through `2xl`)                                              |
| `theme.info`        | Theme name and version                                                                          |

Use `theme.routes` instead of hard-coding `/cart/add.js` and similar paths. The route may include a locale prefix.

## See also

* [Theme events](/developer-platform/events.md): events available to `custom.events.js`
* [Developer blocks](/developer-platform/blocks.md): code that belongs inside a section
* [Global settings & local overrides](/core-concepts/settings-cascade.md): settings and CSS variables to check first
