Skip to content

Commit 02c0036

Browse files
aweellclaudeCopilot
authored
chore(Skill): read Mística Figma grid layouts into GridLayout/ResponsiveLayout (#1605)
## Summary Teaches the `mistica-react` skill to read the layouts the updated `mistica-figma` skill produces, so a design built with the new Figma conventions is implemented with `GridLayout` / `ResponsiveLayout` instead of raw `Grid`/flex `div`s. The mistica-figma skill builds sections as native 12-column GRID frames (column spans + template naming) and full-width padded `ResponsiveLayout` frames. The reading path (`doc/llms/figma-mcp.md`) predates this and was written against the older flex/Tailwind MCP output — it had no `GridLayout` row and no guidance for the grid/section conventions. The mapping rules here are grounded in the real Figma MCP DOM (`grid grid-cols-[repeat(12,minmax(0,1fr))]` + `col-[start/span_N]`) and verified against the `@telefonica/mistica` source (`GridLayout` templates, the `Variant` enum, the `ResponsiveLayout` wrap requirement). ## Changes - **`doc/llms/figma-mcp.md`** — mapping-table rows for the plain `ResponsiveLayout` section, the `grid grid-cols-[repeat(12,…)]` section, and `col-[S/span_N]` children; a "Reading grid sections" subsection with a child-span → `template` lookup (`6+6`, `8+4`, `4+6`, `3+9`, `5+4`, `10`, `8`), the wrap rule (`ResponsiveLayout` = side margins only, `GridLayout` = columns), `px`→margin / `py`→`Box`, background→`variant`, mobile collapse, and a worked `3+9` example. - **`doc/llms/agents/figma-verifier.md`** — flag raw grid/flex/`Grid`/spacers used instead of a span-matched `GridLayout`, an unwrapped `GridLayout`, a padded section without `ResponsiveLayout`, and double-wrapped nav/tabs. ## Validation (A/B test per `TESTING-MISTICA-SKILL.md`) `claude-opus-4-8`, 3 runs/arm, isolated workspaces differing only in these two files, same byte-for-byte prompt against a Figma frame with a `6+6` hero and a `3+9` sidebar+table. | Dimension | Baseline | Modified | | --- | --- | --- | | Correct `GridLayout` template usage | 2/3 runs | **3/3 runs** | | Primitive compliance / pixel parity / tokens / time | 100% / faithful / — | 100% / faithful / no meaningful change | The baseline failure run hand-built the split with low-level `Grid`/`GridItem` + non-responsive hardcoded `paddingX={48}` (still "Mistica primitives", so compliance and the desktop screenshot did not flag it). The modified arm reliably used the semantic `GridLayout` templates. No regression in tokens, cost, latency, or fidelity. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
1 parent 09f7888 commit 02c0036

2 files changed

Lines changed: 80 additions & 19 deletions

File tree

doc/llms/agents/figma-verifier.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,12 @@ rules before starting.
3737
`font-family`, composite props that don't match the non-CodeConnect DOM, spacing values absent from the
3838
DOM, mismatched semantic structure (`Stack` vs `Inline`, flat vs nested), native HTML where a Mistica
3939
primitive exists, wrong/missing skin/font/body-background for the file's brand.
40+
- **Layout primitives.** When the DOM shows `grid grid-cols-[repeat(12,…)]` with `col-[…/span_N]`
41+
children, flag any implementation that used a raw `<div style={{display:'grid'}}>`, a fixed-width flex
42+
row, `Grid`, or manual spacer columns instead of a `GridLayout` whose `template` matches the child
43+
spans. Flag a `GridLayout` that is not wrapped in a `ResponsiveLayout`, and a full-width padded section
44+
(`px-[48/32/16px]`) implemented without a `ResponsiveLayout`. Conversely, flag double-wrapping a
45+
component that embeds its own `ResponsiveLayout` (`MainNavigationBar`, `NavigationBar`, `Tabs`).
4046
4147
5. **Report**. One summary line (`clean` or `N issues found, M blocking`), then a bullet list ordered by
4248
severity. Each issue:

doc/llms/figma-mcp.md

Lines changed: 74 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -34,29 +34,84 @@ that your implementation matches the designer's intent.
3434
**If you cannot point at a line in the DOM to justify a value, do not write that value.** Do not pick "nearby
3535
nicer" numbers.
3636

37-
## Mapping Figma flex to Mistica layout primitives
38-
39-
| Figma | Mistica |
40-
| --------------------------------------- | --------------------------------------------------- |
41-
| `flex gap-[Npx]` (vertical, `flex-col`) | `Stack space={N}` |
42-
| `flex gap-[Npx]` (horizontal) | `Inline space={N}` |
43-
| `justify-between` | `Inline space="between"` |
44-
| `justify-around` | `Inline space="around"` |
45-
| `justify-evenly` | `Inline space="evenly"` |
46-
| `items-center` | `alignItems="center"` on `Inline` |
47-
| `flex-wrap` | `wrap` on `Inline` |
48-
| `p-[Npx]` / `px-[Npx]` / `py-[Npx]` | `Box padding={N}` / `paddingX={N}` / `paddingY={N}` |
49-
| `rounded-[var(--radii/container,...)]` | `Boxed` (or `skinVars.borderRadii.container`) |
50-
| `bg-[var(--background...)]` | `ResponsiveLayout variant` or `Boxed variant` |
37+
## Mapping Figma layout to Mistica layout primitives
38+
39+
| Figma | Mistica |
40+
| ------------------------------------------------------------- | ------------------------------------------------------------------------------------------------ |
41+
| `flex gap-[Npx]` (vertical — has `flex-col`) | `Stack space={N}` |
42+
| `flex gap-[Npx]` (horizontal — no `flex-col`) | `Inline space={N}` |
43+
| Full-width section with `px-[48/32/16px]`, **no** `grid-cols` | `ResponsiveLayout` (responsive side margins; content stacks inside) |
44+
| `grid grid-cols-[repeat(12,…)]` on a full-width section | `GridLayout` (inside a `ResponsiveLayout`) — see [Reading grid sections](#reading-grid-sections) |
45+
| `col-[S/span_N]` on a grid child | a `GridLayout` column of span `N` (drives the `template`) |
46+
| `justify-between` | `Inline space="between"` |
47+
| `justify-around` | `Inline space="around"` |
48+
| `justify-evenly` | `Inline space="evenly"` |
49+
| `items-center` | `alignItems="center"` on `Inline` |
50+
| `flex-wrap` | `wrap` on `Inline` |
51+
| `p-[Npx]` / `px-[Npx]` / `py-[Npx]` | `Box padding={N}` / `paddingX={N}` / `paddingY={N}` |
52+
| `rounded-[var(--radii/container,...)]` | `Boxed` (or `skinVars.borderRadii.container`) |
53+
| `bg-[var(--background...)]` | `ResponsiveLayout variant` or `Boxed variant` |
5154

5255
Each spacing primitive has its own allowed scale. Figma values outside the scale must be rounded to the
5356
nearest allowed value and noted — never silently apply arbitrary CSS.
5457

58+
**Vertical vs. horizontal flex.** The presence of `flex-col` is the deciding signal: `flex` with `flex-col` is
59+
a `Stack`; `flex` without it is an `Inline`. A `flex flex-[1_0_0]` child fills its track — map it to a
60+
component sized to fill its `Inline` / `GridLayout` slot, not to a fixed width.
61+
62+
## Reading grid sections
63+
64+
A full-width section laid out as a 12-column grid is a `GridLayout`. The MCP output makes this explicit — the
65+
section `div` carries `grid grid-cols-[repeat(12,minmax(0,1fr))]` (plus `gap-x-[24px]` on desktop / `16px` on
66+
tablet), and each direct child carries a `col-[start/span_N]` class. Read the `span_N` of each non-empty child
67+
(ignore empty spacer `div`s), in order, and match the sequence to a `GridLayout` template:
68+
69+
| Child column spans, in order | `GridLayout` |
70+
| ---------------------------- | ------------------------------------------------- |
71+
| 6, 6 | `template="6+6"` (`left` / `right`) |
72+
| 8, 4 | `template="8+4"` (`left` / `right`) |
73+
| 4, 6 | `template="4+6"` (`left` / `right`) |
74+
| 3, 9 | `template="3+9"` (`left` / `right`) |
75+
| 5, 4 | `template="5+4"` (`left` / `right`) |
76+
| single span 10 | `template="10"` (`children`) |
77+
| single span 8 | `template="8"` (`children`) |
78+
| anything else | no `template` — pass `children` (raw 12-col grid) |
79+
80+
Rules:
81+
82+
- **Wrap the `GridLayout` in a `ResponsiveLayout`.** `ResponsiveLayout` supplies the responsive side margins
83+
only (it has no columns); `GridLayout` supplies the 12-column structure. The section's `px-[48px]` /
84+
`px-[32px]` / `px-[16px]` is the `ResponsiveLayout` margin — do not re-apply it as `Box` padding. The
85+
section's `py-[Npx]` is real vertical padding → `Box paddingY={N}` inside.
86+
- **Match the template; never hand-build the grid.** Do not emit a raw `<div style={{display:'grid'}}>`, a
87+
fixed-width flex row, or manual spacer columns. The spacer-span templates (`4+6`, `5+4`, `10`, `8`) render
88+
their own spacer columns internally — select the named `template` and ignore any empty spacer `div`s the MCP
89+
DOM shows around the content.
90+
- **Background variant comes from the section fill**, not the grid: `bg-[var(--backgroundalternative,…)]`
91+
`ResponsiveLayout variant="alternative"` (likewise `--backgroundbrand``"brand"`, `--backgroundnegative`
92+
`"negative"`, an image/video background → `"media"`); plain `bg-[var(--background,…)]` → default.
93+
- **Mobile is the same `GridLayout`.** When the design stacks the grid to a single column on mobile, that is
94+
`GridLayout`'s own responsive behaviour (it collapses to one column) — keep one `GridLayout`; do not branch
95+
to a separate `Stack`.
96+
97+
Working example (the `3+9` data section: a filter sidebar beside a table):
98+
99+
```tsx
100+
<ResponsiveLayout variant="default">
101+
<Box paddingY={32}>
102+
<GridLayout template="3+9" left={<FilterPanel />} right={<DataTable />} />
103+
</Box>
104+
</ResponsiveLayout>
105+
```
106+
107+
Do not wrap components that embed their own `ResponsiveLayout` (e.g. `MainNavigationBar`, `NavigationBar`,
108+
`Tabs` — see the list in [`layout.md`](../layout.md)) in another `ResponsiveLayout`.
109+
55110
## Don't snap Figma values to Mistica's rhythm
56111

57-
Mistica's 16 / 24 / 32 vertical-rhythm guidance in `patterns.md` and `layout.md` is for **greenfield
58-
composition** — UI you are designing yourself. It is not a reason to override explicit Figma values. When the
59-
DOM specifies a spacing, use it literally.
112+
Mistica's 16 / 24 / 32 vertical-rhythm guidance in [`patterns.md`](../patterns.md) and
113+
[`layout.md`](../layout.md) is for **greenfield composition** — UI you are designing yourself. It is not a
114+
reason to override explicit Figma values. When the DOM specifies a spacing, use it literally.
60115

61116
## Tokens over literal values
62117

@@ -75,8 +130,8 @@ is ahead of the skin — flag it and extend the skin instead of hardcoding.
75130

76131
Ignore per-node `font-[family-name:var(--fontfamily/fontfamily,'Movistar_Sans:Medium',...)]` and
77132
`font-['On_Air:Regular',...]` classes. Font family is set **once globally** under `ThemeContextProvider` via
78-
`GlobalStyles` — the active skin's font (see `fonts.md`) is the source of truth. Per-node font families in the
79-
MCP output are leaked style from the Figma file, not designer intent.
133+
`GlobalStyles` — the active skin's font (see [`fonts.md`](../fonts.md)) is the source of truth. Per-node font
134+
families in the MCP output are leaked style from the Figma file, not designer intent.
80135

81136
Font weight is handled by the Mistica text components (`Text1`-`Text10` and `Title1`-`Title4`). Map Figma's
82137
`font-weight/text5` to the matching component (e.g. `Text5`), not to a CSS `font-weight`.

0 commit comments

Comments
 (0)