OSS-first docs

These docs teach the open system first: contracts, generated surfaces, runtimes, governance, and incremental adoption. Studio shows up as the operating layer on top, not as the source of truth.

ThemeSpec Overview

ThemeSpec defines a structured, versioned source of truth for design tokens, component variants, and scoped overrides. Use it to describe how tenants or individual users should experience the design system without hand-maintaining ad-hoc theme files. Specs live in @lssm-tech/lib.contracts-spec, making them accessible to generators, docs, and runtime tooling.

field.key.label
ThemeSpec Overview
field.version.label
field.type.label
field.title.label
ThemeSpec Overview
field.description.label

ThemeSpec defines a structured, versioned source of truth for design tokens, component variants, and scoped overrides. Use it to describe how tenants or individual users should experience the design system without hand-maintaining ad-hoc theme files. Specs live in @lssm-tech/lib.contracts-spec, making them accessible to generators, docs, and runtime tooling.

field.tags.label
tech,contracts,themes
field.owners.label
field.stability.label
public

Purpose

`ThemeSpec` defines a structured, versioned source of truth for design tokens, component variants, and scoped overrides. Use it to describe how tenants or individual users should experience the design system without hand-maintaining ad-hoc theme files.

Location

Types, helper, registry, and validation: `packages/libs/contracts-spec/src/themes.ts` and `packages/libs/contracts-spec/src/themes.validation.ts`

Design tokens bridge: `packages/libs/design-system/src/theme/*`

Schema

export interface ThemeSpec {
  meta: ThemeMeta;            // ownership metadata + { key, version, extends?, scopes? }
  tokens: ThemeTokens;
  components?: ComponentVariantSpec[];
  overrides?: ThemeOverride[];
  modes?: Record<string, ThemeModeSpec>; // use keys like "light" and "dark"
}

**ThemeMeta**

`key`: fully-qualified identifier (for example `design.pastel`)

`version`: semver string such as `1.0.0`

`extends?`: optional `ThemeRef` to a base theme

`scopes?`: default scopes where the theme applies (`global`, `tenant`, `user`)

**ThemeTokens**

`colors`, `radii`, `space`, `typography`, `shadows`, `motion`

each entry is a map of `{ value, description? }`

**ComponentVariantSpec**

`component`: design-system component key (for example `Button`, `NavMain`)

`variants`: map of variant names to `{ props?, tokens? }`

**ThemeOverride**

`scope`: `'global' | 'tenant' | 'user'`

`target`: scoped identifier such as `tenant:artisanos` or `user:123`

`tokens?` / `components?`: partial overrides for that target

Authoring

import { defineTheme } from '@lssm-tech/lib.contracts-spec/themes';

export const PastelTheme = defineTheme({
  meta: {
    key: 'design.pastel',
    version: '1.0.0',
    title: 'Pastel',
    description: 'Soft pastel palette for marketing surfaces.',
    domain: 'design-system',
    owners: ['platform.design'],
    tags: ['theme', 'marketing'],
    stability: 'experimental',
    scopes: ['tenant'],
  },
  tokens: {
    colors: {
      background: { value: '#fdf2f8', format: 'hex', usage: 'semantic' },
      primary: {
        value: 'oklch(0.72 0.11 221.19)',
        format: 'oklch',
        usage: 'semantic',
      },
    },
  },
  modes: {
    dark: {
      tokens: {
        colors: {
          background: {
            value: 'oklch(0.24 0.03 255)',
            format: 'oklch',
            usage: 'semantic',
          },
          primary: {
            value: 'oklch(0.64 0.15 246)',
            format: 'oklch',
            usage: 'semantic',
          },
        },
      },
    },
  },
});

`tokens` stays the default/light-compatible token bag. Use `modes.dark.tokens` to overlay dark-mode values and preserve full CSS color strings such as OKLCH.

Use `validateThemeSpec()` or `assertThemeSpecValid()` in CI and setup flows to catch duplicate overrides, empty targets, self-referential inheritance, invalid mode keys, and missing ownership metadata before publish time.

Registry Usage

import { ThemeRegistry } from '@lssm-tech/lib.contracts-spec/themes';

const themes = new ThemeRegistry();
themes.register(PastelTheme);

const theme = themes.get('design.pastel', '1.0.0');
const tenantVariant = theme?.overrides?.find(
  (override) => override.target === 'tenant:artisanos'
);

The registry guarantees `key + version` uniqueness and exposes `list()` for discovery tooling.

Rendering

The design system consumes specs through adapters you provide:

1.

Resolve the base theme plus applicable overrides.

2.

Merge default tokens plus the selected light/dark mode token map using `ThemeTokens`.

3.

Feed the result into `mapTokensForPlatform` or the Tailwind bridge in `@lssm-tech/lib.design-system`.

function resolveTokens(
  registry: ThemeRegistry,
  ref: ThemeRef,
  ctx: { tenant?: string; user?: string }
) {
  const spec = registry.get(ref.key, ref.version);
  if (!spec) throw new Error('Theme not found');

  const tokens = deepMerge(spec.tokens, collectOverrides(spec.overrides, ctx));
  return mapTokensForPlatform(tokens);
}

CLI

contractspec create theme

Use the theme wizard to scaffold a `.theme.ts` file that already imports and calls `defineTheme(...)`.