# Add CLItrail to Material for MkDocs

Where the CLItrail tag goes in Material for MkDocs, in its own idiom, with the consent, CSP and client-side routing details and how to check it works.

Written for Material for MkDocs 9.7 (the built-in MkDocs themes take the same override)

## Where the tag goes

Edit `overrides/main.html (with custom_dir: overrides in mkdocs.yml)`. Put the tag on every page that shows your install command; site-wide is best, after your existing analytics tags.

```html
{% extends "base.html" %}

{% block extrahead %}
  {{ super() }}
  <script defer src="https://YOUR_SERVICE/browser.js" data-website="YOUR_WEBSITE_ID"></script>
{% endblock %}
```

Turn on the override

```
theme:
  name: material
  custom_dir: overrides
```

## Only after consent

Material's built-in cookie consent lists CLItrail as its own cookie; the script loads the tag only when the visitor accepted it. Leave the override's tag out when you gate on consent.

```js
// Material reloads the page after a consent choice, so one check per page load is enough.
(() => {
  const consent = __md_get('__consent');
  if (!consent || !consent.clitrail) return;
  const tag = document.createElement('script');
  tag.src = 'https://YOUR_SERVICE/browser.js';
  tag.dataset.website = 'YOUR_WEBSITE_ID';
  document.head.append(tag);
})();
```

Ask for consent and load the script

```
extra:
  consent:
    # …your title, description and actions
    cookies:
      # …your existing cookies
      clitrail: CLItrail
extra_javascript:
  - javascripts/clitrail-consent.js
```

## Client-side routing

With `navigation.instant`, pages change without a reload. Material's `document$` emits on every page, including the first (which the tag already recorded).

```js
// Records a visit for each instant-loaded page.
(() => {
  let recorded;
  const recordVisit = () => {
    recorded ??= new URL(performance.getEntriesByType('navigation')[0]?.name ?? location.href).pathname;
    const sdk = window.InstallAttribution;
    if (!sdk?.ready || location.pathname === recorded) return;
    recorded = location.pathname;
    sdk.ready = sdk.init({ service: 'https://YOUR_SERVICE', project: 'YOUR_WEBSITE_ID' });
  };
  document$.subscribe(recordVisit);
})();
```

## Content Security Policy

The build is static files, so the policy lives in your host's header settings (for example `_headers` on Cloudflare Pages or Netlify, or `vercel.json`): add `https://YOUR_SERVICE` to `script-src` and `connect-src`, and `blob:` to `worker-src` (only Safari before 26 needs that one).

```
script-src 'self' https://YOUR_SERVICE;
connect-src 'self' https://YOUR_SERVICE;
worker-src 'self' blob:;
```

## Verify it works

1. Run `mkdocs serve` (http://127.0.0.1:8000) or deploy.
2. Open a page with the tag in Chrome or Firefox (accept analytics first if you gate CLItrail on consent), open the developer console and run `await InstallAttribution.ready`. It resolves to `{ ok: true, urlId, hasAnalyticsContext, expiresAt }`.
3. In the CLItrail dashboard, open Visits & identities: the visit is listed.
4. Anything else names the cause: `disabled` (no consent yet, or `data-enabled="false"`), `opfs_unavailable` (the page is not served over https or from localhost), `Error` (the service refused the visit: add the page's origin, including a development origin such as `http://localhost:3000`, under Settings → Additional domains). If `InstallAttribution` is undefined, the tag did not load: check the Network tab and your Content-Security-Policy.

Then run your installer on the same computer and check **Install events**, or run the hook with [`--doctor`](https://clitrail.com/docs/doctor).

## Notes

`extra_javascript` cannot set `data-*` attributes, so the tag goes in the `extrahead` block of a theme override.

Material for MkDocs documentation: [squidfunk.github.io/mkdocs-material/customization](https://squidfunk.github.io/mkdocs-material/customization/)

Using other install paths or destinations? The [setup generator](https://clitrail.com/docs/setup-generator) puts this snippet together with your hooks and destination checklist.
