Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions doc/llms/agents/figma-verifier.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ rules before starting.
`font-family`, composite props that don't match the non-CodeConnect DOM, spacing values absent from the
DOM, mismatched semantic structure (`Stack` vs `Inline`, flat vs nested), native HTML where a Mistica
primitive exists, wrong/missing skin/font/body-background for the file's brand.
- **Layout primitives.** When the DOM shows `grid grid-cols-[repeat(12,…)]` with `col-[…/span_N]`
children, flag any implementation that used a raw `<div style={{display:'grid'}}>`, a fixed-width flex
row, `Grid`, or manual spacer columns instead of a `GridLayout` whose `template` matches the child
spans. Flag a `GridLayout` that is not wrapped in a `ResponsiveLayout`, and a full-width padded section
(`px-[48/32/16px]`) implemented without a `ResponsiveLayout`. Conversely, flag double-wrapping a
component that embeds its own `ResponsiveLayout` (`MainNavigationBar`, `NavigationBar`, `Tabs`).

5. **Report**. One summary line (`clean` or `N issues found, M blocking`), then a bullet list ordered by
severity. Each issue:
Expand Down
79 changes: 67 additions & 12 deletions doc/llms/figma-mcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,22 +36,77 @@ nicer" numbers.

## Mapping Figma flex to Mistica layout primitives

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

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

**Vertical vs. horizontal flex.** The presence of `flex-col` is the deciding signal: `flex` with `flex-col`
is a `Stack`; `flex` without it is an `Inline`. A `flex flex-[1_0_0]` child fills its track — map it to a
component sized to fill its `Inline` / `GridLayout` slot, not to a fixed width.

## Reading grid sections

A full-width section laid out as a 12-column grid is a `GridLayout`. The MCP output makes this explicit — the
section `div` carries `grid grid-cols-[repeat(12,minmax(0,1fr))]` (plus `gap-x-[24px]` on desktop / `16px` on
tablet), and each direct child carries a `col-[start/span_N]` class. Read the `span_N` of each child, in
order, and match the sequence to a `GridLayout` template:
Comment on lines +64 to +67

| Child column spans, in order | `GridLayout` |
| ---------------------------- | ---------------------------------------------- |
| 6, 6 | `template="6+6"` (`left` / `right`) |
| 8, 4 | `template="8+4"` (`left` / `right`) |
| 4, 6 | `template="4+6"` (`left` / `right`) |
| 3, 9 | `template="3+9"` (`left` / `right`) |
| 5, 4 | `template="5+4"` (`left` / `right`) |
| single span 10 | `template="10"` (`children`) |
| single span 8 | `template="8"` (`children`) |
| anything else | no `template` — pass `children` (raw 12-col grid) |
Comment on lines +71 to +78

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems redundant to have all the cases, but I guess that's how we tested it


Rules:

- **Wrap the `GridLayout` in a `ResponsiveLayout`.** `ResponsiveLayout` supplies the responsive side margins
only (it has no columns); `GridLayout` supplies the 12-column structure. The section's `px-[48px]` /
`px-[32px]` / `px-[16px]` is the `ResponsiveLayout` margin — do not re-apply it as `Box` padding. The
section's `py-[Npx]` is real vertical padding → `Box paddingY={N}` inside.
- **Match the template; never hand-build the grid.** Do not emit a raw `<div style={{display:'grid'}}>`, a
fixed-width flex row, or manual spacer columns. The spacer-span templates (`4+6`, `5+4`, `10`, `8`) render their
own spacer columns internally — select the named `template` and ignore any empty spacer `div`s the MCP DOM
shows around the content.
- **Background variant comes from the section fill**, not the grid: `bg-[var(--backgroundalternative,…)]` →
`ResponsiveLayout variant="alternative"` (likewise `--backgroundbrand` → `"brand"`, `--backgroundnegative`
→ `"negative"`, an image/video background → `"media"`); plain `bg-[var(--background,…)]` → default.
- **Mobile is the same `GridLayout`.** When the design stacks the grid to a single column on mobile, that is
`GridLayout`'s own responsive behaviour (it collapses to one column) — keep one `GridLayout`; do not branch
to a separate `Stack`.

Worked example (the `3+9` data section: a filter sidebar beside a table):

```tsx
<ResponsiveLayout variant="default">
<Box paddingY={32}>
<GridLayout template="3+9" left={<FilterPanel />} right={<DataTable />} />
</Box>
</ResponsiveLayout>
```

Do not wrap components that embed their own `ResponsiveLayout` (e.g. `MainNavigationBar`, `NavigationBar`,
`Tabs` — see the list in `layout.md`) in another `ResponsiveLayout`.

## Don't snap Figma values to Mistica's rhythm

Mistica's 16 / 24 / 32 vertical-rhythm guidance in `patterns.md` and `layout.md` is for **greenfield
Expand Down
Loading