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

# Advanced styling for Easy themes

> Add theme CSS, block CSS, custom classes, custom HTML, and scrolling announcement bars without a code storefront.

Easy themes provide several styling surfaces before you need an Advanced code storefront.
Use the smallest surface that matches your change.

| Surface          | Scope                   | Limit                      | Best use                              |
| ---------------- | ----------------------- | -------------------------- | ------------------------------------- |
| Theme Custom CSS | The complete storefront | 65,536 UTF-8 bytes (64 KB) | Fonts, animations, and shared styles  |
| Section CSS      | One block               | 16,384 UTF-8 bytes (16 KB) | A unique layout or shape              |
| Extra Classes    | One block root          | 256 characters             | Stable class names for CSS selectors  |
| Custom HTML      | One sandboxed iframe    | 262,144 characters         | Self-contained HTML, CSS, and scripts |
| Announcement Bar | The shop header         | 160 text characters        | Static or scrolling promotion text    |

## Add theme-level CSS

Theme Custom CSS applies across the storefront. Shoppex scopes each selector to the storefront container.
Theme CSS can declare global names with `@keyframes` and `@font-face`.

1. Open the Easy theme in the visual Builder.
2. Select the **Theme** tab.
3. Open **Custom CSS**.
4. Paste the CSS.
5. Preview the affected pages.
6. Publish the theme.

### Animate a heading

First, select the heading block. Open **Custom CSS & Class**, then paste `glitch-heading` into **Extra Classes**.

Paste this example into the theme-level **Custom CSS** field:

```css theme={"system"}
@keyframes cyber-glitch {
  0%, 88%, 100% {
    transform: translate(0);
    text-shadow: 2px 0 #00f5ff, -2px 0 #ff2bd6;
  }
  90% {
    transform: translate(-2px, 1px);
    text-shadow: 5px 0 #00f5ff, -5px 0 #ff2bd6;
  }
  92% {
    transform: translate(2px, -1px);
    text-shadow: -4px 0 #00f5ff, 4px 0 #ff2bd6;
  }
}

.glitch-heading h1,
.glitch-heading h2 {
  animation: cyber-glitch 2.8s steps(1, end) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .glitch-heading h1,
  .glitch-heading h2 {
    animation: none;
  }
}
```

The extra class gives the block a stable selector. The animation affects headings inside that block only.

### Load a font outside the curated list

`@font-face` can load a font from a full HTTPS URL. This example loads Atkinson Hyperlegible from an external font CDN.

```css theme={"system"}
@font-face {
  font-family: "Atkinson Shop";
  src: url("https://cdn.jsdelivr.net/fontsource/fonts/atkinson-hyperlegible@5.2.8/latin-400-normal.woff2") format("woff2");
  font-style: normal;
  font-weight: 400;
  font-display: swap;
}

body {
  font-family: "Atkinson Shop", system-ui, sans-serif;
}
```

The font host must permit cross-origin font requests. For your own asset, use its complete HTTPS URL.

## Style one block

Section CSS stays inside one block. Shoppex adds the block ID to each selector before it renders the page.

1. Select the block in the Builder.
2. Open **Custom CSS & Class** in the block inspector.
3. Paste `cyber-card` into **Extra Classes**.
4. Paste the CSS into **Section CSS**.

```css theme={"system"}
body.cyber-card {
  clip-path: polygon(
    0 18px,
    18px 0,
    100% 0,
    100% calc(100% - 18px),
    calc(100% - 18px) 100%,
    0 100%
  );
  border: 1px solid #00f5ff;
  background: linear-gradient(135deg, #0b1020, #17102a);
  box-shadow: inset 0 0 24px rgb(0 245 255 / 12%);
}

body.cyber-card:hover {
  border-color: #ff2bd6;
}
```

In Section CSS, a leading `body` targets the block root. Here, `body.cyber-card` targets the root with your extra class.

Extra Classes accepts letters, numbers, hyphens, and underscores. Separate multiple class names with spaces.

