Load the panel only for you (the site owner)
Use owner-autoload to load the design-token panel only in your own browser, keeping general visitors completely free of panel JS.
You are deploying a public site and want the design-token panel available to yourself as the developer — without loading any panel JavaScript for general visitors. The owner-autoload feature is the answer:
General visitors: zero bundle cost. The panel JS is never fetched. No JavaScript, no CSS, no DOM element.
You (the owner): the panel bundle loads eagerly on every page visit, mounts CLOSED, and arms the Alt+click element-path inspector so you can click any element to copy its selector — even without opening the panel UI.
Opt-in is explicit and per-browser: run window.<ns>.enableAutoload() once in DevTools on your personal machine. The flag is stored in localStorage so it survives page reloads, soft navigations, and browser restarts — and only lives in your browser, not on the server or in the build.
For the storage-key derivation and the full gate contract, see PORTABLE-CONTRACT.md §6.2.
How it works
The host adapter probes localStorage on every page load and fetches the panel bundle only when at least one owner signal is set. When none is — the common case for a first-time visitor on a public site — no bundle is fetched at all. The owner-autoload flag is one of those signals.
| Signal | Meaning |
|---|---|
${storagePrefix}:visible | Panel was open on the last visit |
${storagePrefix}-open | The same open state, under the panel's own open-state key |
${storagePrefix}-state (v1) and ${storagePrefix}-state-v2 … -vN | User has saved token overrides |
${storagePrefix}:autoload | Owner-autoload flag — you are in owner mode |
${storagePrefix}-elpath-enabled | Element-path inspector is enabled |
${storagePrefix}-domtweaker-enabled | DOM Tweaker is enabled (probed only when the host configures domTweaker) |
The -state family is matched exactly — ${storagePrefix}-state plus ${storagePrefix}-state-v<N> for any version — so a sibling instance whose prefix happens to start with yours cannot trip your gate. Each matching key is also content-checked: a key holding an empty envelope ({}, [], null, or an empty string) is not a signal. Note that a panel reset does not leave one behind — it removes the -state keys outright — so in practice this guard only matters for envelopes written by hand or by another tool.
enableAutoload() writes the :autoload flag and arms the element-path inspector. disableAutoload() clears all four owner-mode keys and unmounts the panel.
Opt in (run this once in DevTools)
Open DevTools on any page of your site and run:
window.myapp.enableAutoload();
// Replace 'myapp' with your PanelConfig.consoleNamespace value.From that point on, every page load in that browser will:
Fetch the panel bundle eagerly (same path as when you have saved overrides).
Mount the Preact shell CLOSED — the panel UI stays out of sight.
Arm the Alt+click element-path inspector — hover over any element and click to copy a detailed selector block to the clipboard.
Leave the
window.myapp.{show,hide,toggle}DesignPanel()console helpers — and the fixed-namezdtp.{show,hide,toggle}()global — available, just as always.
Opt out
To stop loading the panel in your browser:
window.myapp.disableAutoload();This clears the :autoload flag, the :visible flag, the element-path enabled key, and the open-state key, then unmounts the Preact shell. The next page load will be panel-free again.
Auto-remember: opening the panel arms autoload
Opening the panel by any means — showDesignPanel(), toggleDesignPanel(), the fixed-name zdtp.show() / zdtp.toggle() global, or the panel header's close/re-open button — automatically sets the :autoload flag. You do not need to call enableAutoload() explicitly if you have already opened the panel through any of these paths. The flag is set on first open and persists across page loads.
The value written by this path is 'auto', not '1' — the next section explains why that distinction matters. A host that does not want this behaviour at all can switch it off with autoRememberOnOpen: false.
The stored value carries its provenance
:autoload is not a bare on/off bit. Its value records how the browser came to be in owner mode:
| Stored value | Written by | Meaning |
|---|---|---|
'1' | enableAutoload() | Explicit — the owner deliberately armed owner mode |
'auto' | any action that opens the panel (auto-remember) | Inferred — something merely opened the panel once |
absent, '0', anything else | disableAutoload() removes the key | Not in owner mode |
Both values arm zdtp's own eager load. The package's shouldAutoload() check matches '1' and 'auto' alike, so owner behaviour is exactly what the sections above describe no matter which path you took — nothing about the panel changes because of the split.
The split exists for a downstream host writing its own lazy-load probe. Testing === '1' narrows the eager-load population to owners who typed enableAutoload() in DevTools, and stops eagerly fetching the bundle for a visitor who clicked a panel button once, months ago.
Auto-remember never downgrades an explicit '1' to 'auto': if you armed owner mode deliberately and later open the panel, the value stays '1'.
Note
disableAutoload() removes the key outright, whichever value it held. There is no "downgrade to 'auto'" path — opting out is always a full opt-out.
The footgun: visible open-triggers become public opt-ins
Because any open action arms :autoload, a UI trigger that is visible to general visitors becomes a de-facto opt-in for anyone who clicks it. Every visitor who happens to click it gets :autoload = 'auto' in their browser, and the panel bundle loads for them on every subsequent page visit.
There are two ways out, and which one applies depends on whether you can hide the trigger:
You control the trigger — omit or hide the open-panel affordance from the production build, or gate it behind authentication, and use
enableAutoload()from DevTools as your deliberate opt-in.The trigger must stay visible — a public docs site that deliberately shows a panel button to every reader cannot hide it. Turn auto-remember off instead, with
autoRememberOnOpen: false(next section).
Keep a public panel button: autoRememberOnOpen: false
PanelConfig.autoRememberOnOpen controls whether opening the panel writes :autoload = 'auto'. It defaults to true — the historical behaviour described above. Set it to false and opening the panel never persists owner mode:
import { configurePanel } from '@takazudo/zdtp';
configurePanel({
storagePrefix: 'myapp-design-token-panel',
consoleNamespace: 'myapp',
// Public site: the panel button is visible to every reader, so a click
// must NOT arm owner-mode autoload for them. enableAutoload() stays
// available as the explicit, owner-only opt-in.
autoRememberOnOpen: false,
tabs: [
// ...your token manifest
],
});On an Astro host you never call configurePanel yourself — put the field on the same PanelConfig object you already hand to <DesignTokenPanelHost>:
---
// src/layouts/Layout.astro
// myPanelConfig carries autoRememberOnOpen: false, exactly as above.
import DesignTokenPanelHost from '@takazudo/zdtp/astro/DesignTokenPanelHost.astro';
import { myPanelConfig } from '../lib/my-panel-config';
---
<DesignTokenPanelHost config={myPanelConfig} />With that in place:
A reader clicks the button, uses the panel, and closes the tab. Nothing is written to
:autoload, so their next page load fetches no bundle.You run
window.myapp.enableAutoload()once in DevTools. That writes'1'and is not subject to this setting — owner mode still works exactly as documented above.
Tip
autoRememberOnOpen: false suppresses the :autoload write and nothing else. The panel's other persisted state is untouched, and two of those keys are eager-load signals in their own right: saving a token override writes the -state envelope, and leaving the panel open when you navigate away leaves :visible set. Either one loads the bundle on that reader's next visit. That is intended — both mean the reader is actually using the panel — but it is why autoRememberOnOpen: false is not the same as "never load for anyone but the owner". Closing the panel clears :visible again.
Worked example — a safe owner-only loader
Below is the pattern this documentation site itself uses (#422): a tiny always-present <script> block that checks localStorage and dynamically imports the panel only when the owner flag is already set. The panel is never imported on first visit, and the flag is set only via the DevTools console call above.
<!-- Your page layout — runs on every page, before </body> -->
<script>
// Tiny stub — the full panel bundle is imported only when :autoload is set.
// Replace storagePrefix with your PanelConfig.storagePrefix value.
const storagePrefix = 'myapp-design-token-panel';
const autoloadKey = `${storagePrefix}:autoload`;
const autoload = window.localStorage?.getItem(autoloadKey);
// '1' = the owner ran enableAutoload() deliberately.
// 'auto' = the panel was merely opened once (auto-remember).
// Drop the second test to serve eager loads to explicit owners only.
if (autoload === '1' || autoload === 'auto') {
// Owner mode: load the panel module. The module's own bootstrap logic
// reads the gate signals, finds the :autoload value, and re-applies the
// full owner-mode state (CLOSED mount + element-path armed).
void import('@takazudo/zdtp');
}
</script>Testing both values mirrors zdtp's own gate. Testing === '1' alone is the narrower policy: it keeps the bundle off the page for everyone who reached owner mode by clicking rather than by typing.
For an Astro site using the <DesignTokenPanelHost> component, this stub is unnecessary — the host adapter already handles the lazy-load gate including the :autoload signal. This pattern applies to non-Astro hosts or custom wiring where you want explicit control over the import trigger.
Tip
If you are on an Astro site with <DesignTokenPanelHost>, just runwindow.myapp.enableAutoload() once in DevTools. The adapter handles the rest on every subsequent page load automatically.
Legacy caveat: existing browsers already hold '1'
The provenance value is new. Browsers that auto-remembered before this release stored a bare '1', and no migration can recover a provenance that was never written down. They read as explicit owners forever.
Warning
Do not expect an === '1' probe to shed your existing auto-remembered population on day one. The discrimination applies only to writes made from this version onward; the legacy '1' values age out as those browsers clear storage or run disableAutoload().
The same holds for autoRememberOnOpen: false — it stops future writes. A visitor who already holds '1' or 'auto' keeps it until the key is cleared.
Summary
| Step | Action |
|---|---|
| Opt in | window.myapp.enableAutoload() in DevTools (once) — writes '1' |
| Opt out | window.myapp.disableAutoload() in DevTools — removes the key |
| General visitors | Zero cost — no bundle, no CSS, no DOM |
| You (after opt-in) | Bundle loads eagerly, panel mounts CLOSED, Alt+click armed |
| First open | Writes 'auto' so future pages re-load the panel |
| Public site with a visible panel button | autoRememberOnOpen: false — opening never writes :autoload |
Related
PORTABLE-CONTRACT.md §6.2 — lazy-load gate specification and the full
enableAutoload/disableAutoloadAPI contract, including the:autoloadprovenance values.Storage-key reference —
storagePrefixand the colon-separated${storagePrefix}:autoloadkey derivation.Lazy color presets — another pattern that keeps general-visitor page loads lean.