Every figmascope export starts with a .zip containing seven artifacts. CONTEXT.md is the one that gets read first — by design. It's not a README. It's not generated docs. It's a compiler-style contract between the design and the agent that will turn it into code.
This post walks through what's in it, why it's structured the way it is, and what goes wrong when you skip it.
What a contract looks like vs. what docs look like
Documentation describes what exists. A contract specifies what must be true. That distinction matters when your reader is an LLM that will cheerfully invent spacing values, flatten layout hierarchies, or hardcode hex colors the moment it doesn't have a better option.
CONTEXT.md opens with a target declaration:
# CONTEXT.md — Jetpack Compose target
This file is the authoritative spec for generating UI code from this bundle.
Read it before reading any other file.
The "Jetpack Compose target" isn't decoration. The agent uses it to pick composable primitives, handle dp vs sp units, know that Modifier.padding() is the right abstraction for spacing, and understand that Box with Alignment is how you handle overlay layouts. A React/Tailwind target generates a different CONTEXT.md with different primitives in the constraints.
The "read this first" instruction is also intentional. Agents process context sequentially. If a token rule appears after the agent has already started reasoning about a component, the constraint arrives too late. Front-loading the contract means the rules are active from the first token of generation.
The strict constraints section
The most load-bearing part of CONTEXT.md is the constraints block:
## Strict constraints (must follow)
- Never hardcode dp values if a token exists within ±2dp
- Never flatten layout hierarchy — preserve every stack/overlay/absolute node
- Use stringRef values from strings.json, never inline literal text
- Treat missing fields as absent, not zero
- Do not invent component names not present in components/inventory.json
Each constraint exists because we've seen agents violate it. The ±2dp token rule is the clearest example. Without it, an agent encountering a gap: 14 on a stack node will write Arrangement.spacedBy(14.dp). With the rule, it knows to check whether spacing.16 or spacing.12 is within 2dp and use the token instead. That's a different output — one that stays coherent when the token value changes, one that doesn't.
The hierarchy rule exists because Compose is a layout tree. When an agent flattens a nested stack into a flat list of positioned elements, it destroys the responsive behavior implied by the original structure. The IR preserves every level of nesting for a reason — see Per-Screen IR — Stack, Overlay, Absolute, Leaf for how that nesting encodes layout intent.
The constraint isn't "use the tokens if you feel like it." It's "never hardcode if a token exists." That's a prohibition, not a suggestion. LLMs respond differently to prohibitions than suggestions.
The stringRef rule matters for i18n. If an agent inlines "Speed Test" instead of referencing the resource key, you've created a localization hole. The constraint points the agent to strings.json explicitly.
How the agent reads CONTEXT.md sequentially
LLM context windows process tokens left-to-right. The structure of CONTEXT.md exploits this. The order is:
- Target declaration (sets the entire generation frame)
- Strict constraints (prohibitions that apply to every decision)
- Bundle map (what files exist and what each one contains)
- Token usage rules (how to resolve spacing, color, radius, typography)
- Scope notes (honest gaps — what this bundle does and doesn't cover)
The bundle map section tells the agent which files to read and in what order:
## Bundle contents
- tokens.json — design tokens (spacing, radius, color, typography)
- screens/*.json — per-screen IR in stack/overlay/absolute/leaf format
- components/inventory.json — component identity list
- strings.json — i18n resource keys
- _meta.json — generation metadata and warnings
Without this map, an agent might not know components/inventory.json exists, or might treat _meta.json as the main spec. The explicit enumeration guides the reading order.
Scope notes — what v0.4 honestly doesn't cover
The scope notes section is where figmascope documents its own limitations. This is deliberate and non-negotiable. An agent that doesn't know a spec is incomplete will confidently fill the gaps with invention. That's worse than knowing the gap exists.
## Scope notes (v0.4)
- Gradient fills are not supported. Nodes with gradient fills emit a warning
in _meta.json under warnings. Treat as a solid fill approximation or TODO.
- Typography tokens require Figma Variables to be populated. If tokensSource
is "inferred-from-frequency" or "none", typography coverage may be partial.
- This bundle covers UI structure only. Navigation, state management,
and network calls are outside scope.
- Component instances are identified by componentId. Full component
source props are not included — the inventory gives identity, not implementation.
The gradient warning is particularly important. Figma supports complex gradient fills that have no direct Compose equivalent without custom drawing. Rather than silently dropping gradients or generating broken code, figmascope emits a background-gradient-not-supported:<name> warning in _meta.json and notes it here so the agent knows to treat affected nodes as a known gap rather than a bug in its own output.
The typography note connects to the tokensSource field in _meta.json. When a Figma file has no Variables, figmascope infers tokens from frequency — common values become tokens, rare values don't. The scope note tells the agent this inference is imperfect and not to assume full typography coverage. See tokens.json Explained for how the inference fallback works.
WRONG / RIGHT examples
Let's make this concrete. Given a stack node with gap: 16 and a token file containing spacing.16: 16:
Without CONTEXT.md constraints (WRONG):
Column(
modifier = Modifier.padding(horizontal = 24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
With CONTEXT.md constraints (RIGHT):
Column(
modifier = Modifier.padding(horizontal = Spacing.spacing24),
verticalArrangement = Arrangement.spacedBy(Spacing.spacing16)
) {
The RIGHT output is token-referenced. When the design system changes spacing.16 from 16dp to 14dp, the code stays correct without a codebase search-and-replace.
Another example — string handling. Given a leaf node with text: "Speed Test" and stringRef: "speed.test":
WRONG:
Text(text = "Speed Test")
RIGHT:
Text(text = stringResource(R.string.speed_test))
The constraint points to strings.json, the key is speed.test, and the agent knows how to map dot-notation to Android resource IDs. That's only possible if the agent read the constraint before generating the component.
Why not just prompt the agent yourself?
You can write these instructions manually in your prompt. figmascope externalizes them into a file because:
- The constraints are derived from the actual bundle. The ±2dp rule only makes sense if
tokens.jsonexists. CONTEXT.md is generated knowing what tokens are present. - Per-project scope notes change per file. A Figma file with full Variables coverage gets a different typography note than one with inferred tokens.
- The file is in the zip. You can reference it as context without copy-pasting. Cursor, Claude Code, and similar tools all support @-file references.
- It's reproducible. Every export from the same Figma file generates the same CONTEXT.md. Your team's agent reads the same contract every time.
The goal isn't to prompt-engineer around the agent's defaults. It's to ship a spec that makes the defaults irrelevant.
Structure compared to alternatives
Figma's Dev Mode outputs measurements, variables, and code snippets. It doesn't produce a spec. The agent using Dev Mode output has to infer rules from examples — which means it will infer wrong rules for edge cases.
Locofy and similar tools generate code directly. They have no CONTEXT.md equivalent because they don't expect an agent to read a spec — they expect to be the agent. That works when the tool's code generation exactly matches your stack. It doesn't work when you have project-specific naming conventions, a custom component library, or a design system with tokens the tool doesn't know about.
CONTEXT.md is a machine-readable design brief. It's the thing that existed as a Confluence page or Notion doc in every pre-LLM design handoff, just structured for an audience that actually follows rules.
What to do with it
When you open a figmascope export in Cursor or Claude Code:
- Add
@CONTEXT.mdto your context before any prompt about the design. - Reference the screen JSON you're working on:
@screens/home.json. - If you're touching tokens, add
@tokens.json. - If you're working with strings, add
@strings.json.
The CONTEXT.md constraints apply across the whole session once loaded. You don't re-add it per prompt — you add it once at the start and let it frame every subsequent generation.
The other artifacts in the bundle are covered in the rest of this series. Start with tokens.json Explained for how the token system handles files with and without Figma Variables, or Per-Screen IR for how Figma layouts map to stack/overlay/absolute/leaf nodes. Ready to try it? Paste your Figma URL on figmascope.dev and export your first bundle.