Skip to content

feat(config): nested designSystem chains and smart include#3629

Merged
segunadebayo merged 7 commits into
chakra-ui:v2from
Adebesin-Cell:feat/design-system-nested-chains
Jun 29, 2026
Merged

feat(config): nested designSystem chains and smart include#3629
segunadebayo merged 7 commits into
chakra-ui:v2from
Adebesin-Cell:feat/design-system-nested-chains

Conversation

@Adebesin-Cell

@Adebesin-Cell Adebesin-Cell commented Jun 25, 2026

Copy link
Copy Markdown
Collaborator

Two related additions to how designSystem and include resolve. Both replace a hand-written node_modules path with a single named field, and they stay distinct: designSystem answers "what is my design language?", include answers "which files use it?".

Nested designSystem chains

A design system can now build on another. You set designSystem once; if that design system was itself built on a parent, Panda pulls the parent in for you. No import { parentPreset }, no second entry in presets.

// @acme/marketing's panda.lib.json records its own parent
{ "name": "@acme/marketing", "designSystem": "@acme/foundations", ... }

// your app — one line, the whole chain comes with it
export default defineConfig({
  designSystem: '@acme/marketing',
})

This builds on the single-level designSystem field (#3627), which rejected nested chains with "not supported yet". The rejection is replaced with the walk.

How it resolves. loadDesignSystemChain (packages/config) walks the parent links on disk:

  • Resolves each parent from where its child is installed, not the consumer's cwd. A transitive parent works when your app only depends on the leaf, and it survives Docker builds where only part of the workspace exists in the layer.
  • Merges presets root → leaf. Ancestors sit lowest; the leaf and your own config override them.
  • Guards cycles with a visited set keyed by resolved manifest path.

The node driver (hydrateDesignSystem) hydrates each level's pre-extracted styles under its own name, so no design system in the chain is re-extracted.

Diagnostics name the package and the fix:

  • Cycle — Design-system cycle: @a → @b → @a. A design system can't depend on itself.
  • Parent not installed — designSystem "@acme/marketing" extends "@acme/foundations", which isn't installed alongside it.

Smart include

Name a package in include and Panda resolves it for you. A library that uses Panda but isn't a design system — say a charts package built on @acme/ds — goes straight in include as a bare specifier:

export default defineConfig({
  designSystem: '@acme/ds',     // the design language — merged, pre-extracted
  include: ['@acme/charts'],    // a consumer of it — globbed and scanned
})

Panda resolves the package, globs its source so its styles land in your CSS, and appends a node_modules exclude so the package's own dependencies aren't scanned. No hand-written ./node_modules/... glob. Plain globs and local folder names pass through untouched. The CLI --include override flows through the same resolution.

If you point include at an actual design system (a package shipping panda.lib.json), Panda stops and tells you to move it to designSystem — reporting every offender at once, not one per run. The redirect is what keeps the two fields from blurring: manifest-bearing belongs in designSystem, everything else in include.

Notes

  • Smart include's extension list is a hand-maintained mirror of the engine's parseable set, since config stays binding-free. A future engine-side extension gate would make it the single source of truth.
  • Deferred: cross-package source watch — re-running cssgen on edits inside a consumed package. The package's package.json is registered as a config dep today; live source watch is a separate open item.

Tests

packages/config/__tests__/design-system.test.ts — depth-2 merge order, one importMap root per design system, resolve-against-manifest-dir, cycle + parent-not-found diagnostics, chain linked by specifier.

packages/config/__tests__/smart-include.test.ts — package vs glob vs path discrimination, exports-hidden package.json and on-disk manifest detection, manifest redirect (batched), auto-glob + nested-node_modules exclude, resolveSmartInclude (--include path), no-op when include is empty.

pnpm typecheck, pnpm test packages/config, and the compiler design-system/lifecycle suites pass.

A manifest's designSystem field links to its parent. loadDesignSystemChain
walks the chain, resolving each parent against the previous manifest's
directory, guards cycles, and merges presets root-first. The driver hydrates
each level under its own name. Diagnostics: cycle, parent-not-found.
@changeset-bot

changeset-bot Bot commented Jun 25, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 101fbc4

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 15 packages
Name Type
@pandacss/config Minor
@pandacss/compiler Minor
@pandacss/cli Minor
@pandacss/compiler-wasm Minor
@pandacss/dev Minor
@pandacss/eslint-plugin Minor
playground Patch
@pandacss/mcp Minor
@pandacss/postcss Minor
@pandacss/vite Minor
website Patch
@pandacss/compiler-shared Minor
@pandacss/preset-base Minor
@pandacss/preset-panda Minor
@pandacss/types Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
panda-docs Error Error Jun 29, 2026 3:46pm

Request Review

@vercel

vercel Bot commented Jun 25, 2026

Copy link
Copy Markdown

@Adebesin-Cell is attempting to deploy a commit to the Chakra UI Team on Vercel.

A member of the Team first needs to authorize it.

Name a package in `include` and Panda resolves it via Node resolution. A
library that uses Panda but isn't a design system auto-globs its source and
excludes the package's own node_modules. A package shipping panda.lib.json is
redirected to designSystem with a batched error. The CLI --include override
flows through the same resolution.
@Adebesin-Cell Adebesin-Cell changed the title feat(config): compose design systems with nested designSystem chains feat(config): nested designSystem chains and smart include Jun 27, 2026
The smart-include exclude merge was injecting **/*.d.ts as a base, duplicating
a default scan_exclude() already guarantees. Drop it and route the CLI
--include override through the same mergeExcludes helper so both paths merge
excludes one way.
@segunadebayo segunadebayo merged commit 18305d2 into chakra-ui:v2 Jun 29, 2026
9 of 12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants