Zudo Token Panel

Type to search...

to open search from anywhere

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.

Revision History