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

# Appearance and Events

> Customize the Payment Element card field and react to its lifecycle with supported browser events.

Use this page to style the hosted field and react to state changes without owning raw card input.

## Appearance example

```js theme={null}
const card = elements.create("card", {
  theme: "default",
  style: {
    base: {
      color: "#111827",
      fontFamily: "Inter, system-ui, sans-serif",
      fontSize: "16px",
      borderRadius: "8px",
      backgroundColor: "#ffffff",
    },
    focus: {
      borderColor: "#2563eb",
    },
    invalid: {
      color: "#dc2626",
    },
    placeholder: {
      color: "#9ca3af",
    },
  },
});

card.mount("#card-element");
```

Supported themes are `default`, `night`, and `flat`. Use the `style` object for brand-specific colors and typography instead of placing raw card inputs in your own DOM.

## Supported event pattern

```js theme={null}
let cardState = {
  valid: false,
  brand: null,
  errors: {},
};

card.on("ready", () => {
  messageEl.textContent = "";
});

card.on("change", (event) => {
  cardState = event;

  submitButton.disabled = !event.valid;
  brandEl.textContent = event.brand ? event.brand.toUpperCase() : "";
  messageEl.textContent = Object.values(event.errors ?? {})[0] ?? "";
});

card.on("error", (event) => {
  messageEl.textContent = event.message ?? "Unable to initialize the card field.";
});
```

Call `card.off("change", handler)` when your component unmounts if your framework keeps the same card instance alive across renders.

## Event payloads

`ready` fires when the hosted iframe is loaded. It does not mean the card details are valid.

`change` fires whenever card input changes:

```json theme={null}
{
  "valid": false,
  "brand": "visa",
  "errors": {
    "number": "Card number is invalid"
  }
}
```

When the card brand is unknown, `brand` is `null`. When the current state is valid, `errors` is empty and `valid` is `true`.

`error` fires when the hosted field fails to load or receives a payment failure outside an active tokenization request:

```json theme={null}
{
  "code": "mount_timeout_no_load",
  "message": "Unable to initialize the card field."
}
```

Known error codes include `card_error`, `iframe_load_failed`, `mount_timeout_no_load`, and `mount_timeout_no_handshake`.

## Invalid states

Use `change.valid` as the only submit gate. Do not infer validity from the card brand alone.

```js theme={null}
card.on("change", ({ valid, brand, errors }) => {
  submitButton.disabled = !valid;
  cardBrandEl.textContent = brand ?? "";

  const firstError = Object.values(errors ?? {})[0];
  errorEl.textContent = firstError ?? "";
});
```

The `errors` object is keyed by the hosted field that needs attention. The exact keys are controlled by the hosted card field, so render the messages rather than hardcoding every possible key.

Fix: confirm the script version, environment, and `publicKey`. Event handlers should update UI state only; final payment state still comes from your backend and webhooks.
