Skip to content

Want to skip the docs? Check out pandamastery.com - the best way to learn Panda CSS

Governance

Find what nobody uses

Run panda analyze in each app before you remove a token, recipe, utility, pattern, keyframe, or composition.

Removing a token that an app still uses doesn't fail the build. The reference is emitted as a literal CSS value and the page quietly loses its color. panda analyze tells you, per app, what is safe to drop.

Run it

Run it in the app, not the design-system package. It scans the app's sources and reports against the theme the app resolved.

pnpm panda analyze --limit 5
analyze: scanned 151 files

Summary
tokens         1032 uses, 101 unique
recipes        29 uses, 12 unique
utilities      2299 uses, 140 unique
patterns       270 uses, 17 unique
keyframes      26 uses, 10 unique
compositions   41 uses, 9 unique

Keyframes   10/25 used (40.00%)
Keyframe    Uses   Used in
fade-in     7      1 file
fade-out    7      1 file
spin        2      globalCss, via animations.spin
Unused (15): bounce, marqueeScroll, ping, pulse, scale-in, +10 more

A use is anywhere a name is written: a source file, or a place in your config. "Used in" lists both. Config places look like globalCss, recipes.button, or staticCss.css. A name pulled in by something else reads via that thing, like a keyframe via animations.spin or a token via textStyles.body.

--scope prints one section. --limit caps the rows and unused names.

Read the output

Each section answers one question, and its last line is the list you act on.

Tokens

Which tokens carry the design, and which raw values could have been tokens. A category at 12% used is a palette you can shrink.

pnpm panda analyze --scope tokens --limit 3
Tokens
Category    Used              Top tokens                             Raw values   Files
colors      42/352 (11.93%)   fg (60), border (50), fg.subtle (47)   18           64
spacing     27/71 (38.03%)    4 (66), 2 (57), 3 (53)                 10           69
fontSizes   11/15 (73.33%)    md (14), sm (9), 3xl (8)               1            19

A token used only by a recipe, a global style, or another token counts as used. Files counts source files only.

Recipes

Which variants get picked, and whether a recipe is reached through JSX or a function call. A recipe requested by staticCss counts as used. Defining a recipe alone doesn't.

pnpm panda analyze --scope recipes --limit 3
Recipes
Recipe      Variants        Top variants                                    Used in                                Used as
button      3/13 (23.08%)   color.neutral (2), size.icon (1), size.xs (1)   5 files                                jsx 33%, fn 67%
segmented   2/6 (33.33%)    size.sm (2), tone.pill (2)                      3 files, staticCss.recipes.segmented   jsx 67%, fn 33%
callout     0/4 (0.00%)     -                                               1 file                                 jsx 0%, fn 100%

Utilities

Which properties dominate, and which configured utilities nobody uses. That's how you decide what to prune from a preset. Shorthands count as their longhand, so p and padding are one row.

pnpm panda analyze --scope utilities --limit 3
Utilities   120/410 used (29.27%)
Utility      Uses   Used in
color        197    54 files, globalCss, recipes.button
background   123    53 files
display      119    44 files
Unused (290): WebkitTextFillColor, accentColor, alignContent, +287 more

Patterns

Whether anyone reaches for hstack, or everyone wrote flex by hand. That decides whether a pattern earns its place in the generated code.

pnpm panda analyze --scope patterns --limit 3
Patterns   10/20 used (50.00%)
Pattern   Uses   Used in
box       132    25 files
stack     47     31 files
hstack    40     23 files
Unused (10): aspectRatio, bleed, cq, +7 more

Keyframes

Which keyframes are dead. A keyframe nobody references is pure CSS weight, which is why the removeUnusedKeyframes optimizer exists. A keyframe reached through an animation token or an animation style shows via that token or style.

pnpm panda analyze --scope keyframes --limit 3
Keyframes   10/25 used (40.00%)
Keyframe               Uses   Used in
fade-in                7      1 file
slide-from-left-full   2      1 file
spin                   1      via animations.spin
Unused (15): bounce, marqueeScroll, ping, +12 more

Compositions

Which text styles, layer styles, and animation styles get applied. Names carry their group, such as textStyles.body or animationStyles.slide-fade-in.

pnpm panda analyze --scope compositions --limit 3
Compositions   9/14 used (64.29%)
Composition                     Uses   Used in
textStyles.body                 19     9 files, globalCss
layerStyles.card                12     7 files
animationStyles.slide-fade-in   4      2 files
Unused (5): textStyles.4xl, textStyles.5xl, +3 more

"Unused" means no scanned file and no part of your config uses the name, directly or through something else. Usage in another app, or a value built only at runtime, doesn't show here. Run it in every consumer before you remove anything.

Decide: remove or deprecate

Zero uses across every consuming app: remove it in the next major. Any uses at all: deprecate it first, so those apps get warned while they migrate.

--unused skips the tables and prints only the names nobody uses, one per line and uncapped. That is the list you paste into the deprecation PR, or diff between two runs.

pnpm panda analyze --unused
analyze: scanned 151 files
Unused

Tokens (477)
animations.bounce
animations.ping
animations.pulse
…

Recipes (0)

Utilities (290)
WebkitTextFillColor
accentColor
…

Keyframes (15)
bounce
marqueeScroll
…

Compositions (5)
textStyles.4xl
…

With a scope it prints the names and nothing else, so it pipes:

pnpm panda analyze --scope keyframes --unused | xargs

Script it

--outfile writes the full report as JSON and keeps the terminal output. --json sends the same payload to stdout.

  • views.<scope>.unused lists removal candidates.
  • views.<scope>.items has one row per used name. uses counts source sites plus config places, files counts source files, and config lists the config places, such as ["globalCss", "via colors.brand"].
  • usages has one entry per source site, with kind, name, file, line, and column.
pnpm panda analyze --outfile analyze.json
jq '.views.tokens.unused[]' analyze.json
jq '.usages[] | select(.name == "colors.brand.300")' analyze.json
jq '.views.keyframes.items[] | select(.config)' analyze.json

Next: mark it deprecated and ship the warning before the removal.

Edit this page on GitHubView as markdown
Last updated on