> ## Documentation Index
> Fetch the complete documentation index at: https://anypay-docs-widget-callback-intent-id.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Customization

> Customize the Trails widget appearance with CSS variables

To customise the widget, pass CSS variables through the `customCss` prop. You can pass a string of CSS declarations or a key-value object — both formats work.

```tsx theme={null}
import { Pay } from '0xtrails/widget'

<Pay
  apiKey="YOUR_API_KEY"
  customCss={`
    --trails-primary: rgb(99 102 241);
    --trails-border-radius-button: 8px;
  `}
/>

// Object form also accepted
<Pay
  apiKey="YOUR_API_KEY"
  customCss={{
    '--trails-primary': 'var(--your-brand-primary)',
    '--trails-bg-primary': 'var(--your-brand-background)',
  }}
/>
```

## Match your brand colours

Override the primary colour and its interactive states:

```tsx theme={null}
<Pay
  apiKey="YOUR_API_KEY"
  customCss={`
    --trails-primary: rgb(99 102 241);
    --trails-primary-hover: rgb(79 70 229);
    --trails-primary-disabled: rgb(209 213 219);
    --trails-primary-disabled-text: rgb(107 114 128);
    --trails-focus-ring: rgb(99 102 241);
    --trails-widget-border: 2px solid rgb(99 102 241);
  `}
/>
```

To pull in colours from your existing design system, reference your own CSS variables directly:

```tsx theme={null}
customCss={{
  '--trails-primary': 'var(--color-brand-500)',
  '--trails-primary-hover': 'var(--color-brand-600)',
  '--trails-bg-primary': 'var(--color-surface)',
  '--trails-text-primary': 'var(--color-text-default)',
}}
```

## Set the border radius style

Rounded theme:

```tsx theme={null}
customCss={`
  --trails-border-radius-widget: 32px;
  --trails-border-radius-button: 24px;
  --trails-border-radius-input: 20px;
  --trails-border-radius-dropdown: 16px;
`}
```

Square / no-radius theme:

```tsx theme={null}
customCss={`
  --trails-border-radius-widget: 0px;
  --trails-border-radius-button: 0px;
  --trails-border-radius-input: 0px;
  --trails-border-radius-dropdown: 0px;
  --trails-border-radius-container: 0px;
`}
```

## Set the font family

```tsx theme={null}
customCss={`
  --trails-font-family: "Inter", "SF Pro Display", -apple-system, sans-serif;
`}
```

Monospace:

```tsx theme={null}
customCss={`
  --trails-font-family: "Fira Code", "Monaco", monospace;
`}
```

## Configure dark mode

Use the `theme` prop for light/dark/auto. If you need to override colours for a specific theme, combine `theme` with `customCss`:

```tsx theme={null}
<Pay
  apiKey="YOUR_API_KEY"
  theme="dark"
  customCss={`
    --trails-bg-primary: rgb(15 23 42);
    --trails-bg-secondary: rgb(30 41 59);
    --trails-text-primary: rgb(248 250 252);
    --trails-border-primary: rgb(51 65 85);
    --trails-primary: rgb(99 102 241);
  `}
/>
```

## Advanced: glassmorphism

```tsx theme={null}
customCss={`
  --trails-bg-primary: rgba(255, 255, 255, 0.1);
  --trails-bg-secondary: rgba(255, 255, 255, 0.05);
  --trails-border-primary: rgba(255, 255, 255, 0.2);
  --trails-shadow: 0 8px 32px 0 rgba(31, 38, 135, 0.37);
`}
```

## Advanced: disable default CSS

To apply a fully custom stylesheet without Trails' defaults interfering:

```tsx theme={null}
<Pay
  apiKey="YOUR_API_KEY"
  disableCss={true}
/>
```

## Global CSS overrides

Variables cascade as normal CSS custom properties. You can set them globally or scope them to a container:

```css theme={null}
/* Apply to all widget instances */
:root {
  --trails-primary: #6366f1;
}

/* Scope to a specific container */
.my-payment-section {
  --trails-primary: #10b981;
}
```

***

## Widget copy overrides

Pass `labels` to replace the widget's default copy. Every key is optional, and a blank string falls back to the built-in text.

```tsx theme={null}
import { Fund } from '0xtrails/widget'

<Fund
  apiKey="YOUR_API_KEY"
  labels={{
    confirmButton: 'Donate',
    formTitle: 'Donate',
    methodsTitle: 'Choose a donation method',
    tokensTitle: 'Donate with any token',
  }}
/>
```