## Know the CSS safety rules

Shoppex parses Custom CSS before it saves or renders the theme. Invalid CSS returns an error instead of partial output.

| The sanitizer rejects                                                  | Use instead                                       |
| ---------------------------------------------------------------------- | ------------------------------------------------- |
| `@import`                                                              | Paste the required CSS into Custom CSS.           |
| Relative, `http://`, protocol-relative, and `data:` values in `url()`  | Use a full `https://` URL or a local `#fragment`. |
| The `behavior` and `-moz-binding` properties                           | Use standard CSS properties.                      |
| The `expression()`, `image-set()`, and `-webkit-image-set()` functions | Use static values or one HTTPS asset URL.         |
| A closing `</style>` tag                                               | Put CSS only in the CSS field.                    |
| Escapes in property or function names                                  | Write the property or function name directly.     |

Theme CSS permits `@keyframes`, `@font-face`, `@page`, and `@property`.
Section CSS cannot contain these page-wide rules. Declare them in theme-level Custom CSS, then reference them from Section CSS.

For example, Section CSS can use `animation: cyber-glitch 2.8s infinite` after theme CSS declares `@keyframes cyber-glitch`.

## Add self-contained HTML

Use a `custom-html` block for a self-contained interface that needs HTML, CSS, or JavaScript.
Shoppex renders the block in a separate sandboxed iframe.

1. Add a **Custom HTML** block to the page.
2. Paste the complete example into the **HTML** field.
3. Set **Height mode** to **Auto**.
4. Preview the page.

```html theme={"system"}
<div class="access-card">
  <p class="label">MEMBER ACCESS</p>
  <button type="button" id="reveal-code">Reveal access code</button>
  <p id="access-code" hidden>NEON-20</p>
</div>

<style>
  body {
    margin: 0;
    background: transparent;
    font-family: system-ui, sans-serif;
  }

  .access-card {
    padding: 24px;
    border: 1px solid #00f5ff;
    color: #f8fafc;
    background: #0b1020;
    clip-path: polygon(0 16px, 16px 0, 100% 0, 100% calc(100% - 16px), calc(100% - 16px) 100%, 0 100%);
  }

  .label {
    color: #00f5ff;
    letter-spacing: 0.14em;
  }

  button {
    padding: 10px 14px;
    border: 0;
    color: #0b1020;
    background: #00f5ff;
    cursor: pointer;
  }

  #access-code {
    margin-bottom: 0;
    color: #ff2bd6;
    font-size: 28px;
    font-weight: 700;
  }
</style>

<script>
  const button = document.querySelector("#reveal-code");
  const code = document.querySelector("#access-code");

  button.addEventListener("click", () => {
    code.hidden = false;
    button.hidden = true;
  });
</script>
```

The sandbox permits scripts, forms, and popups. It does not grant same-origin or top-navigation access to the storefront page.

Theme CSS does not enter the iframe. Include all required styles inside the Custom HTML content.

<Warning>
  The `custom-html` block is forbidden on Checkout. Use it on storefront pages or merchant-created pages.
</Warning>

## Scroll an announcement bar

The Announcement Bar widget has **Static** and **Scrolling** behavior.
Scrolling mode repeats the message as a marquee and uses a static version for reduced-motion visitors.

1. Open the **Widgets** page.
2. Add an **Announcement Bar**.
3. Enter the following values.
4. Save the widget.

```text theme={"system"}
Text: NEW DROP // 20% OFF UNTIL MIDNIGHT
Link label: SHOP NOW
Link URL: /all-products
Behavior: Scrolling
Scroll speed: 30
```

The scroll speed uses seconds per cycle. The field accepts values from 8 through 40 seconds.

## Continue the theme workflow

Use [Editing with AI](/storefront/editing-with-ai) for chat-based or local AI changes.
Use the [Theme CLI](/storefront/theme-cli) to edit a ThemeDocument checkout with local files.
