defineChakraConfig

The function that writes your Panda config, so your stylesheet and our compiled runtime name the same classes

Source: packages/panda-preset/src/config.ts

defineChakraConfig() is the one part of theming here that is not Panda’s API. It replaces Panda’s defineConfig — it returns the same object, so it is still a plain panda.config.ts default export:

panda.config.ts
import { defineChakraConfig } from "@chakra-ui-solid/panda-preset";
 
export default defineChakraConfig({
  include: ["./node_modules/chakra-ui-solid/dist/**/*.jsx", "./src/**/*.{ts,tsx}"],
  outdir: "styled-system-app",
});

It exists because of one failure mode that has no other guard. Panda’s usual model generates the runtime and the stylesheet together from a single config; here the runtime was compiled into @chakra-ui-solid/styled-system before you installed it, and only the stylesheet is yours. Several config keys decide names on both sides of that split. Set differently on your side alone, they produce a sheet whose rules our class names never match: every component renders unstyled, and nothing anywhere raises an error.

The function owns the merge so that none of that is constructable. You never spread it, so there is no shallow-spread hazard; the keys that must agree are a type error to pass; and the keys that would otherwise replace ours are merged for you.

What it sets

Eight keys, and none of them are yours to choose:

KeyValueWhy
ejecttrueDrops @pandacss/preset-panda, whose palette disagrees with Chakra’s about colors.gray.*. You keep every utility — the preset declares @pandacss/preset-base itself
jsxFramework"solid"Extracts style props from any capitalized JSX component, which is why <Box p="4"> in your source produces a rule at all
jsxFactory"chakra"A different knob, and the one with the silent failure: chakra.button is lowercase, so without it Panda’s isUpperCase fallback declines the tag and every <chakra.button bg="…"> you write emits nothing
importMapour packagesTells your extractor the styled-system API lives in our published package rather than in a directory you generated
hashfalseOur runtime computes p_4; a hashed sheet has no such rule
separator"_"p_4 would become p=4 in your sheet only
prefixunsetclassName renames every rule; cssVar renames every variable, and the var(--sizes-*) strings inside SimpleGrid and Bleed were compiled before you installed them
presets[chakraSolidPreset]Chakra’s tokens and all 75 recipes. Yours are appended after it

Writing any of the eight is a type error whose message names the key:

panda.config.ts
export default defineChakraConfig({
  // ✗ `hash` is set by defineChakraConfig, because our published runtime already
  //   committed to these names
  hash: true,
  include: [...],
});

preflight is set to true as a default, not a lock — pass false or a scope and yours wins.

What you can pass

Three classes, and the type tells them apart for you.

KeysWhat happens
Lockedthe eight aboveA type error. If one reaches Panda anyway — an untyped panda.config.js, or a spread of the return value — your Panda run prints [chakra-ui-solid] restoring … and puts our value back
Mergedpresets, plugins, theme, staticCssAdded to ours rather than replacing it. Your presets and plugins come after ours; theme accepts only extend; staticCss is merged for you
Yoursinclude, outdir, globalCss, preflight, conditions, cssVarRoot, hooks, the restPassed straight through

include is the one required key. Panda’s default (src/**/*.{js,jsx,ts,tsx}) misses the dist glob, and that glob is not optional: it is the channel through which values our components name, and your source never writes, reach your extractor.

theme takes extend only

extend deep-merges; the key beside it replaces. A bare theme would drop Chakra’s whole token table and all 19 recipes, so theme here accepts extend and nothing else:

panda.config.ts
export default defineChakraConfig({
  theme: { extend: { tokens: { colors: { brand: { value: "#5b8" } } } } },
  include: [...],
});

theme: { tokens: … } is a type error naming the fix.

staticCss is merged for you

Write it the way Panda documents it — the merge is this function’s job:

panda.config.ts
export default defineChakraConfig({
  staticCss: { css: [{ properties: { color: ["red.500"] } }] },
  include: [...],
});

That one needs the merge more than theme does, and neither spelling avoids the need. A config’s staticCss.css replaces a preset’s whole array, and staticCss: { extend: { css } } replaces it too. So the preset’s own seven entries are written into your config alongside yours, rather than left behind in the preset for Panda to drop — and if you write an extend block, it is folded into that same union rather than passed through.

extend does not extend staticCss, whatever you have read. Panda’s Extend page lists staticCss among the parts the keyword extends. Measured on one merge against three of its list-mates: conditions, globalCss and utilities each keep the preset’s entry and yours; staticCss keeps only yours. The three are objects keyed by name, so a clash is per name — staticCss.css is an array, and the whole array loses. Nothing warns you, which is why this function does the union itself.

Those seven are not decoration: <Flex inline>, <Wrap> and every StackSeparator flip a value at runtime, Panda’s usage scan cannot see a value that appears in no source file, and the pre-generated rule is the only thing that makes the prop do anything. Lose them and those props go quietly inert.

Not mergeConfigs. It is not exported from @pandacss/dev, so a consumer who installed only the peer dependency cannot reach it, and it returns any — discarding the Config types this function exists to supply.

An extra preset

Ours stays first, so yours is later and wins on a conflict:

panda.config.ts
export default defineChakraConfig({
  presets: [myPreset],
  include: [...],
});

responsive

Recipe variants are pre-generated at one condition by default. Pass responsive to also generate them at every breakpoint, which is what <Button size={{ base: "sm", md: "lg" }}> needs:

panda.config.ts
export default defineChakraConfig({
  responsive: { button: ["size"] },
  include: [...],
});

Three grains, so nobody pays for conditions they do not use:

GrainMeaning
{ button: ["size"] }One variant key on one recipe
["button", "heading"]Every variant key on those recipes
trueEvery variant key on all 75

Each expands to the staticCss block Panda already understands, so the opt-in asks nothing new of Panda:

defineChakraConfig({ responsive: { button: ["size"] }, include: [] }).staticCss.recipes;
// => { button: [{ size: ["*"], responsive: true }] }

If you also write staticCss.recipes yourself, the two are merged per recipe rather than one replacing the other — so responsive and a custom staticCss can both be set.

Off by default because the stylesheet already carries 488 recipe-variant values, and turning responsive on for all of them multiplies that by six — base plus five breakpoints.

The recipe registry

Three more exports, read off the preset rather than typed out, so a Chakra release that adds a recipe is covered by the version bump alone:

import { recipeKeys, slotRecipeKeys, variantKeysFor } from "@chakra-ui-solid/panda-preset";
 
recipeKeys.length; // => 19 — button, input, heading, …
slotRecipeKeys.length; // => 56
variantKeysFor().find((entry) => entry.recipe === "button");
// => { recipe: "button", keys: ["size", "variant"] }

variantKeysFor() is what expands the two coarser responsive grains: ["button"] means every variant key on Button, and nothing else knows which those are.