| Key             | What it renames                                                                                                   | Default                                                                                                                    |
| --------------- | ----------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------- |
| `confirmButton` | The action verb on the primary form button, and on the modal trigger button                                       | `Fund`, `Pay`, `Swap`, `Deposit`, `Withdraw` by mode; the modal trigger shows `Earn` in earn mode                          |
| `formTitle`     | The amount form screen, and the crypto-transfer screens that continue it, so the header stays put across the flow | `Fund`, `Pay`, `Swap`, `Withdraw`, `Earn` by mode; `Deposit` on the transfer screens                                       |
| `methodsTitle`  | The payment method picker                                                                                         | `Choose a funding method`; `Choose a payment method` in pay and earn modes; `Payment methods` on the pay flow's own picker |
| `tokensTitle`   | The screen where the user picks what to pay *with*                                                                | Varies by mode — `Fund with any token`, `Select From Token`, and others                                                    |

Overrides apply only to the **configured mode's** screens. With `mode="fund"`, a `confirmButton` override does not follow the user onto the swap
sub-screen reached from the fund header.

<Note>
  Destination token pickers keep their own titles, and transient button states — "Enter an amount", "Getting quote...", "Connect your wallet",
  "Processing...", "Insufficient Balance" — are never overridden.
</Note>

Where a modal trigger button is rendered, an explicit `buttonText` prop takes precedence over `labels.confirmButton`.

***

## Consent checkbox

Pass `consentCheckbox` to render a consent line directly above the primary form button.

```tsx theme={null}
<Fund
  apiKey="YOUR_API_KEY"
  consentCheckbox={{
    label:
      `I've read and agree to the ` +
      `<a href="https://example.org/terms">Terms &amp; Conditions</a>`,
    requiredMessage: 'Please accept the Terms & Conditions to continue.',
  }}
/>
```

| Key               | Required | Purpose                                                                       |
| ----------------- | -------- | ----------------------------------------------------------------------------- |
| `label`           | Yes      | The consent line, as inline HTML                                              |
| `requiredMessage` | No       | Standing notice shown under the checkbox after the button is pressed unticked |

Setting the prop makes the tick **mandatory** — there is no separate required flag. While the box is unticked the button is not disabled by the
checkbox; pressing it flags the row instead of submitting, so a ready quote still reads as ready. A failed press moves focus to the checkbox. With
`requiredMessage` set, the notice stands under the row until the box is ticked and is announced to assistive technology. Without it, a failed press
shakes and tints the row, and the tint clears itself (both are skipped under `prefers-reduced-motion`).

The row is not a `<label>`: clicking a link inside the consent line follows the link and does not tick the box.

The checkbox renders on the confirm screens of every mode, and is scoped to the configured mode exactly as `labels` is. Consent is per session and
never persisted — a reload asks again, which is what makes the tick evidence of assent rather than a remembered preference.

### Label sanitising

`label` is inline HTML, sanitised against a fixed allowlist:

* Allowed tags: `a`, `b`, `strong`, `i`, `em`, `span`, `br`. Every other tag is unwrapped — the tag goes, its text stays — except `script`,
  `style`, `template`, `iframe`, `object`, `embed` and `noscript`, which are removed with their contents. Every attribute is stripped.
* Surviving anchors are rewritten with `target="_blank" rel="noopener noreferrer"`, so following the link never costs the user a filled-in quote.
* An anchor keeps its `href` only if that href resolves to `http:`, `https:` or `mailto:` — which includes relative (`/terms`) and protocol-relative
  (`//host/path`) forms. Anything else, such as `javascript:` or `data:`, is unwrapped to plain text so nothing inert is left looking clickable.

<Warning>
  A blank or whitespace-only `label` counts as no checkbox at all — and therefore no gate. The SDK logs a console warning when this happens. Bind
  this to a CMS or i18n value with care.

  Without `DOMParser` (server rendering), the label degrades to plain text: all markup is stripped, including allowed tags and links.

  If you interpolate an untrusted value into an `href`, validate it yourself. The allowlist is not that check.
</Warning>

***

## CSS variable reference

### Typography

| Variable               | Default                                                                                                                             |
| ---------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| `--trails-font-family` | `ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Noto Sans", sans-serif` |

### Border radius

| Variable                              | Applies to            | Default |
| ------------------------------------- | --------------------- | ------- |
| `--trails-border-radius-widget`       | Main widget container | `32px`  |
| `--trails-border-radius-button`       | Primary buttons       | `24px`  |
| `--trails-border-radius-input`        | Input fields          | `24px`  |
| `--trails-border-radius-dropdown`     | Dropdown menus        | `12px`  |
| `--trails-border-radius-container`    | Generic containers    | `8px`   |
| `--trails-border-radius-list`         | Token lists           | `8px`   |
| `--trails-border-radius-list-button`  | List item buttons     | `12px`  |
| `--trails-border-radius-large-button` | Large action buttons  | `16px`  |

### Widget structure

| Variable                 | Default                                                         |
| ------------------------ | --------------------------------------------------------------- |
| `--trails-widget-border` | `none`                                                          |
| `--trails-shadow`        | `0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1)` |

### Primary colours

| Variable                         | Default            |
| -------------------------------- | ------------------ |
| `--trails-primary`               | `rgb(59 130 246)`  |
| `--trails-primary-hover`         | `rgb(37 99 235)`   |
| `--trails-primary-disabled`      | `rgb(209 213 219)` |
| `--trails-primary-disabled-text` | `rgb(107 114 128)` |

### Background colours

| Variable                | Default            |
| ----------------------- | ------------------ |
| `--trails-bg-primary`   | `rgb(255 255 255)` |
| `--trails-bg-secondary` | `rgb(249 250 251)` |
| `--trails-bg-tertiary`  | `rgb(243 244 246)` |
| `--trails-bg-card`      | `rgb(255 255 255)` |
| `--trails-bg-overlay`   | `rgb(255 255 255)` |

### Text colours

| Variable                  | Default            |
| ------------------------- | ------------------ |
| `--trails-text-primary`   | `rgb(17 24 39)`    |
| `--trails-text-secondary` | `rgb(75 85 99)`    |
| `--trails-text-tertiary`  | `rgb(107 114 128)` |
| `--trails-text-muted`     | `rgb(156 163 175)` |
| `--trails-text-inverse`   | `rgb(255 255 255)` |

### Border colours

| Variable                    | Default            |
| --------------------------- | ------------------ |
| `--trails-border-primary`   | `rgb(229 231 235)` |
| `--trails-border-secondary` | `rgb(209 213 219)` |
| `--trails-border-tertiary`  | `rgb(243 244 246)` |

### Interactive states

| Variable              | Default            |
| --------------------- | ------------------ |
| `--trails-hover-bg`   | `rgb(243 244 246)` |
| `--trails-hover-text` | `rgb(17 24 39)`    |
| `--trails-focus-ring` | `rgb(59 130 246)`  |

### Status colours

| Variable                  | Default            |
| ------------------------- | ------------------ |
| `--trails-success-bg`     | `rgb(240 253 244)` |
| `--trails-success-text`   | `rgb(22 163 74)`   |
| `--trails-success-border` | `rgb(187 247 208)` |
| `--trails-warning-bg`     | `rgb(255 251 235)` |
| `--trails-warning-text`   | `rgb(217 119 6)`   |
| `--trails-warning-border` | `rgb(253 230 138)` |
| `--trails-error-bg`       | `rgb(254 242 242)` |
| `--trails-error-text`     | `rgb(220 38 38)`   |
| `--trails-error-border`   | `rgb(254 202 202)` |

### Input fields

| Variable                      | Default            |
| ----------------------------- | ------------------ |
| `--trails-input-bg`           | `rgb(255 255 255)` |
| `--trails-input-border`       | `rgb(209 213 219)` |
| `--trails-input-text`         | `rgb(17 24 39)`    |
| `--trails-input-placeholder`  | `rgb(156 163 175)` |
| `--trails-input-focus-border` | `rgb(59 130 246)`  |
| `--trails-input-focus-ring`   | `rgb(59 130 246)`  |

### Dropdowns

| Variable                          | Default            |
| --------------------------------- | ------------------ |
| `--trails-dropdown-bg`            | `rgb(255 255 255)` |
| `--trails-dropdown-border`        | `rgb(229 231 235)` |
| `--trails-dropdown-text`          | `rgb(17 24 39)`    |
| `--trails-dropdown-hover-bg`      | `rgb(249 250 251)` |
| `--trails-dropdown-selected-bg`   | `rgb(243 244 246)` |
| `--trails-dropdown-selected-text` | `rgb(17 24 39)`    |
| `--trails-dropdown-focus-border`  | `rgb(59 130 246)`  |

### Lists

| Variable                 | Default            |
| ------------------------ | ------------------ |
| `--trails-list-bg`       | `rgb(255 255 255)` |
| `--trails-list-border`   | `rgb(229 231 235)` |
| `--trails-list-hover-bg` | `rgb(249 250 251)` |
