Context
ds-context
A model misses the design system when the context is wrong: nothing, or the entire Storybook. ds-context turns a local folder into a short pack and shows what each packing strategy costs in tokens.
Why
An agent drifts off the system for two different reasons. Sometimes it receives no spec. Sometimes it receives every token and every example at once. In the first case it invents a Button. In the second it drowns.
ds-context sits in front of the model. It builds the pack you can hand to Claude or Codex and then measure with ds-eval.
Input
The MVP reads a folder. It does not crawl Storybook or call the Figma API. A Figma file arrives as markdown, for example from figma-to-design-md. The Acme fixture is five files:
fixtures/acme/
tokens.css
components.json
patterns.md
anti-patterns.md
examples.md
dist/
design-system.md
components.md
patterns.md
anti-patterns.md
examples.md
manifest.jsontokens.css is the scale a later repair checks against.
:root {
--color-accent: #8B5CF6;
--color-text: #212223;
--color-surface: #ffffff;
--space-2: 8px;
--space-3: 12px;
--space-4: 16px;
--radius-2: 8px;
--font-size-body: 14px;
}components.json holds the name, props, example, and what to avoid. The example is copied into the pack only for the full strategy.
[
{
"name": "Button",
"props": ["variant", "size", "children"],
"example": "<Button variant=\"primary\">Save</Button>",
"avoid": "A raw <button> with an inline background."
},
{
"name": "Input",
"props": ["label", "hint", "error"],
"example": "<Input label=\"Email\" />",
"avoid": "An unlabeled <input>."
},
{
"name": "Dialog",
"props": ["open", "title", "onClose"],
"example": "<Dialog open title=\"Delete account\" />",
"avoid": "A <div className=\"modal\"> with a hand-rolled overlay."
},
{
"name": "Select",
"props": ["label", "options", "value"],
"example": "<Select label=\"Country\" options={countries} />",
"avoid": "A native <select> when the system Select exists."
}
]Three markdown files set the patterns, the bans, and short screen examples.
# Patterns
## Destructive confirm
Ask for confirmation before a delete. Use Dialog, name the object, and make the confirm button the danger variant.
## Empty state
Show one next action. Do not invent a second illustration style.
## Settings
Group fields by task. Use Input with a label. Actions sit in the page footer, not inside the form card.# Anti-patterns
- Do not hardcode #8B5CF6. Use var(--color-accent).
- Do not build a modal from a positioned div.
- Do not add a button with no accessible name.
- Do not pick 13px of padding. Use the space scale.# Examples
A settings page uses Input for the name and email, Select for the country, and Button for save.
A delete flow opens Dialog. The confirm action is variant="danger". The close control has an accessible name.
Empty lists use the empty-state pattern: one sentence and one Button.Run
python3 ds-context/ds_context.py build ds-context/fixtures/acmeThe command writes the pack to ds-context/fixtures/acme/dist. Offline. No keys and no model call. On this fixture it prints:
source ds-context/fixtures/acme
out ds-context/fixtures/acme/dist
pack files
design-system.md ~ 292 tokens
components.md ~ 160 tokens
patterns.md ~ 87 tokens
anti-patterns.md ~ 54 tokens
examples.md ~ 72 tokens
strategies (estimate, ~4 chars/token, manifest excluded)
full ~ 665
compact ~ 375
components ~ 180Output
design-system.md is the file you can put in context whole. The full strategy inlines patterns, anti-patterns, and examples. The separate files in dist/ repeat the source markdown.
# Design system
Components: Button, Input, Dialog, Select.
## Tokens
| Token | Value |
| --- | --- |
| `--color-accent` | #8B5CF6 |
| `--color-text` | #212223 |
| `--color-surface` | #ffffff |
| `--space-2` | 8px |
| `--space-3` | 12px |
| `--space-4` | 16px |
| `--radius-2` | 8px |
| `--font-size-body` | 14px |
# Patterns
## Destructive confirm
Ask for confirmation before a delete. Use Dialog, name the object, and make the confirm button the danger variant.
## Empty state
Show one next action. Do not invent a second illustration style.
## Settings
Group fields by task. Use Input with a label. Actions sit in the page footer, not inside the form card.
# Anti-patterns
- Do not hardcode #8B5CF6. Use var(--color-accent).
- Do not build a modal from a positioned div.
- Do not add a button with no accessible name.
- Do not pick 13px of padding. Use the space scale.
# Examples
A settings page uses Input for the name and email, Select for the country, and Button for save.
A delete flow opens Dialog. The confirm action is variant="danger". The close control has an accessible name.
Empty lists use the empty-state pattern: one sentence and one Button.components.md is the API. The example and the Avoid line come from the JSON.
# Component API
### Button
Props: `variant`, `size`, `children`
Example:
```jsx
<Button variant="primary">Save</Button>
```
Avoid: A raw <button> with an inline background.
### Input
Props: `label`, `hint`, `error`
Example:
```jsx
<Input label="Email" />
```
Avoid: An unlabeled <input>.
### Dialog
Props: `open`, `title`, `onClose`
Example:
```jsx
<Dialog open title="Delete account" />
```
Avoid: A <div className="modal"> with a hand-rolled overlay.
### Select
Props: `label`, `options`, `value`
Example:
```jsx
<Select label="Country" options={countries} />
```
Avoid: A native <select> when the system Select exists.Strategies
The size estimate is about 4 characters per token. It is not the Claude tokenizer. The manifest is excluded from the total. On the Acme fixture:
| Strategy | Estimate | Contents |
|---|---|---|
| full | ~665 | Tokens, API with examples, patterns, anti-patterns, and screen examples |
| compact | ~375 | Tokens, API without examples, and anti-patterns. No separate examples.md |
| components | ~180 | Overview with tokens and the API only. No patterns or examples |
Full pack by file:
| File | Estimate |
|---|---|
design-system.md | ~292 |
components.md | ~160 |
patterns.md | ~87 |
anti-patterns.md | ~54 |
examples.md | ~72 |
Manifest
The manifest is for the next step in the loop: pick a strategy under a token budget and pass only those files to the model. Component and token names are listed so ui-repair and ds-eval can check against the same source.
{
"strategy": "full",
"source": "ds-context/fixtures/acme",
"components": [
"Button",
"Input",
"Dialog",
"Select"
],
"tokens": [
"--color-accent",
"--color-text",
"--color-surface",
"--space-2",
"--space-3",
"--space-4",
"--radius-2",
"--font-size-body"
],
"files": {
"design-system.md": 292,
"components.md": 160,
"patterns.md": 87,
"anti-patterns.md": 54,
"examples.md": 72
},
"tokens_total": 665
}Where it sits
Figma / local spec
→ ds-context
→ model
→ ui-repair
→ ds-eval
→ prompt-regressds-context prepares the context. ds-eval measures whether it helped. prompt-regress shows whether a new prompt broke cases that used to pass.
Limit
The tool does not open Storybook or Figma. It does not choose a slice of the pack for one screen: the strategies are global. The token estimate is for comparing packs, not for an API bill.