---
title: "Design with Claude"
description: "Claude designs from a Design System artifact on claude.ai."
canonical: design-with-claude.html
navigation-title: "Design with Claude"
---

<a id="design-with-claude"></a>

# Design with Claude

- [What you need](#what-you-need)
- [The first import](#the-first-import)
- [Designing with it](#designing-with-it)
  - [How to ask for a surface](#how-to-ask-for-a-surface)
  - [What to check in the result](#what-to-check-in-the-result)
- [A design in your project](#a-design-in-your-project)
- [In Claude Code, in a project of your own](#in-claude-code-in-a-project-of-your-own)
- [The uploaded system, kept current](#the-uploaded-system-kept-current)
  - [Which design system a sync lands in](#which-design-system-a-sync-lands-in)
  - [A new one, from the start](#a-new-one-from-the-start)
- [When it does not look right](#when-it-does-not-look-right)
- [What goes up](#what-goes-up)
- [Why the guidelines are sections](#why-the-guidelines-are-sections)
- [Why every element ships a contract](#why-every-element-ships-a-contract)
- [What the gate checks](#what-the-gate-checks)

Claude designs from a Design System artifact on claude.ai. It holds a brand
book, the tokens, the elements live in their states, the layouts to start
from, and the guidelines with their pictures. This repository builds the
files such an artifact keeps.

Import it once into a design system of your own, then design against it. The
artifact is yours, and `make design-sync` is an ordinary task.

<a id="what-you-need"></a>

## What you need

- **Docker and Make.** Every command here runs in the repository's container.
- **Claude Code**, with the Artifact tool and a claude.ai login (`/login`).
  The tool publishes files into an artifact through that login.
- **An account that can make a "Design System" artifact.** The type is one
  of the Artifact types the account lists. `Artifact list scope:types`
  in Claude Code shows it.

<a id="the-first-import"></a>

## The first import

1. **Get the repository**
   ```bash
   git clone https://github.com/TYPO3/soul-design-system.git
   cd soul-design-system
   ```

   Every command below runs from there.
2. **Build the upload**
   ```bash
   make design-sync
   ```

   It builds the files, runs the gate, says what will change, and writes
   `.design-sync/.cache/upload-plan.json`. A first run reports no link
   and no removals, because nothing is in the artifact yet.

   > [!WARNING]
   > A red gate stops it, and nothing goes up. Every fault it names is
   > invisible in review and wrong in every design after it. An undefined
   > class does nothing, a broken reference ships an unstyled preview, an
   > element that cannot render outside a browser ships no first frame.
3. <a id="upload-it"></a>

   **Upload it**
   In Claude Code, in this checkout:

   ```text
   Run the plan in .design-sync/.cache/upload-plan.json with the Artifact tool,
   step by step, in its order.
   ```

   The plan carries the decisions:

   - **With no link set, it makes a new design system from the type.** A
     fresh one starts empty, so this upload is everything in it.
   - **With one set, it reads the artifact first.** The record of what the
     system holds, the index, and the list of its files. The tool refuses a
     publish into an artifact the session has not read.
   - **Uploads first, then the files, then the index.** Every picture goes
     to the artifact's store one call at a time. `make design-index`
     writes the ids it gets back into the previews and the index.
   - **It reads the index back** at the end, because a publish result is
     not proof.

   > [!NOTE]
   > The plan is the whole instruction, order included. An agent that
   > improvises the upload forgets a renamed file the way a hand-derived
   > list does.
4. <a id="remember-where-it-landed"></a>

   **Make sure the link is set**
   The tool addresses an artifact by its link. Without it here, the next
   sync makes a second system. [Upload it](#upload-it) reported it, so
   this step confirms:

   ```bash
   make design-project
   ```

   It names the link a sync uses and which of its three sources answered.
   To set it by hand, give it the link the upload reported:

   ```bash
   make design-project ARGS=https://claude.ai/artifact/Xk2pQ9rTvB4nLm7sWc3dYe
   ```

   The link lands in `.design-sync/config.local.json`, untracked. The
   task refuses to replace a link this clone already has; \``ARGS="<url>
   --force"`\` is how you mean it.

   Nothing set, and the reported line gone? Paste this into Claude Code:

   ```text
   List my artifacts of the type "Design System" with their links, newest
   first, and run `make design-project ARGS=<the newest link>`.
   ```
5. **Record what the artifact holds**
   ```bash
   make design-synced
   ```

   Without it, `make design-status` and `make design-plan` answer from
   the previous upload.
6. **Open it**
   Open the artifact. The cover stands above the brand book, the tokens
   have their sections, and the guidelines follow them. The Components
   pane holds every element under its domain and every layout under
   "Layouts". The page compiles its own cards, `tokens.css` and the
   README's index on that first open.

<a id="designing-with-it"></a>

## Designing with it

Everything the agent needs is there before your first sentence:

- `README.md`: the conventions, the layouts to start from, and the index
  the page appends
- `guidelines/`: `SKILL.md` as the operating instruction, then the
  brand, the signet, the states and the icons, each card a picture
- the diagram and illustration prompts, with worked examples
- each element's `README.md` and `.d.ts`: its attributes, and what goes
  between its tags
- each element's `preview.html`: the element live, in the states its
  stories show, written the way a page writes it
- each layout's `preview.html`: a complete page at its design width

<a id="how-to-ask-for-a-surface"></a>

### How to ask for a surface

**Name the surface and its job, not its markup.** "A get-started page for an
extension: what it does, how it installs, the first command." The layout is
a decision already made; [Screens](screens.md) says which shape answers which job.

**Start from a layout where one fits.** It settles the shell, header,
measure and footer in one move.

**Name a component by its element.** `<sds-code code-lang="bash">`, not a
`div` with classes on it.

**Ask the agent to name a gap, not fill it.** "If the system has no answer for
this, say so instead of CSS." A gap closes here, in the component. CSS in a
design is the one part of the output that cannot travel.

<a id="what-to-check-in-the-result"></a>

### What to check in the result

| Check | Why |
| --- | --- |
| It links `components/bundle.css` and writes no CSS of its own | That stylesheet is the whole contract: tokens, then the class layer. |
| Every class is one the system defines | An invented name does nothing. An `sds-x__y` part belongs to its component. |
| No `spec-*` class anywhere | Those draw the specimen cards' chrome and stop at the card. |
| One accent, no emoji | `--accent` marks three things; status is a colour and a glyph. [Colours](colours.md) carries the rest. |
| It holds up in both modes | The tokens carry light and dark. Switch the mode and read it again. |

<a id="a-design-in-your-project"></a>

## A design in your project

Markup plus one stylesheet, so it moves as it stands. Install the package or
copy the drop-in: [Quick start](../frontend/quickstart.md), and
[Documents](../frontend/documents.md) for a page of prose.

A stylesheet of the design's own does not travel. If the design needed a
declaration the system has no name for, close that gap in the component here.

<a id="in-claude-code-in-a-project-of-your-own"></a>

## In Claude Code, in a project of your own

Claude Code designs a page from a skill it has loaded, and `SKILL.md` at
the root of this repository is one. It carries the build rules and the
recipe for a page that goes out as one file: a review, a report, a
claude.ai Artifact. With it loaded, an agent that writes such a page writes
it on this system and not on a palette of its own.

1. **Put the skill where Claude Code reads skills**
   ```bash
   git clone https://github.com/TYPO3/soul-design-system.git ~/.claude/skills/soul-design-system
   ```

   A clone, because the skill names files beside itself:
   `packages/frontend/dist/soul-inline.css` and
   `packages/frontend/dist/soul-finish.js`, both in git. A checkout you
   already have works the same through a symbolic link at that path. The
   directory under `.claude/skills/` of one project holds it for that
   project alone.
2. **Name it in the project**
   One line in the project's `CLAUDE.md`:

   ```text
   A page for the TYPO3 community, a review or a report among them,
   follows the Soul design system: load the soul-design-system skill
   before you write markup.
   ```

   Claude Code loads a skill when a task matches its description, and this
   line makes the match. Without it the agent reads the built-in design
   guidance first and reaches for the system only if it finds one.
3. **Ask for the page**
   Name the document and its job, as [How to ask for a surface](#how-to-ask-for-a-surface) says. The agent writes the elements,
   renders them with `soul-finish.js`, pastes the sheet, and publishes.
   What to check in the result is the list above, plus: the page carries no
   script, and its title stands before the sheet.

<a id="the-uploaded-system-kept-current"></a>

## The uploaded system, kept current

A token moved, a component grew. The same three stops, into the system the
link names.

1. **Build, gate, and plan what will change**
   ```bash
   make design-sync
   ```
2. **Push what moved**
   ```text
   Run the plan in .design-sync/.cache/upload-plan.json with the Artifact tool.
   ```

   It reads what the artifact holds before it writes anything, uploads
   only the pictures that changed, and publishes only the files that
   moved.
3. **Record that the artifact now holds this build**
   ```bash
   make design-synced
   ```

Look at what moved. The gate checks mechanics, not judgement. When \```` make
design-status`` lists changed sections, run ``make baseline ```\` before the
change and `make shots && make diff` after.

> [!NOTE]
> On a fresh clone the plan lists no removals and says so. The record of
> what the artifact holds is a local cache this clone never wrote. The
> plan's preflight fetches it; then run `make design-plan` again.

<a id="which-design-system-a-sync-lands-in"></a>

### Which design system a sync lands in

`make design-project` reads three places and reports which one answered.
That explains a sync that arrived somewhere unexpected.

**Only a system made from the "Design System" type is a target, and a first
import makes its own.** The plan reads the artifact's index before it writes
a byte and stops on one without the type's marker.

<a id="a-new-one-from-the-start"></a>

### A new one, from the start

```bash
make design-project ARGS=--forget
```

That is the whole reset. The link goes, and with it the record of what the
old design system held, the cached index, the plan and the uploads in
flight. The next `make design-sync` is a first import again:
the plan makes a new design system, and you set its link as in [Make sure
the link is set](#remember-where-it-landed).

All of it goes together on purpose. With the record kept and the link on a
new design system, the next plan computes removals for files that were never
there. Nothing else changes. The old artifact stays until you delete it, and
the screenshots of a visual review stay in the cache.

<a id="confval-sds-design-system"></a>

**`SDS_DESIGN_SYSTEM`**

- Type: environment variable

The design system a sync updates, as the artifact's link, and the first
source read. Then `.design-sync/config.local.json`, which \```` make
design-project ARGS=<url>`` writes. Then the committed ``config.json ```\`,
which carries none, because a clone must not inherit somebody else's. An
export outranks both files.

<a id="when-it-does-not-look-right"></a>

## When it does not look right

| What you see | What it is |
| --- | --- |
| Every preview renders in a system face | The generated fonts are not in the clone. `make verify ARGS=assets` names what to run. |
| A guideline section names a card without its picture | The page caps a section, and that picture did not fit. `make build` says which stayed out; the card itself is in Storybook. |
| A preview shows a broken picture | The preview names an upload the store had no id for when the file went up. `make design-index` fills the ids; run the plan from step 2. |
| The gallery lists a picture twice | A changed picture is a new upload, and nothing removes the old blob. Delete the old one in the page. |
| A second design system appeared beside yours | That sync ran with no link. Set it, see [Make sure the link is set](#remember-where-it-landed), then delete the duplicate. |
| The plan reports no removals | This clone has no cache of what the artifact holds; see the note above. |
| `make design-sync` stops before the plan | The gate is red. Fix what it names and run it again. |
| The tool refuses the publish | The session has not read the artifact, or a path it touches. Run the plan's preflight first, in the same session. |
| The tool refuses the publish and names a newer version | A save landed after the preflight: the page saves on its own when somebody opens it. Read the record and the index again. Unchanged, publish the same call again; changed, refresh the cache and run `make design-index` again first. |

<a id="what-goes-up"></a>

## What goes up

`make build` writes `.out/bundle/project/`, the tree a Design System
artifact keeps:

```text
.out/bundle/project/
  design-system.json    the index: the system's name, the asset groups, the last change
  tokens.json           every token, one list per family, a colour per theme
  README.md             the conventions, and the layouts to start from
  guidelines/           the rules, the brand, the signet, the states, the icons, the two prompts — one section each
  components/
      bundle.js         the elements, as one classic script — window.SDS
      bundle.css        the faces, the tokens and the class layer, as one sheet
      index.d.ts        every element's properties, read out of its source
      <Class>/          an element: README.md, <Class>.d.ts and preview.html, live from its stories
      <Name>Screen/     a layout: a whole page to start a design from
      Cover/            the system's face, above the brand book
  fonts/                the faces
  assets/<Group>/       the marks, the icons, and the fixtures the previews point at — uploads the index names
  icons/                the icon lookup and the sprites
  sync.json             the record the next sync compares against
```

<a id="why-the-guidelines-are-sections"></a>

## Why the guidelines are sections

A guideline card is a picture of its rule: the clear space around the mark,
what breaks it, what an empty state says. The page has one place
for a picture with prose beside it, and that is a Markdown section. So each
group of guideline cards is one section, with its cards photographed in.
The two prompts stand in the same row with their worked examples. The page
caps a section, so a picture that cannot fit stays out and `make build`
says which.

The Components pane holds the elements alone, each under the domain its
story stands in, and the layouts as showcase pages. The token cards the
specimens draw — colour, type, spacing — stay in Storybook: the page compiles
its own from `tokens.json`.

<a id="why-every-element-ships-a-contract"></a>

## Why every element ships a contract

An agent that only has classes writes classes. So the elements have their
own place.

`components/<Class>/` is what the elements *are*: a `.d.ts` and a
`README.md` per tag, which `scripts/lib/elements.ts` compiles out of the
element's own source. The properties Lit registers, the attribute each
answers to, and what the props interface says about it. A second copy of a
component's surface goes stale at the next property, so this one reads the
source. Beside them `preview.html` shows the element live. Its stories,
written the way a page writes them, drawn once for the first frame; the
bundle in the artifact upgrades them.

`components/bundle.js` is the bundle that registers them, built from the
same entry as the drop-in's `soul.js`. The artifact loads a classic script
before every preview, and a module cannot be one. So this is the same code
under the other format. It registers the elements as it loads and puts every
class under `window.SDS`. A project installs the module; \```` make verify
ARGS=dist`` checks that one against ``src/``. ``make build`` stops if ``make
dist ```\` has not run, because the stylesheet ships from there.

<a id="what-the-gate-checks"></a>

## What the gate checks

```bash
make verify
```

- every card declares a `@dsCard` header, and renders at its declared size
- every class in use has a definition in the stylesheets
- every local reference resolves
- every card comes from a story, and every story has its card
- every element renders outside a browser, so its first frame draws
- the committed drop-in still matches its sources
- every name the conventions header writes exists in the built stylesheet

> [!NOTE]
> The written rules that travel with the upload are `SKILL.md`. The pages
> in this section keep each rule beside its reason and its rendered
> evidence.
