Panthera Design System

For the past year i’ve been a co-owner of Panthera, our design system at Testlio. I’m a designer, so my contributions to the apps themselves are small, fix a component prop here or some css there. But i do write most of the design system now and yes, i use AI to do so. As always, the problem was the difference between the design system and its consumers. Components looked polished in isolation but different in the applications.

Agents

The first thing i discovered was that the examples in our design system were wrong. The setup is Chakra as the base, our components and styling on top, Storybook with docs and examples. Storybook builds a manifest of those examples and the agents read it, import lines included, exactly as written. Our internal rule was that everything is imported from our library, @panthera-v2, but every example in Storybook said @chakra-ui. The engineers knew it and imported from @panthera-v2 but the agents saw a contradicting example. A simple fix.

eslint.config.mjsjs
"files": ["**/*.stories.tsx"],
"rules": {
    "no-restricted-imports": ["error", {
        "name": "@chakra-ui/react",
        "message": "Import from '@panthera-v2'. Story imports are copied verbatim into the AI manifests."
    }]
}

Docs

The main consumers of the design system today are agents, and they take instructions quite literally. Writing the docs for humans in Storybook becomes a duplication that easily goes stale when you update a component. So i moved all of it to code, each component now describes itself in JSDoc and Storybook shows how we actually use them in our applications with real examples. I wrote a separate skill so every component reads the same way and tells the agent where its API lives, on our end or in Chakra.

CardElement.tsxtsx
export type CardElementProps = {
    /** Fills the top of the card (image, avatar…); clipped to the rounded corners via `overflow: clip`, and holds its height when the card has a fixed height. */
    header?: React.ReactNode;
    /** Required; main card label. */
    title: React.ReactNode;
    /** Secondary text below the title. */
    description?: React.ReactNode;
    /** Bottom slot for tags, badges, or status indicators. */
    accessory?: React.ReactNode;
    /** Renders the card as a different element (e.g. Chakra `Link`). Defaults to `Button`. */
    as?: React.ElementType;
    /** Link target; pass alongside `as={Link}` for anchor behaviour. */
    href?: string;
} & Omit<ButtonProps, 'title' | 'ref'>;

In Storybook a good idea would be to only expose examples you already use or plan to use. They basically describe the end product you’re building. Including counter-examples confuses the agents, they just see another example. So if it’s in the product but not in the examples, an agent can’t know if it’s right. But if it’s in the examples and not in the product, it will end up in the product sooner or later.

Conformance

On that last point, it means you need up-to-date examples. Someone still needs to decide and own what is correct and what to include in the design system. So beyond just judging it yourself you could also scan the existing applications and libraries. I built another skill for conformance, it reads what our docs and Chakra say about a component, then looks at every spot it’s used and reports anything that doesn’t match.

For example we discovered that a bunch of props typechecked but didn’t do anything, and they pulled in Chakra v2 which was 27 kB of dead weight in the bundle. Colours were written by hand instead of using semantic tokens. And some were simply examples the design system was missing. The avatar had a bg-color prop and no way to remove it, so both apps had worked around it in the same way, overriding the background by hand wherever an image was shown. But the image covers the bg-color anyway, the override did nothing, and the shadow meant for the image sat on the circle instead. So i removed the workaround and added the shape as an example. The agents copy that instead of hallucinating it or coming up with it on the spot.

Avatar.tsxdiff
- <Avatar.Root bgColor='transparent' boxShadow='xs'>
+ <Avatar.Root>
      <Avatar.Fallback>
          <User />
      </Avatar.Fallback>
-     <Avatar.Image src={src} alt={name} />
+     <Avatar.Image src={src} alt={name} boxShadow='xs' />
  </Avatar.Root>

Looping it

What i want is for the design system and the applications to match. The conformance skill tells me where they don’t, but you still have to know where the fix applies. A hand-written colour is an app issue. A custom component that is reused should be added to the system.

The decisions themselves should always go into the system, not into the applications. Once a decision is in there it becomes an example, and the rest is routine that can be automated. We update the system, the agents update the apps, and we review the result. The first time we ran it on the avatar, one change across 27 files took an app from six mismatches to none. It took us roughly a day to launch dark mode as the non-semantic colour tokens got flagged and fixed by the conformance loop, the apps just had to be updated.