Conversation
|
Size stats
|
|
Accessibility report ℹ️ You can run this locally by executing |
84eb045 to
4d3d95c
Compare
There was a problem hiding this comment.
Pull request overview
This PR updates the Mistica documentation to improve LLM-driven Figma (via MCP) implementations by adding a dedicated translation/rules doc, reinforcing key “no escape hatches” principles, and improving layout/text guidance.
Changes:
- Add new
doc/figma-mcp.mdwith mandatory translation rules for implementing from Figma MCP output (incl. Code Connect guidance and asset handling). - Consolidate “Critical rules” in
doc/llms.mdand makedoc/patterns.mdpoint to it as the single source of truth; add an explicit “re-apply rules during debugging” rule. - Enhance layout/text docs: add an
Inlinenesting pattern todoc/layout.mdand add centering guidance todoc/components.md.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| doc/patterns.md | Replaces duplicated critical rules with a link to llms.md; minor example indentation cleanup. |
| doc/llms.md | Adds rule about re-applying critical rules during debugging and links the new Figma MCP doc in the references. |
| doc/layout.md | Improves Inline docs formatting and adds an Inline nesting example/pattern. |
| doc/figma-mcp.md | New documentation defining MCP-to-Mistica translation rules, Code Connect drill-down workflow, and asset handling guidance. |
| doc/components.md | Adds centering guidance combining <Align> and textAlign. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
Deploy preview for mistica-web ready!
Deployed with vercel-action |
1.2 case is visually worse than the stable version 🤔 |
|
@yceballost Some things are worse and some things are better in the stable version. All the images failed downloading from figma tho, that makes it look weird. |
|
Size stats
|


















