This document defines the first prototype path for importing a Figma-like structured export into a FrameLab draft.
It is a planning milestone only. It does not define a Figma plugin, a production importer, or a full roundtrip workflow.
The importer plan is intentionally limited to the current supported subset described in SUPPORTED.md.
Accept a small structured JSON export and turn it into a draft that is close enough for AI or a human to finish inside the current FrameLab subset.
The prototype importer should solve one narrow problem:
- convert explicit structure and token hints into a draftable FrameLab-shaped representation
It should not try to solve:
- behavior inference
- accessibility policy inference
- full Figma fidelity
- automatic final
.flcorrectness without refinement
The importer should produce a draft, not a final reviewed compiler input.
That draft may be either:
- a partial
.fldraft - or an intermediate JSON draft ready for AI-assisted conversion into
.fl
For this milestone, the recommended target is an intermediate JSON draft because it keeps the prototype narrow and explicit about gaps.
The prototype importer should accept one small JSON shape:
{
"schema": "framelab-draft-import",
"version": 1,
"tokens": [
{ "path": "surface.raised", "value": "#18181c" },
{ "path": "space.gap.md", "value": "16px" },
{ "path": "type.lg", "value": "22px" }
],
"component": {
"name": "ProductCard",
"variants": [
{ "axis": "tone", "values": ["default", "featured"], "default": "default" }
],
"slots": [
{ "name": "thumbnail" },
{ "name": "actions" }
],
"layout": {
"kind": "surface",
"props": {
"fillToken": "surface.raised",
"radiusToken": "radius.card",
"paddingToken": "space.inset.md"
},
"children": [
{
"kind": "stack",
"props": {
"direction": "vertical",
"gapToken": "space.gap.md"
},
"children": [
{ "kind": "slot", "name": "thumbnail" },
{
"kind": "stack",
"props": {
"direction": "vertical",
"gapToken": "space.gap.sm"
},
"children": [
{
"kind": "text",
"contentHint": "category-text",
"props": {
"sizeToken": "type.xs",
"textColorToken": "color.text.muted"
}
},
{
"kind": "text",
"contentHint": "title-text",
"props": {
"sizeToken": "type.lg",
"textColorToken": "color.text.primary"
}
},
{
"kind": "text",
"contentHint": "price-text",
"props": {
"sizeToken": "type.sm",
"textColorToken": "color.text.primary"
}
}
]
},
{ "kind": "slot", "name": "actions" }
]
}
]
},
"semanticHints": {
"surfaceRole": "article",
"primaryLabelField": "title-text"
}
}
}schemaversioncomponent.namecomponent.layout.kindcomponent.layout.children
tokenscomponent.variantscomponent.slotslayout.propstext.contentHintcomponent.semanticHints
Input:
component.name
Importer behavior:
- preserve the explicit component name
- do not invent a different name
- reject or flag names that are empty or obviously malformed
What still needs refinement later:
- exact prop declarations inferred from text content hints
- final naming cleanup if source data uses design-system-only naming
Input:
component.slotslayout.childrennodes withkind: "slot"
Importer behavior:
- map each explicit slot name into a slot hint in the draft
- preserve slot order from the layout tree
- do not infer requiredness
- do not infer fallback content
What still needs refinement later:
- whether the slot should be optional or required
- whether any slot needs typed content hints beyond the prototype
Input:
component.variants
Importer behavior:
- map explicit axis names and values into draft variant axes
- preserve explicit default when supplied
- do not invent variant axes from styling differences alone
What still needs refinement later:
- whether the variant names fit FrameLab authoring conventions
- whether body-level variant overrides should be added in
.fl
Input:
- recursive
layout - supported
kindvalues:surface,stack,text,slot
Importer behavior:
surfacemaps to a draft surface root or nested surfacestackmaps to stack structure with explicit direction and gap hints when presenttextmaps to a text node with content and token hintsslotmaps to a named slot position in the tree
Prototype limits:
- only explicit nested structure should be imported
- no pixel-based auto-inference of stacks from arbitrary coordinates
- no flattening into final FrameLab props beyond obvious supported mappings
What still needs refinement later:
- exact text weights or role decisions when the export does not specify them clearly
- any unsupported structures that need simplification before valid
.fl
Input:
tokens- token-valued fields in
layout.propsor text props such asfillToken,gapToken,sizeToken
Importer behavior:
- preserve explicit token paths as token hints
- include literal token values in the draft input when they are exported
- do not rename tokens automatically
- do not assume all external token paths are valid final FrameLab token policy
What still needs refinement later:
- token normalization if export naming does not match desired FrameLab naming
- removal or replacement of token references that do not fit the current subset cleanly
Input:
component.semanticHints
Supported prototype fields:
surfaceRoleprimaryLabelField- optional text-level role hints if explicitly present in the export
Importer behavior:
- preserve explicit semantic hints as non-authoritative guidance
- do not convert them directly into final intent policy
- do not infer accessibility behavior from visual structure alone
What still needs refinement later:
- accessible block content
- intent kind and identifier
- final label and description policy
The prototype importer may infer only small structural conveniences:
- if the root node is
surface, treat it as the component root hint - if a
stackexplicitly declares direction, preserve it directly - if a
textnode has acontentHint, treat that as a likely prop hint - if a slot name appears both in
component.slotsand in the tree, preserve the tree position and keep the explicit slot list
These are safe structural inferences because they stay inside the current supported subset and do not invent behavior.
After import, AI or a human still needs to refine:
- exact
.flsyntax - prop declarations
- intent blocks
- accessible blocks
- final token naming cleanup
- unsupported or ambiguous structures
- any compiler validation failures
The importer should be judged by whether it gives refinement a strong starting point, not by whether it solves authoring end to end.
Structured export input:
{
"schema": "framelab-draft-import",
"version": 1,
"tokens": [
{ "path": "surface.raised", "value": "#18181c" },
{ "path": "radius.card", "value": "18px" },
{ "path": "space.inset.md", "value": "20px 22px" },
{ "path": "space.gap.md", "value": "16px" },
{ "path": "space.gap.sm", "value": "10px" },
{ "path": "type.xs", "value": "12px" },
{ "path": "type.sm", "value": "14px" },
{ "path": "type.lg", "value": "22px" },
{ "path": "color.text.primary", "value": "#f0ede8" },
{ "path": "color.text.muted", "value": "#a09b94" }
],
"component": {
"name": "ProductCard",
"slots": [
{ "name": "thumbnail" },
{ "name": "actions" }
],
"layout": {
"kind": "surface",
"props": {
"fillToken": "surface.raised",
"radiusToken": "radius.card",
"paddingToken": "space.inset.md"
},
"children": [
{
"kind": "stack",
"props": {
"direction": "vertical",
"gapToken": "space.gap.md"
},
"children": [
{ "kind": "slot", "name": "thumbnail" },
{
"kind": "stack",
"props": {
"direction": "vertical",
"gapToken": "space.gap.sm"
},
"children": [
{
"kind": "text",
"contentHint": "category-text",
"props": {
"sizeToken": "type.xs",
"textColorToken": "color.text.muted"
}
},
{
"kind": "text",
"contentHint": "title-text",
"props": {
"sizeToken": "type.lg",
"textColorToken": "color.text.primary"
}
},
{
"kind": "text",
"contentHint": "price-text",
"props": {
"sizeToken": "type.sm",
"textColorToken": "color.text.primary"
}
}
]
},
{ "kind": "slot", "name": "actions" }
]
}
]
},
"semanticHints": {
"surfaceRole": "article",
"primaryLabelField": "title-text"
}
}
}Importer output intent:
- component name becomes
ProductCard - slot hints become
thumbnailandactions - root structure becomes
surface -> stack -> text/slot - token references remain explicit token hints
- semantic hints remain draft metadata, not final behavior
What still happens after import:
- AI or a human adds prop declarations for
title-text,category-text, andprice-text - AI or a human adds
intent navigate: "product-detail"if that behavior is desired - AI or a human adds the accessible label strategy
- the compiler validates the final
.flsource and repair may still be needed
The prototype importer is successful if it can:
- accept a small explicit structured export
- preserve component name, variants, slots, layout hints, token hints, and semantic hints
- produce a draft shape that is easy for AI or a human to finish
- stay honest about unsupported gaps
The prototype importer is not required to:
- generate final clean
.flevery time - infer behavior or semantics from visual appearance
- roundtrip arbitrary Figma files
- capture full design fidelity