Secondary cluster: configure or disable
Two-state semantics: omit the secondary color tab (hidden) or add a TabConfig with id 'color-secondary' (host-supplied).
The panel supports an optional second Color tab keyed to the reserved id
'color-secondary'. Whether to show it, hide it, or never wire it at all is
a matter of whether that tab appears in PanelConfig.tabs. This recipe covers
the two cases with concrete snippets.
For the underlying contract, see Color cluster reference (multi-cluster section).
The two states
| tabs contains id: 'color-secondary'? | Render | Apply / clear / load |
|---|---|---|
| No (tab omitted) | Secondary section hidden. | No write. The persist envelope’s secondary slice stays inert. |
| Yes (tab present with colorExtras) | Secondary palette + semantic table render below the primary. | Same write semantics as the primary, scoped to the secondary palette and CSS-var names. |
💡 Tip
Previously there was a null opt-out. Older docs described a three-state
secondaryColorCluster field where null would hard-skip the secondary load
path. In the current tabs-based config, simply omitting the
'color-secondary' tab achieves the same result — no secondary state is
hydrated.
Case 1 — secondary hidden (default)
Just omit the 'color-secondary' tab from your tabs array. This is the
recommended starting point for every new host.
// src/lib/my-panel-config.ts
import type { PanelConfig } from '@takazudo/zdtp';
import { colorTab } from './my-tabs';
export const myPanelConfig: PanelConfig = {
storagePrefix: 'myapp-design-token-panel',
consoleNamespace: 'myapp',
modalClassPrefix: 'myapp-design-token-panel-modal',
schemaId: 'myapp-design-tokens/v1',
exportFilenameBase: 'myapp-design-tokens',
tabs: [colorTab],
// No 'color-secondary' tab — secondary section is hidden.
};
Case 2 — host-supplied secondary cluster
Add a second TabConfig with id: 'color-secondary' to tabs. Use a
different cssVar prefix for palette and semantic items so the writes do not
collide with the primary Color tab.
// src/lib/my-tabs.ts
import type { PanelConfig, ColorScheme } from '@takazudo/zdtp';
type TabConfig = PanelConfig['tabs'][number];
const accentDark: ColorScheme = {
background: 0,
foreground: 7,
cursor: 7,
selectionBg: 8,
selectionFg: 0,
palette: [
'#202124', '#ff7597', '#7cd992', '#f5d97a',
'#7ab0f5', '#c79df0', '#7fdacf', '#dde2ec',
'#3a3d44', '#ff7597', '#7cd992', '#f5d97a',
'#7ab0f5', '#c79df0', '#7fdacf', '#bfc6d4',
],
shikiTheme: 'github-dark',
};
export const colorSecondaryTab: TabConfig = {
id: 'color-secondary',
label: 'Accent',
colorExtras: {
id: 'myapp-accent',
label: 'Accent surface',
baseRoles: {
background: '--myapp-accent-bg',
foreground: '--myapp-accent-fg',
},
baseDefaults: {
background: 0,
foreground: 7,
},
defaultShikiTheme: 'github-dark',
colorSchemes: {
'Accent Dark': accentDark,
},
panelSettings: {
colorScheme: 'Accent Dark',
colorMode: false,
},
},
tiers: [
{
id: 'palette',
label: 'Palette',
items: [
{ id: 'myapp-accent-p0', cssVar: '--myapp-accent-p0', label: 'P0', default: '#202124', type: { kind: 'color' } },
{ id: 'myapp-accent-p1', cssVar: '--myapp-accent-p1', label: 'P1', default: '#ff7597', type: { kind: 'color' } },
{ id: 'myapp-accent-p2', cssVar: '--myapp-accent-p2', label: 'P2', default: '#7cd992', type: { kind: 'color' } },
{ id: 'myapp-accent-p3', cssVar: '--myapp-accent-p3', label: 'P3', default: '#f5d97a', type: { kind: 'color' } },
{ id: 'myapp-accent-p4', cssVar: '--myapp-accent-p4', label: 'P4', default: '#7ab0f5', type: { kind: 'color' } },
{ id: 'myapp-accent-p5', cssVar: '--myapp-accent-p5', label: 'P5', default: '#c79df0', type: { kind: 'color' } },
{ id: 'myapp-accent-p6', cssVar: '--myapp-accent-p6', label: 'P6', default: '#7fdacf', type: { kind: 'color' } },
{ id: 'myapp-accent-p7', cssVar: '--myapp-accent-p7', label: 'P7', default: '#dde2ec', type: { kind: 'color' } },
{ id: 'myapp-accent-p8', cssVar: '--myapp-accent-p8', label: 'P8', default: '#3a3d44', type: { kind: 'color' } },
{ id: 'myapp-accent-p9', cssVar: '--myapp-accent-p9', label: 'P9', default: '#ff7597', type: { kind: 'color' } },
{ id: 'myapp-accent-p10', cssVar: '--myapp-accent-p10', label: 'P10', default: '#7cd992', type: { kind: 'color' } },
{ id: 'myapp-accent-p11', cssVar: '--myapp-accent-p11', label: 'P11', default: '#f5d97a', type: { kind: 'color' } },
{ id: 'myapp-accent-p12', cssVar: '--myapp-accent-p12', label: 'P12', default: '#7ab0f5', type: { kind: 'color' } },
{ id: 'myapp-accent-p13', cssVar: '--myapp-accent-p13', label: 'P13', default: '#c79df0', type: { kind: 'color' } },
{ id: 'myapp-accent-p14', cssVar: '--myapp-accent-p14', label: 'P14', default: '#7fdacf', type: { kind: 'color' } },
{ id: 'myapp-accent-p15', cssVar: '--myapp-accent-p15', label: 'P15', default: '#bfc6d4', type: { kind: 'color' } },
],
},
{
id: 'semantic',
label: 'Semantic',
referencesTier: 'palette',
items: [
{ id: 'surface', cssVar: '--myapp-accent-surface', label: 'Surface', default: 'myapp-accent-p0', type: { kind: 'color' } },
{ id: 'onSurface', cssVar: '--myapp-accent-on-surface', label: 'On surface', default: 'myapp-accent-p7', type: { kind: 'color' } },
],
},
],
};
Then include both tabs in PanelConfig.tabs:
// src/lib/my-panel-config.ts
import type { PanelConfig } from '@takazudo/zdtp';
import { colorTab, colorSecondaryTab } from './my-tabs';
export const myPanelConfig: PanelConfig = {
storagePrefix: 'myapp-design-token-panel',
consoleNamespace: 'myapp',
modalClassPrefix: 'myapp-design-token-panel-modal',
schemaId: 'myapp-design-tokens/v1',
exportFilenameBase: 'myapp-design-tokens',
tabs: [colorTab, colorSecondaryTab],
};
Case 3 — light/dark pairing on the secondary
Same shape as Case 2, but with panelSettings.colorMode light/dark pairing
enabled. The panel watches document.documentElement[data-theme] and
swaps schemes accordingly on init.
export const colorSecondaryTab: TabConfig = {
id: 'color-secondary',
label: 'Accent',
colorExtras: {
// ...same id, baseRoles, baseDefaults, defaultShikiTheme...
colorSchemes: {
'Accent Light': accentLight,
'Accent Dark': accentDark,
},
panelSettings: {
colorScheme: 'Accent Dark',
colorMode: {
defaultMode: 'dark',
lightScheme: 'Accent Light',
darkScheme: 'Accent Dark',
},
},
},
tiers: [ /* ...same tiers as Case 2... */ ],
};
ℹ️ Info
colorMode is independent on the primary and the secondary. You can set it
on one and leave the other as false; the panel resolves them separately.
Related
- Color cluster reference — primary cluster shape and apply-time write order; the secondary follows the same rules.
- Custom color cluster — wiring the primary cluster from scratch.
- <code>configurePanel</code> reference —
PanelConfig.tabsand the reserved'color-secondary'id.