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

# ClicktermDom

> API reference for the ClicktermDom class — inline clickwrap rendering and finalization.

<Note>
  Requires **SDK v2.2.0** or later. See [Installation](/dev/sdk/web/installation).
</Note>

`ClicktermDom` renders inline clickwrap checkboxes directly inside your page. Unlike [`ClicktermDialog`](/dev/sdk/web/clickterm-dialog) (which shows a modal overlay), inline mode embeds a checkbox + agreement text in a container you provide, giving you full control over the submission flow.

## Access

```javascript theme={null}
const { ClicktermDom } = window.Clickterm;
```

## Methods

### renderInline

Renders an inline clickwrap checkbox inside the specified container element.

```javascript theme={null}
ClicktermDom.renderInline(containerId, request, options?)
```

**Parameters:**

| Parameter     | Type                       | Required | Description                                 |
| ------------- | -------------------------- | -------- | ------------------------------------------- |
| `containerId` | `string`                   | Yes      | The `id` of the HTML element to render into |
| `request`     | `ClickwrapTemplateRequest` | Yes      | The request payload (see below)             |
| `options`     | `ClicktermInlineOptions`   | No       | Callbacks and style overrides (see below)   |

**ClickwrapTemplateRequest:**

| Field                  | Type     | Required | Description                                                                                                                                   |
| ---------------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `endUserId`            | `string` | Yes      | Your identifier for the end user (max 256 chars)                                                                                              |
| `clickwrapTemplateId`  | `string` | Yes      | Template ID from the [ClickTerm dashboard](https://app.clickterm.com/templates)                                                               |
| `language`             | `string` | No       | Language code (e.g., `"en"`, `"de"`). Falls back to the configured default. See [supported languages](/product/reference/supported-languages) |
| `templatePlaceholders` | `object` | No       | Structured [placeholder](/dev/guides/placeholders) object with standard fields plus `customPlaceholders`                                      |

**ClicktermInlineOptions:**

| Field       | Type                                                     | Required | Description                                                                                                                              |
| ----------- | -------------------------------------------------------- | -------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| `onChange`  | `(checked: boolean) => void`                             | No       | Called every time the checkbox state changes. Use this to enable/disable your submit button                                              |
| `onLoading` | `(info: ClicktermInlineLoadingInfo) => void`             | No       | Called before the agreement is requested. Use it to show your own loader (see [Inline lifecycle callbacks](#inline-lifecycle-callbacks)) |
| `onReady`   | `(info: ClicktermInlineReadyInfo) => void`               | No       | Called once the agreement is ready, whichever outcome applies                                                                            |
| `onError`   | `(error: Error, info: ClicktermInlineErrorInfo) => void` | No       | Called when rendering fails. `renderInline()` still rejects — this callback is for UI updates                                            |
| `style`     | `ClicktermInlineStyleOptions`                            | No       | Visual customization (see [Styling reference](#styling-reference))                                                                       |

**Returns:** `Promise<ClicktermInlineHandle>`

**Throws:** `ClickwrapError` if:

* The container element is not found in the DOM
* An inline clickwrap is already rendered in that container
* The backend response is missing inline content
* A network or server error occurs

**Example:**

```javascript theme={null}
const handle = await ClicktermDom.renderInline(
  'consent-checkbox',
  {
    endUserId: 'user-123',
    clickwrapTemplateId: 'YOUR_TEMPLATE_ID',
    language: 'en',
    templatePlaceholders: {
      fullName: 'Alice Johnson',
      customPlaceholders: { region: 'EMEA' },
    },
  },
  {
    onChange: (checked) => {
      document.getElementById('submit-btn').disabled = !checked;
    },
    onLoading: () => showSkeleton(),
    onReady: ({ outcome }) => hideSkeleton(outcome),
    onError: (error) => showRetryMessage(error),
    style: {
      checkbox: { color: '#6941C6', size: 18, borderRadius: '4px' },
      text: { fontFamily: "'Inter', sans-serif", fontSize: 14 },
    },
  }
);
```

***

## Inline lifecycle callbacks

<Note>
  Added in Web SDK `2.5.0`. All three callbacks are optional — existing integrations keep working unchanged.
</Note>

Inline rendering involves a backend round trip, so the container stays empty for a moment. These callbacks let you show your own loading state instead of an empty gap, and reserve space so the rest of your form does not jump when the agreement appears.

They fire in order: `onLoading` before the request, then either `onReady` or `onError`.

```javascript theme={null}
await ClicktermDom.renderInline('consent-checkbox', request, {
  onLoading: ({ containerId }) => {
    document.getElementById(containerId).classList.add('is-loading');
  },
  onReady: ({ containerId, outcome, clicktermSignature, handle }) => {
    document.getElementById(containerId).classList.remove('is-loading');

    if (outcome === 'EXISTING_SIGNATURE') {
      // Already agreed on a previous visit — reuse the signature, no submit needed
      attachSignature(clicktermSignature);
    }
  },
  onError: (error, { containerId }) => {
    document.getElementById(containerId).classList.remove('is-loading');
    showRetryMessage(error.message);
  },
});
```

### Callback payloads

```typescript theme={null}
type ClicktermInlineOutcome = 'RENDERED' | 'ALREADY_ACCEPTED' | 'EXISTING_SIGNATURE';

interface ClicktermInlineLoadingInfo {
  containerId: string;
}

interface ClicktermInlineReadyInfo {
  containerId: string;
  outcome: ClicktermInlineOutcome;
  /** Set only for EXISTING_SIGNATURE; null for RENDERED and ALREADY_ACCEPTED. */
  clicktermSignature: string | null;
  handle: ClicktermInlineHandle;
}

interface ClicktermInlineErrorInfo {
  containerId: string;
}
```

`onReady` fires for every successful outcome, not just a freshly rendered checkbox:

| Outcome              | Meaning                                                                  |
| -------------------- | ------------------------------------------------------------------------ |
| `RENDERED`           | The checkbox was rendered and is waiting for the user                    |
| `ALREADY_ACCEPTED`   | The user has already accepted this version — nothing to render           |
| `EXISTING_SIGNATURE` | A valid signature already exists and is returned in `clicktermSignature` |

<Warning>
  A callback that throws will not fail the render — the SDK catches it and logs to
  `console.error`. `onError` is a notification, not a replacement for handling the
  rejected promise from `renderInline()`.
</Warning>

***

### finalizeAll

Finalizes multiple inline clickwraps at once. Useful when your page has several agreements that should all be submitted together.

```javascript theme={null}
ClicktermDom.finalizeAll(containerIds?)
```

**Parameters:**

| Parameter      | Type       | Required | Description                                                                                              |
| -------------- | ---------- | -------- | -------------------------------------------------------------------------------------------------------- |
| `containerIds` | `string[]` | No       | Specific container IDs to finalize. If omitted, finalizes **all** rendered inline clickwraps on the page |

**Returns:** `Promise<Record<string, ClicktermInlineFinalizeResult>>`

The result is an object keyed by container ID:

```json theme={null}
{
  "consent-tos": {
    "status": "UNVERIFIED",
    "clicktermSignature": "eyJhbGciOi...",
    "submittedStatus": "ACCEPTED"
  },
  "consent-privacy": {
    "status": "UNVERIFIED",
    "clicktermSignature": "eyJhbGciOi...",
    "submittedStatus": "DECLINED"
  }
}
```

**Throws:** `ClickwrapError` if:

* No inline clickwraps are available to finalize
* Any of the specified container IDs do not have a rendered clickwrap

**Example:**

```javascript theme={null}
// Finalize all rendered clickwraps
const results = await ClicktermDom.finalizeAll();

// Or finalize specific ones
const results = await ClicktermDom.finalizeAll(['consent-tos', 'consent-privacy']);

// Access individual results
console.log(results['consent-tos'].clicktermSignature);
```

***

## ClicktermInlineHandle

The handle returned by `renderInline()`. Use it to check state and finalize the agreement.

| Method               | Returns                                  | Description                                                                                     |
| -------------------- | ---------------------------------------- | ----------------------------------------------------------------------------------------------- |
| `isChecked()`        | `boolean`                                | Current checkbox state (`true` = checked)                                                       |
| `finalize()`         | `Promise<ClicktermInlineFinalizeResult>` | Locks the checkbox and submits the agreement. Returns a cached result on subsequent calls       |
| `getCurrentResult()` | `ClicktermInlineFinalizeResult \| null`  | The finalize result if available, or `null` if not yet finalized                                |
| `destroy()`          | `void`                                   | Removes the inline clickwrap from the DOM and cleans up. The container can be reused after this |

### ClicktermInlineFinalizeResult

```typescript theme={null}
interface ClicktermInlineFinalizeResult {
  status: 'UNVERIFIED' | 'ACCEPTED' | 'DECLINED' | 'ALREADY_ACCEPTED' | 'ERROR';
  clicktermSignature: string | null;
  submittedStatus?: 'ACCEPTED' | 'DECLINED';
  error?: Error;
}
```

| Field                | Description                                                                                                                                                                                                |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `status`             | `UNVERIFIED` — the backend accepted it, [verify the signature](/dev/guides/verifying-signature) server-side. `ALREADY_ACCEPTED` — the user already accepted this template. `ERROR` — the submission failed |
| `clicktermSignature` | The Signature proving the agreement. Send to your backend for [verification](/dev/guides/verifying-signature). `null` if not available                                                                     |
| `submittedStatus`    | What was sent to the backend: `ACCEPTED` (checkbox checked) or `DECLINED` (unchecked). Not present for `ALREADY_ACCEPTED` handles                                                                          |
| `error`              | The error object if `status` is `ERROR`                                                                                                                                                                    |

<Info>
  If `finalize()` fails (network error, server error), the checkbox is
  automatically **unlocked** so the user can retry.
</Info>

***

## Styling reference

The inline clickwrap renders inside a closed Shadow DOM, isolating it from your page styles. Customize its appearance using the `style` option.

<Note>
  Web SDK `2.5.0` expanded this option considerably. The container, links, formatting
  marks, and every checkbox state are now covered, so matching your own design should
  no longer require injecting CSS into the Shadow DOM. Everything below is additive —
  existing `style` objects keep working.
</Note>

### ClicktermInlineStyleOptions

Any field typed `Dimension` accepts either a number (treated as pixels) or a CSS string such as `'0.5rem'`.

```typescript theme={null}
type Dimension = number | string;

interface ClicktermInlineStyleOptions {
  checkbox?: ClicktermInlineCheckboxStyleOptions;
  text?: ClicktermInlineTextStyleOptions;
  container?: ClicktermInlineContainerStyleOptions;
  locked?: ClicktermInlineLockedStyleOptions;
}
```

<Tabs>
  <Tab title="checkbox">
    ```typescript theme={null}
    interface ClicktermInlineCheckboxStyleOptions {
      /** Broad accent. Narrow state and checkmark colors inherit from it. */
      color?: string;
      size?: Dimension;
      borderRadius?: Dimension;
      borderColor?: string;
      borderWidth?: Dimension;
      background?: string;
      checkColor?: string;
      checkWidth?: number;
      boxShadow?: string;
      /** CSS margin shorthand. Negative values are supported. */
      margin?: Dimension;
      /** CSS padding shorthand around the control and its clickable area. */
      padding?: Dimension;
      /** Ignored when `margin` is set. Retained for compatibility. */
      topOffset?: Dimension;
      /** Placement against the full text block. `center` is an alias for `middle`. */
      verticalAlign?: 'top' | 'middle' | 'bottom' | 'center';

      hover?:    { borderColor?: string; background?: string };
      active?:   { borderColor?: string; background?: string };
      checked?:  { borderColor?: string; background?: string;
                   hover?: { borderColor?: string; background?: string } };
      disabled?: { borderColor?: string; background?: string; opacity?: number };
      focus?:    { outlineStyle?: string; outlineColor?: string;
                   outlineWidth?: Dimension; outlineOffset?: Dimension;
                   boxShadow?: string };
    }
    ```
  </Tab>

  <Tab title="text">
    ```typescript theme={null}
    interface ClicktermInlineTextStyleOptions {
      fontFamily?: string;
      fontSize?: Dimension;
      color?: string;
      lineHeight?: Dimension;
      fontWeight?: number | string;
      letterSpacing?: Dimension;
      textAlign?: string;
      /** Vertical spacing between blocks of agreement content. */
      contentSpacing?: Dimension;

      heading?: { color?: string; fontWeight?: number | string; fontFamily?: string };

      link?: {
        color?: string;
        textDecoration?: string;
        fontWeight?: number | string;
        textUnderlineOffset?: Dimension;
        textDecorationThickness?: Dimension;
        hover?: { color?: string; textDecoration?: string };
      };

      marks?: {
        bold?: { color?: string; fontWeight?: number | string };
        italic?: { color?: string };
        underline?: { color?: string; textDecorationColor?: string };
        strikethrough?: { color?: string; textDecorationColor?: string };
        /** Replaces all text colors authored in the ClickTerm editor. */
        textColor?: string;
        /** Replaces all highlight colors authored in the ClickTerm editor. */
        backgroundColor?: string;
        indentation?: { scale?: number };
        superscript?: { fontSize?: Dimension };
        subscript?: { fontSize?: Dimension };
      };
    }
    ```
  </Tab>

  <Tab title="container">
    ```typescript theme={null}
    interface ClicktermInlineContainerStyleOptions {
      background?: string;
      borderColor?: string;
      borderWidth?: Dimension;
      borderStyle?: string;
      borderRadius?: Dimension;
      boxShadow?: string;
      padding?: Dimension;
      /** Space between the checkbox and the text block. */
      gap?: Dimension;
    }
    ```

    To remove the default frame entirely:

    ```javascript theme={null}
    style: {
      container: { background: 'transparent', borderWidth: 0, padding: 0 },
    }
    ```
  </Tab>

  <Tab title="locked">
    Applied after `finalize()`, while the agreement is locked.

    ```typescript theme={null}
    interface ClicktermInlineLockedStyleOptions {
      background?: string;
      contentOpacity?: number;
    }
    ```
  </Tab>
</Tabs>

### CSS custom properties

Each inline instance gets a unique theme, so multiple instances on the same page can have different styles. Every `style` field is applied as a `--ct-w-inline-*` custom property on that instance's theme scope.

| CSS Variable                           | Default   | Controlled By                 |
| -------------------------------------- | --------- | ----------------------------- |
| `--ct-w-inline-checkbox-accent`        | `#7f56d9` | `style.checkbox.color`        |
| `--ct-w-inline-checkbox-size`          | `16px`    | `style.checkbox.size`         |
| `--ct-w-inline-checkbox-radius`        | `4px`     | `style.checkbox.borderRadius` |
| `--ct-w-inline-font-family`            | `inherit` | `style.text.fontFamily`       |
| `--ct-w-inline-font-size`              | `inherit` | `style.text.fontSize`         |
| `--ct-w-inline-container-background`   | `#ffffff` | `style.container.background`  |
| `--ct-w-inline-link-color`             | `#7f56d9` | `style.text.link.color`       |
| `--ct-w-inline-locked-content-opacity` | `0.9`     | `style.locked.contentOpacity` |

<Note>
  This table lists the most commonly overridden properties. The full theme defines
  roughly 77 variables following the same `--ct-w-inline-<area>-<property>` naming.
  Prefer the `style` option over targeting these variables directly — the option is the
  supported surface and survives internal changes.
</Note>

### Examples

**Default look (no customization):**

```javascript theme={null}
await ClicktermDom.renderInline('container', request);
// Default purple (#7f56d9), 16px checkbox, 4px border radius
// Text inherits font from the host page
```

**Custom color and size:**

```javascript theme={null}
await ClicktermDom.renderInline('container', request, {
  style: {
    checkbox: { color: '#2563EB', size: 20, borderRadius: '6px' },
  },
});
```

**Circular checkbox:**

```javascript theme={null}
await ClicktermDom.renderInline('container', request, {
  style: {
    checkbox: { borderRadius: '50%' },
  },
});
```

**Custom font:**

```javascript theme={null}
await ClicktermDom.renderInline('container', request, {
  style: {
    text: { fontFamily: "'Inter', 'Segoe UI', sans-serif", fontSize: 14 },
  },
});
```

**Frameless, blended into your own form:**

By default the inline clickwrap draws a white background and a border. Clear them to let it sit directly on your page.

```javascript theme={null}
await ClicktermDom.renderInline('container', request, {
  style: {
    container: { background: 'transparent', borderWidth: 0, padding: 0, gap: 10 },
    checkbox: { color: '#111827', borderRadius: '3px', verticalAlign: 'top' },
    text: { color: '#374151', fontSize: 14, lineHeight: 1.5 },
  },
});
```

**Full brand match, including states and links:**

```javascript theme={null}
await ClicktermDom.renderInline('container', request, {
  style: {
    checkbox: {
      color: '#2563EB',
      size: 18,
      hover: { borderColor: '#1D4ED8' },
      checked: { background: '#2563EB', hover: { background: '#1D4ED8' } },
      focus: { outlineStyle: 'solid', outlineWidth: 2, outlineOffset: 2 },
      disabled: { opacity: 0.4 },
    },
    text: {
      fontFamily: "'Inter', sans-serif",
      link: { color: '#2563EB', textDecoration: 'underline',
              hover: { color: '#1D4ED8' } },
      marks: { bold: { fontWeight: 600 } },
    },
  },
});
```

<Note>
  Dashboard-based [widget customization](/dev/guides/widget-customization) applies
  to **dialog mode** only. Inline mode appearance is controlled through the
  `style` option shown above.
</Note>

***

## How it works

The inline clickwrap is rendered inside a **closed Shadow DOM** attached to your container element. This provides full CSS isolation — your page styles cannot affect the widget, and widget styles cannot leak into your page.

The SDK maintains an internal **registry** of all rendered inline clickwraps. This registry prevents rendering twice into the same container, handles race conditions from concurrent `renderInline()` calls, and automatically cleans up entries when containers are removed from the DOM.

When the agreement text contains links, clicking them opens a **read-only modal** showing the full content. The user's checkbox state is not affected.

All agreement HTML from the backend is **sanitized** before injection. Dangerous tags (`script`, `iframe`, event handlers) are stripped, and only safe URL protocols (`http:`, `https:`, `mailto:`) are allowed.