Introduction
The objective of this task was investigating figma MCP (with and without code connect).
First, we started by carrying out a bunch of manual tests. They made clear that implementing figma designs with a screenshot was far worse than implementing them with the MCP.
Then, using the MCP we looked for common implementation flaws. Based on those flaws we created a specific doc targeting LLMs trying to implement figma designs (hence this PR). We mainly found that the LLM didn't dig down into the figma layouts sometimes and missed props. For example, instead of using a slot for the prices it usually puts all the text in the description. On the other hand, it missed a lot of the text sizings and sometimes it tried to use the literal hex colors in the MCP output. Also sometimes it ignored the literal spacing values from the MCP. We wrote doc to try to avoid all these common flaws.
We also found a core flaw while trying to center texts. We added instructions to avoid it to the global skill doc.
Some of these tests made us think that figma essentially behaved the same with vs without code connect when implementing figma designs with mistica.
In order to systematize our investigation, we followed by carrying out some systematic tests comparing different conditions, that will be detailed below.
Previous investigation pointed us towards these 3 conditions:
As you can see further investigation should probably be carried out in the future with code connect.
Summary
Systematic tests proved LLMs are rather inconsistent when carrying out large tasks like implementing these figmas. The variation was enormous: a lot of hallucination, skipping explicit rules in the doc, using native primitives (llm made excuses like "being under pressure") or even cases where it skipped implementing parts of the page entirely. Also despite having clear values spit out by the MCP, llms "forget" them fairly frequently. One of the subagents even got stuck in one of the tasks and had to be relaunched. These tests were carryed out with Opus xhigh, so this makes us think that LLMs probably aren't suited for big tasks without surveillance and they behave far better when given small tasks and clear objectives. We would like to investigate splitting these large tasks in small subtasks in the future via skill or similar.
Patterns
figma-mcp.mddoc improves implementation consistently Beta's wins come from doc-driven behaviors: Drilling into composite nodes withdisableCodeConnect: true, picking modern card props (imageSrc/slot/headline) over deprecated ones (media={<Image>}/extra/description), mapping Tag types per item and better centering.Grid/GridLayoutfor asymmetric splits,Boxedfor tile containers,<Align x="center">for centering — all are consistently missed in favour of<div style={{display:"grid"}}>,<Inline>withoutfullWidth, or<div style={{textAlign:"center"}}>. The skill's instructions on these are explicit; both agents still fall back to CSS escape hatches randomly.Chip,BoxedRowList/BoxedRow,Slideshow,MediaCard, content-switchingTabs, andTag typemapping per item.case 1andcase 2ended in ties or stable wins. Iter-3case 3reopened the gap (perhaps because it is a more complex page).Random content
Despite having explicit instructions and clear outputs from figmas, both versions randomly missed primitives or components.
disableCodeConnect: truedrill-down step is skipped intermitently.<Image>.BoxedRowList/BoxedRow(right) vs custom<button>wrapping<Boxed>(wrong).Iterations
We used 3 figma designs in each iteration. With 3 iterations and 2 conditions that makes a total of 18 tests (they were super heavy, and I ran out of tokens twice). All the figma desings can be checked out here (raw vs polished): https://www.figma.com/design/a0kSW0iLff0omlQbUoaJQO/IA-x-Design?node-id=93-1039&m=dev.
Iteration 1: MCP doc vs MCP no doc
Original (unpolished) Figma files; no Code Connect. Beta's only material advantage is the new
doc/figma-mcp.mdrule file shipped in16.62.0-beta.1.1.1 — Case 1 (product list)
<button>w/ inline stylePaginationprimitive in either version.<div style={{display:"grid"}}>repeat(auto-fill, minmax)<Grid columns={3} gap={24}><span style={{color}}><Text2 as="span" medium><div style={{marginLeft:"auto"}}>Inline space="between"(with empty<span/>spacer)<aside>around<FiltersSidebar />IconChevronLeftRegular/IconChevronRightRegularIconChevronDownRegularrotated via<span style={{transform:rotate(...)}}>Net. Beta uses Mistica primitives in 5/7 categories vs 1/7 for stable, and lands closer to the Figma visually (correct column count, real price slot, correctly sized stars). Stable's only win is using the proper directional chevrons instead of rotating one. Shared unavoidable escape hatches: page-number
<button>(noPagination).1.2 — Case 2 (PDP)
<Grid columns={2}><GridLayout template="6+6"><Tabs>rendered butselectedTabignored; sections stackedselectedTabdrives{selectedTab === 0 && <InformacionTab />}<div>s with chevron icons inside<IconButton small Icon={IconChevronLeftRegular}><div style={{flex:1}}>flex hack wrappingBoxed<button style={...}>ThumbnailButton<button>w/ inline style<button>+ 5 hex literals<button>+ 5 hex literalsRadioGroup+RadioButtoninsideBoxedBoxed+Box+Stack+Image(no card primitive)InfoRating value={4.5} withHalfValueInfoRating value={4}<div style={{flex:1}}>× 2 insideInline<Inline space="between">stretches each rowNet. Beta uses Mistica primitives in 5/11 vs 2/11. Beta adds
GridLayout, content-switchingTabs,Inline space="between", probably becausefigma-mcp.mddoc steering primitive selection. Shared errors: color swatches (no primitive, hex literals), companion cards defaulting toBoxedinstead ofMediaCardon this run.Note beta version screen is smaller because it implemented tab logic.
1.3 — Case 3
<DataCard>with raw<img>asasset<MediaCard imageSrc=… mediaAspectRatio="1:1">extra={…}(deprecated;slot=is the replacement)slot={<Stack>…<Text4 medium>{price}</Text4>…</Stack>}Tag type="promo"for all four cards including "Novedad"Tag type={tag.type}per item (Novedad →active, Exclusivo online →promo)<Grid columns={4}>(static)<Carousel itemsPerPage={{mobile:1, tablet:2, desktop:4}}><Circle size={56} backgroundColor=…><div style={{borderRadius:"50%"}}>(skipped MisticaCircle)<Inline alignItems="center">+<div style={{margin:"0 auto"}}><Align x="center"><div style={{display:"flex", justifyContent:"center"}}><Align x="center">for icon + each text lineMainNavigationBartopSlot<div style={{...}}>w/ inline icon<span style={{color:"#fff", fontWeight:700, …}}>{letter}</span><div style={{borderRadius:"50%"}}>colored dots. No brand SVGs in either.color="#fff"literal<MovistarLogo>(no override; skin handles inverse)MainNavigationBarright-side actions<NavigationBarActionGroup>+NavigationBarAction<Avatar size={32} initials="ES"><CoverHero backgroundImage=…><CoverHero backgroundImage="/images/hero-fibra.png"><CoverHero>insideResponsiveLayout variant="negative"<CoverHero backgroundImage=…><CoverCard>× 3 inGridw/height={456}matching Figma<CoverCard>× 3 with no aspect-ratio / height (renders shorter than designed)Net. Beta uses Mistica primitives in 10/15 vs 7/15. Wins are concentrated in card composition (
MediaCardoverDataCard+<img>,slot=over deprecatedextra=, correctTagtypes per item,Carouselover a staticGrid,Alignfor centering, no hardcoded white). Stable's structural advantages are real but localized: it pinned the promo cover cards' height to match Figma and usedCirclefor category icons — beta missed both.Iteration 2: MCP doc polished figmas vs MCP no doc polished figma
Same designs as iteration-1, rebuilt for agent compatibility (real designer assets where applicable, explicit placeholder cards where not, tighter Code Connect mappings, cleaner DOM structure).
2.1 — Case 1 (product list)
<Hero>withimageSrc=andheadline={<Tag type="info">}<Tag type="active"><Chip active={…} onPress={…}>per option<Checkbox><Checkbox><RadioGroup>+<RadioButton>+<InfoRating><Grid columns={12}>+<GridItem columnSpan={3}>/columnSpan={9}<Grid columns={4}>+ raw<div style={{gridColumn:"span 3"}}>(worse: skippedGridItem). Neither usesGridLayout.<MediaCard imageSrc=… mediaAspectRatio="1:1" headline={<Tag>} title=… description=…><div>w/<img>(agent invented a wrong constraint aboutMediaCardnot exposing the slot)description="Por solo:\n64,90 € / iva incl."blobPriceSlotwithText2for labels +Text4 mediumdigitsTag type="info"for "Novedad" (wrong);promofor "Exclusivo online" (right)activefor "Novedad" (matches Figma),promofor "Exclusivo online"IconButton+ page-number<button>Net. Polishing the Figma raises the floor for both versions vs iteration-1. Both used
Hero,Chip,Checkbox,RadioGroup+RadioButton+InfoRatingwithout prompting. The version-attributable gap that remains is in typography and tag semantics: beta consistently picks the rightTag typeper item and steps the text presets correctly across price blocks; stable used the wrong tag type and flatdescription=. Notable inversion: beta hand-rolled the product card on this run while stable correctly usedMediaCard. Visible defects: wrong filter-header alignment in both versions; beta's rating filter repeatsvalue={3}3 times, breaking the filter visually.2.2 — Case 2 (PDP)
<Slideshow>correct, but the thumbnail row does not render in the page (broken layout)<Slideshow withBullets>with placeholder thumbnails visible and clickableInlineof two<Image>s<Carousel>for the image strip<Grid columns={12}>+<GridItem columnSpan={7/5}><GridLayout template="6+6"><RadioGroup>+<BoxedRowList>+<BoxedRow><Chip><div>+ 5 hex literals<button>+ 6 hex literals. No swatch primitive.<MediaCard>with deprecatedmedia={<Image>}<MediaCard imageSrc=… title=… pretitle=… slot=…>(modern API)description="..."blobslot={<Stack>…<Text4 medium>{price}</Text4>…</Stack>}<Tabs>driving conditional content<ProgressBar>correct primitive but renders broken<div>shim, also visually wrong. Neither version renders this section correctly.Starswrapper aroundInfoRating<div style={{display:"inline-flex"}}>wrapperInfoRatingdirectlyNet. Both versions picked up
Chip,BoxedRowList/BoxedRow,Slideshow,MediaCard, content-switchingTabs, perhaps from the cleaner Figma — no library change required. Where versions still differ, the gap is consistent with iteration-1: beta uses the modernslot-style props,GridLayout,Carousel, andimageSrc=; stable uses the deprecatedmedia=/ flat-descriptionprops,Grid+columnSpan, and a staticInlineof images. Two visible defects to flag: stable's hero thumbnail row doesn't render in the page; neither version renders the rating distribution bars correctly.2.3 — Case 3
<MainNavigationBar logo={<MovistarLogo>}><Avatar size={32} src=…><Slideshow>wrapping<CoverHero><CoverHero backgroundImage=…><Circle size={56}>containing the icon<Image src=… circular width={80}><CoverCard>× 3<MediaCard imageSrc=… mediaAspectRatio="1:1" headline={<Tag>} pretitle=… description=…><MediaCard imageSrc=… headline={<Tag>} slot={<PriceSlot/>}>description=…blobslot={<PriceSlot>}withText4 mediumdigitsTag type="promo"(red, wrong); other tags mapped inconsistentlyactive, "Exclusivo online" →promo, correctly per item<CoverHero>with title + button only. The 3 Originals/Series/DeporteCoverCards are not in the file at all<CoverHero extra={<Grid columns={3}>{3× <CoverCard>}</Grid>}>matching Figma<MovistarLogo size={52} color={…textPrimaryInverse}>brand-logo.png); rendered slightly cropped<div>/<span>/<button>/<img>and zerostyle={{}}blocks in 455 linesNet. This is the test where stable looks cleanest at the file level — zero raw HTML / inline styles in 455 lines of
App.tsx. Beta still has the structural edge on three categories that map to specific Mistica idioms:slot=for the tech-card price block, correctTag typeper item, and<CoverHero extra={…}>to nest the Movistar Plus+ mini-cards. Stable did not render the threeCoverCards at all — there is noCoverCardfor Movistar Originals / Mejores series y películas / Todo el deporte anywhere in stable'sApp.tsx, so a whole row of the design is missing from the page.Iteration 3: MCP doc polished figma partial code connect vs MCP no doc polished figma partial code connect
Same polished designs as iteration-2. Difference: Figma Code Connect mappings are now enabled, so the MCP returns
<CodeConnectSnippet>hints indicating which Mistica components the designer chose.3.1 — Case 1 (product list)
<Hero>withimageSrc=andheadline={<Tag>}<Chip><Checkbox><Checkbox><RadioGroup>+<RadioButton>+<InfoRating><GridLayout template="3+9">(improvement vs iter-2)GridLayout template="3+9"<MediaCard imageSrc=… mediaAspectRatio="1:1" headline={<Tag>} slot={<PriceSlot/>}>slot=withText2/Text3 regular/Text2 mediumactive/promo<Grid columns={3} gap={24}>{products.map(p => <ProductCard/>)}</Grid><Stack>{rows.map(row => <Inline>{row.map(p => <div style={{flex:1}}><ProductCard/></div>)}</Inline>)}</Stack>(raw flex-hack wrappers)IconButton+ page-number<button>Net. Code Connect on the polished file pushed both versions up to a similar quality bar — but this run actually tipped slightly in stable's favour because beta went off-script on the product grid layout, hand-chunking rows into
<Stack>+<Inline>with<div style={{flex:1}}>wrappers instead of usingGrid columns={3}. The version-attributable items beta usually wins (modern card props, slot-style price block, correct tag types per item) all hit ✓ on stable too this run, so the only structural delta left was the grid choice — and beta lost it. Persistent shared miss: noPaginationprimitive in either version.3.2 — Case 2 (PDP)
<Slideshow>for the main image, with proper bullets/controls<Image>+ raw<button>thumbnails (regression vs iter-2)<GridLayout template="6+6">(improvement vs iter-2)VariantBoxedRow: raw<button>wrapping<Boxed>+ hand-rolled<span>radio (regression vs iter-2)<RadioGroup>+<BoxedRowList>+<BoxedRow><Chip><button>+ 6 hex literals<MediaCard imageSrc=… mediaAspectRatio="1:1" slot={<PriceSlot/>}>(modern API)<MediaCard media={<Image>}>— uses the deprecatedmedia={<Image>}prop instead ofimageSrc=(regression vs iter-2)slot=(digits use raw<Text size={20}>— should beText4 medium)slot=withText3 regularfor digits<Tabs>with conditional content<Carousel itemsPerPage={…}><Carousel><ProgressBar progressPercent={…}>(renders correctly this iteration)<ProgressBar>Tag type="active"(correct)Tag type="active"<Accordion defaultIndex={[0, 1, 2]}>Net. Enabling Code Connect on the polished file closed the version-attributable quality gap for this design — both scored 10/12, with non-overlapping defects. Two role reversals: stable correctly used
imageSrc=onMediaCardwhile beta regressed to deprecatedmedia={<Image>}; stable hand-rolled the variant selector while beta correctly usedBoxedRowList/BoxedRow; beta hand-rolled the hero gallery while stable usedSlideshowcorrectly. Cost-wise, Code Connect was a net negative for stable (longer reads, more tool calls, one stalled run that had to be re-spawned) and a net positive for beta (fewer tool calls, faster wall-clock).3.3 — Case 3
<MainNavigationBar>correct, but logo slot is plain text ("Movistar"); no<Avatar>("Label" placeholder)<MainNavigationBar logo={<MovistarLogo>}+<Avatar size={24} initials="L"><CoverHero backgroundImage=…><div style={{display:"grid", gridTemplateColumns:"repeat(6, 1fr)"}}><Inline space={24}>withoutfullWidth, doesn't span page. Should have been MisticaGrid.<div>w/ inline border/padding/radius (no<Boxed>)<div>shape, no<Boxed><Inline>withoutfullWidth<CoverCard>× 3<CoverCard>× 3<Inline>withoutfullWidth<MediaCard><MediaCard>extra={…}(digits do useText4 medium)slot={<ProductPriceSlot>}withText4 medium+Text2active/promo(improvement vs iter-2)<CoverHero extra={…}>(improvement vs iter-2 where stable dropped the 3 cards)extrarow layout (3 mini cards)<Inline>withoutfullWidth<div style={{display:"grid"}}><Inline space={24}>withoutfullWidth<div style={{textAlign:"center"}}>for section title and per-block. Did not reach for MisticaText textAlign="center"per block) but wrapped icon in<div style={{display:"flex", justifyContent:"center"}}>and titles in<div style={{textAlign:"center"}}>— missed<Align x="center">despite the skill spelling it out<MovistarLogo>Net. The recurring story for iter-3 landing page is layout primitives: every row in the page (categories, promo covers, tech products, Movistar Plus+ extra cards, value props) wants Mistica
GridorGridLayout, and neither version reaches for it on any of those rows. Stable falls back to raw CSS grid every time; beta falls back to<Inline>withoutfullWidth(closer in spirit, still not the right primitive). The version-attributable wins for beta this run are concentrated in three places: modernslot=(vs stable's deprecatedextra=),<MovistarLogo>and<Avatar>in the nav,<MovistarLogo>in the footer. The category-tile container is the most consistent shared miss — both versions hand-roll a<div>with inline border/padding/radius instead of using<Boxed>, exactly the primitive that exists for this. The value-props section is the most explicit failure case: skill explicitly recommends<Align x="center">; beta missed it; stable didn't even attempt and went straight to CSS.Results
Quality scores at a glance
Score = number of categories where the implementation used the right Mistica primitive instead of html+css (denominator varies by design and iteration as the analysis got finer-grained).
case 1(product list)case 2(PDP)case 3Tokens
case 1tokenscase 2tokenscase 3tokensDuration
case 1case 2case 3Conclussions
IMO LLMs don't seem to be ready for these big tasks. The bigger the task is the more it tends to get lost and be inconsistent. Probably due to their predictive nature, randomness strikes negatively when a lot of decisions have to be made. We would probably be better off giving LLMs smaller tasks and having them under surveillance.
ref: WEB-2435