Skip to content

Commit 6e73227

Browse files
committed
docs/critical-rules-reapply-during-debugging: Added some rules
1 parent bf6280b commit 6e73227

5 files changed

Lines changed: 276 additions & 18 deletions

File tree

doc/components.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,25 @@ Positions a primary and optional secondary button:
8383

8484
## Text and Titles
8585

86+
> 💡 **Centering text uses two complementary props:** > [`<Align x="center">`](./layout.md#align) positions
87+
> the `Text*` / `Title*` element within its parent; `textAlign="center"` (on `Text*`) aligns the content
88+
> _within_ the element. For a short single-line label, `Align` alone is enough. For multi-line text that
89+
> should also have each wrapped line centered, use both. `Title*` does not accept `textAlign`.
90+
>
91+
> ```tsx
92+
> // Single-line label — Align alone
93+
> <Align x="center">
94+
> <Text4>Sonido</Text4>
95+
> </Align>
96+
>
97+
> // Multi-line description — Align positions the element, textAlign centers the lines
98+
> <Align x="center">
99+
> <Text2 regular textAlign="center">
100+
> Movistar te garantiza la mejor calidad de conexión de banda ancha del mercado.
101+
> </Text2>
102+
> </Align>
103+
> ```
104+
86105
### Text components
87106
88107
`Text1` through `Text10` render text at progressively larger sizes. `Text1`-`Text4` accept a `weight` prop.

doc/figma-mcp.md

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
# Figma MCP
2+
3+
Mandatory reading whenever you are implementing UI from Figma through the Figma MCP (`get_design_context`,
4+
`get_metadata`, `get_screenshot`, `use_figma`) or when the user provides a `figma.com/...` URL. This file only
5+
covers the translation layer — the rest of the Mistica docs (`patterns.md`, `components.md`, `layout.md`,
6+
`design-tokens.md`) still apply.
7+
8+
## Prime directive: read the DOM verbatim
9+
10+
The Figma MCP response gives you two things:
11+
12+
- a **screenshot** that shows what the design should look like
13+
- a **DOM** (React + Tailwind) that shows what the designer specified
14+
15+
Use the DOM as the source of truth for every numeric and structural decision. The screenshot only validates
16+
that your implementation matches the designer's intent.
17+
18+
**If you cannot point at a line in the DOM to justify a value, do not write that value.** Do not pick "nearby
19+
nicer" numbers. Do not default to Mistica's 16 / 24 / 32 vertical rhythm when the DOM is explicit. If Figma
20+
says `72`, use `72`; if Figma says `justify-between`, use `space="between"` — not an invented fixed gap.
21+
22+
## Mapping Figma flex to Mistica layout primitives
23+
24+
| Figma / Tailwind | Mistica |
25+
| --------------------------------------- | --------------------------------------------------- |
26+
| `flex gap-[Npx]` (vertical, `flex-col`) | `Stack space={N}` |
27+
| `flex gap-[Npx]` (horizontal) | `Inline space={N}` |
28+
| `justify-between` | `Inline space="between"` |
29+
| `justify-around` | `Inline space="around"` |
30+
| `justify-evenly` | `Inline space="evenly"` |
31+
| `items-center` | `alignItems="center"` on `Inline` |
32+
| `flex-wrap` | `wrap` on `Inline` |
33+
| `p-[Npx]` / `px-[Npx]` / `py-[Npx]` | `Box padding={N}` / `paddingX={N}` / `paddingY={N}` |
34+
| `rounded-[var(--radii/container,...)]` | `Boxed` (or `skinVars.borderRadii.container`) |
35+
| `bg-[var(--background...)]` | `ResponsiveLayout variant` or `Boxed variant` |
36+
37+
Each spacing primitive has its own allowed scale. Figma values outside the scale must be rounded to the
38+
nearest allowed value and noted — never silently apply arbitrary CSS.
39+
40+
| Primitive | Allowed values |
41+
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
42+
| `Box` `padding*` | `0 \| 2 \| 4 \| 8 \| 12 \| 16 \| 20 \| 24 \| 32 \| 40 \| 48 \| 56 \| 64 \| 72 \| 80` |
43+
| `Stack` `space` | `0 \| 2 \| 4 \| 8 \| 12 \| 16 \| 24 \| 32 \| 40 \| 48 \| 56 \| 64 \| 72 \| 80` + `"between" \| "around" \| "evenly"` |
44+
| `Inline` `space` | `-16 \| -12 \| -8 \| -4 \| -2 \| 0 \| 2 \| 4 \| 8 \| 12 \| 16 \| 24 \| 32 \| 40 \| 48 \| 56 \| 64` + `"between" \| "around" \| "evenly"` |
45+
46+
`Inline` notably allows negative values and caps at 64; `Stack` starts at 0 and caps at 80; `Box` includes
47+
`20` which the others don't. If Figma says `gap-[10px]`, it doesn't fit any of these — round to `8` or `12`
48+
and flag it, don't invent a CSS override.
49+
50+
## The single-child gap trap
51+
52+
A `flex gap-[Npx]` class applied to a wrapper that has **only one child** renders no space — gap is between
53+
siblings, not around a solo child. Figma's MCP output often stacks wrappers this way:
54+
55+
```tsx
56+
<div className="flex gap-[72px] ...">
57+
<div className="flex ...">
58+
{' '}
59+
{/* single child */}
60+
<FirstColumn />
61+
</div>
62+
<div className="flex gap-[48px] ...">
63+
{' '}
64+
{/* single child — the 48 never renders */}
65+
<SecondColumn />
66+
</div>
67+
<div className="flex gap-[48px] ...">
68+
{' '}
69+
{/* single child — the 48 never renders */}
70+
<ThirdColumn />
71+
</div>
72+
</div>
73+
```
74+
75+
The real spacing here is `72px` (from the outer wrapper). The `48px` on each inner wrapper is dead CSS. When
76+
you see this pattern, use the parent's gap and ignore the child wrappers' gaps.
77+
78+
## Don't snap Figma values to Mistica's rhythm
79+
80+
Mistica's 16 / 24 / 32 vertical-rhythm guidance in `patterns.md` and `layout.md` is for **greenfield
81+
composition** — UI you are designing yourself. It is not a reason to override explicit Figma values. When the
82+
DOM specifies a spacing, use it literally.
83+
84+
## Tokens over literal values
85+
86+
The MCP output often contains hex colors (`#262423`), CSS custom properties
87+
(`var(--backgroundalternative,#fefaf5)`), and raw border-radius values
88+
(`rounded-[var(--radii/container,16px)]`). These must be translated:
89+
90+
- Colors → `skinVars.colors.*` (or `skinVars.rawColors.*` with `applyAlpha`)
91+
- Border radii → `skinVars.borderRadii.*`, or a Mistica component that handles it (`Boxed`, cards)
92+
- Spacing tokens → `skinVars.spacing.*` where applicable
93+
94+
Never keep a hex literal or a `var(--...)` reference in app code. If the right token doesn't exist, the design
95+
is ahead of the skin — flag it and extend the skin instead of hardcoding.
96+
97+
## Fonts
98+
99+
Ignore per-node `font-[family-name:var(--fontfamily/fontfamily,'Movistar_Sans:Medium',...)]` and
100+
`font-['On_Air:Regular',...]` classes. Font family is set **once globally** under `ThemeContextProvider` via
101+
`GlobalStyles` — the active skin's font (see `fonts.md`) is the source of truth. Per-node font families in the
102+
MCP output are leaked style from the Figma file, not designer intent.
103+
104+
Font weight is handled by the Mistica text components (`Text1`-`Text4` accept `light` / `regular` / `medium` /
105+
`bold`; `Text5`-`Text10` and `Title1`-`Title4` have a fixed weight per skin). Map Figma's `font-weight/text5`
106+
to the matching component (e.g. `Text5`), not to a CSS `font-weight`.
107+
108+
## `CodeConnectSnippet` wrappers: gather before you choose
109+
110+
MCP responses wrap mapped components in `<CodeConnectSnippet>`. **The snippet is never authoritative.** It's a
111+
hint about which Mistica component the designer used — not a source of truth for any specific prop value.
112+
Individual values inside it may happen to be correct, may be stale placeholders, or may look right but map to
113+
the wrong semantic slot. You cannot tell reliably from the snippet alone which is which, so don't try.
114+
115+
Don't classify snippet values into "trustworthy" and "untrustworthy". The classification is itself where
116+
mistakes come from. Instead: for every composite component, gather all available information first, then pick
117+
props from the combined picture.
118+
119+
### How to gather
120+
121+
For any CodeConnect-wrapped **composite component** — one with multiple content slots (`headline`, `pretitle`,
122+
`title`, `subtitle`, `description`, `extra`, `slot`, `buttonPrimary`, `buttonSecondary`, `buttonLink`,
123+
`asset`, etc.) — re-fetch the node with Code Connect disabled before mapping props:
124+
125+
```
126+
get_design_context({
127+
nodeId: "<the collapsed node id>",
128+
fileKey: "<same fileKey>",
129+
disableCodeConnect: true,
130+
excludeScreenshot: true // optional, saves tokens if you already have one
131+
})
132+
```
133+
134+
That returns the real child tree: the actual text nodes, their font-size tokens, the actual image aspect
135+
ratio, Tag instances with their `type` (e.g. the `--tagbackgroundinfo` CSS variable tells you `type="info"`),
136+
child slots that correspond to `extra` / `slot` / `headline`, sibling buttons, etc.
137+
138+
Use `get_metadata` on the same node when you also need to understand which children are component instances
139+
vs. raw nodes.
140+
141+
The usual composites: `Hero`, `CoverHero`, `Header`, `CoverCard`, `MediaCard`, `DataCard`, `NakedCard`,
142+
`PosterCard`, `DisplayMediaCard`, `Callout`, `EmptyState`, `EmptyStateCard`, `Row`, `BoxedRow`, anything with
143+
a slot. Pure atoms (`IconTruckRegular` and similar, plain `ButtonPrimary`/`ButtonSecondary`/`ButtonLink`) have
144+
a single content slot — the snippet plus the screenshot is usually enough, but if anything looks off, drill in
145+
anyway.
146+
147+
### How snippets go wrong
148+
149+
Some failure modes to keep in mind — not as a checklist of things to detect, but as reasons to gather the
150+
underlying data rather than read values off the snippet:
151+
152+
- **Stub content**`title="Title"`, `description="Description"`, `imageSrc="https://example.com/image.jpg"`,
153+
`aspectRatio="16:9"` on non-landscape media.
154+
- **Values that happen to match the schema but don't match the design**`variant="default"` on a card that
155+
actually renders inverse; `type="info"` where the real node resolves to `--tagbackgroundpromo`;
156+
`size="default"` on a snap-size card. These pass type checks silently.
157+
- **Mis-mapped content slots** — a price block under a title looks like it belongs in `description`, but the
158+
real node lives in a separate content slot that maps to `extra`. The snippet may say `description="..."`,
159+
may omit it, may stub it — only the real DOM tells you the correct prop.
160+
- **Stale prop names after an API change**`backgroundImage` on `CoverCard` when the current API is
161+
`imageSrc`; `isInverse` where `variant` is now preferred.
162+
- **Noisy artifacts**`🔄ReplaceSlot="5267:4885"`, `asset="ERROR"`, `footer="false"` (string instead of
163+
boolean), conflicting component imports at the top of the output.
164+
165+
### Rule of thumb
166+
167+
Every prop value you write — text, enum, aspect ratio, boolean — should be something you picked after reading
168+
the real (non-CodeConnect) DOM, not something you copied from the snippet. If you can't say which node in the
169+
drilled-in DOM justifies the value, gather more before committing it.
170+
171+
## Assets: always download, store, and serve locally
172+
173+
Figma MCP asset URLs (`https://www.figma.com/api/mcp/asset/<uuid>`) are valid for only ~7 days. Do **not**
174+
inline them anywhere — not in committed code, not in dev-only code, not as "temporary" placeholders. Every
175+
time you would paste a Figma MCP URL, download the asset first, save it into the project (e.g.
176+
`public/images/...` or `src/assets/...`), and reference the local path.
177+
178+
Do **not** substitute unrelated stock photos (Unsplash, Picsum, Lorem Picsum, etc.) for the designer's assets.
179+
The real images are part of the design — an Xbox controller in the Figma means Xbox, not a random
180+
phone-on-a-table from a stock library. Substituting unrelated images is the same failure class as picking an
181+
unrelated spacing value.
182+
183+
The right workflow for every image in a Figma design:
184+
185+
1. Drill into the node (per the composite section above) to get the real asset URL — initial CodeConnect stubs
186+
often use `example.com/image.jpg` placeholders that hide the actual URL.
187+
2. Download the file (`curl -o public/images/<name>.<ext> <mcp-asset-url>`). Use names that describe the
188+
content (`hero-fibra.png`, `partner-eurosport.svg`), not the Figma UUID.
189+
3. Reference it from code via the local path (e.g. `/images/hero-fibra.png` in Vite projects, `/images/...` or
190+
`import heroFibra from './assets/hero-fibra.png'` in bundler-aware setups).
191+
4. If you cannot resolve an asset — the URL 404s, the node has no fill, the design legitimately has no image
192+
there — say so explicitly and ask, rather than inventing a stock replacement.
193+
194+
The only acceptable exception is when the Figma file itself uses a stub URL (`https://example.com/image.jpg`),
195+
in which case drilling in confirmed there is no real asset, and a placeholder is appropriate — but it should
196+
still be a committed asset in the project (e.g. a branded silhouette or a neutral grey rectangle), not a live
197+
external URL.
198+
199+
## Verification checklist
200+
201+
Before closing out a section, be able to justify every decision against the DOM:
202+
203+
- [ ] For every composite component on the page (card, hero, header, callout, row…), you fetched the node with
204+
`disableCodeConnect: true` before writing props — regardless of whether the stub looked filled in or
205+
not.
206+
- [ ] Every text node in the screenshot maps to a specific Mistica prop that you can point at in the real
207+
(non-CodeConnect) DOM. The price line could be `description`, `extra`, or a custom slot; only the
208+
drilled-in DOM tells you which.
209+
- [ ] Every non-text prop (`aspectRatio`, `size`, `variant`, image URL, boolean flags) traces to an attribute
210+
you read from the real DOM, not from the CodeConnect stub. These match the schema even when wrong and
211+
typechecks won't catch them.
212+
- [ ] Every `space={N}` / `padding={N}` / `gap={N}` traces to a `gap-[Npx]` / `p-[Npx]` class on a wrapper
213+
that actually renders it (not a single-child wrapper), or to a nearest-scale round with a one-line
214+
comment explaining the original value.
215+
- [ ] Every `space="between" | "around" | "evenly"` traces to the matching `justify-*` class.
216+
- [ ] Every color uses `skinVars.colors.*`, not a hex or `var(--...)` literal.
217+
- [ ] Every font decision comes from the global `GlobalStyles` + the skin's defaults, not from a per-node
218+
class.
219+
- [ ] Every mapped component uses current Mistica props, not the literal attribute list from the
220+
`CodeConnectSnippet`.
221+
222+
If you can't check an item off against the DOM, re-read the DOM (with `disableCodeConnect: true` if the node
223+
is CodeConnect-mapped) before committing the value.

doc/layout.md

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,8 @@ a horizontal `Stack`, and it covers the most common row-layout use cases you mig
7979
It supports:
8080

8181
- horizontal distribution via `space={number}` or `space="between" | "around" | "evenly"`
82-
- vertical alignment of children via `alignItems="flex-start" | "flex-end" | "center" | "stretch" | "baseline"`
82+
- vertical alignment of children via
83+
`alignItems="flex-start" | "flex-end" | "center" | "stretch" | "baseline"`
8384
- wrapping via `wrap` and row spacing via `verticalSpace`
8485

8586
:information_source: Check `Inline` in
@@ -139,6 +140,24 @@ Distribute items evenly. Items have equal space around them
139140

140141
<img src="./images/layout/inline-evenly.svg" />
141142

143+
### nesting
144+
145+
Nest `Inline` components to compose richer rows. A common pattern groups a leading icon and label on the left
146+
with a value on the right via `space="between"`:
147+
148+
```tsx
149+
<Inline space="between" alignItems="center">
150+
<Inline space={8} alignItems="center">
151+
<IconTruckRegular size={24} color={skinVars.colors.neutralHigh} />
152+
<Text2 regular>Envío:</Text2>
153+
</Inline>
154+
<Text2 regular>Mañana, gratis</Text2>
155+
</Inline>
156+
```
157+
158+
The outer `Inline` distributes the two groups to opposite ends; the inner `Inline` keeps the icon tightly
159+
grouped with its label at a fixed gap.
160+
142161
## Align
143162

144163
Positions its children within a container using CSS grid alignment. Useful for centering content or placing it

doc/llms.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,11 @@ a documented composite component would be simpler.
5454
```tsx
5555
<style>{`body { background-color: ${skinVars.colors.background}; }`}</style>
5656
```
57+
11. **Re-apply these rules during debugging, reviewing, and fixingnot just while writing new code.**
58+
TypeScript errors, visual mismatches, and "I just need this to work" pressure are not exceptions. If you
59+
are about to add `style={{...}}` to a `<div>`, a hex literal, a manual `font-size`, a `margin: auto` /
60+
`justify-content: center` workaround, or any other escape hatch inside Mistica code, stop and find the
61+
primitive firstthe rule you need is already above.
5762

5863
## Install
5964

@@ -324,6 +329,7 @@ After reading the minimum set, read any further files that apply to your specifi
324329
| **Testing (read if you have to implement tests)** | `doc/testing.md` |
325330
| **Migrating from older versions** | `doc/migration-guide.md` |
326331
| **Optimizing bundle size with lottie** | `doc/lottie.md` |
332+
| **Implementing a Figma design via Figma MCP** | `doc/figma-mcp.md` |
327333

328334
## Docs reference
329335

@@ -347,3 +353,5 @@ After reading the minimum set, read any further files that apply to your specifi
347353
- [Testing](./testing.md): NODE_ENV, unit tests, acceptance tests, isRunningAcceptanceTest
348354
- [Lottie](./lottie.md): optimizing bundle size with lottie-web light build
349355
- [Migration guide](./migration-guide.md): cards ecosystem migration (v16), v12 to v13 migration
356+
- [Figma MCP](./figma-mcp.md): mandatory translation rules when implementing from Figma MCP output — read the
357+
DOM verbatim, map flex to Mistica primitives literally, don't snap to Mistica's rhythm

doc/patterns.md

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,8 @@
22

33
## Critical rules
44

5-
1. **NEVER hardcode colors in app/component UI code.** Always use `skinVars.colors.*` from
6-
`@telefonica/mistica`. For skins and theme-level customization, see [theme-config.md](./theme-config.md).
7-
2. **NEVER use raw `<div>` for layout.** Use `Box`, `Stack`, `Inline`, `ResponsiveLayout`, `GridLayout`,
8-
`Grid`.
9-
3. **NEVER set font sizes manually.** Use text components (`Text1`-`Text10`, `Title1`-`Title4`). If those
10-
don't cover your necessities you can set custom sizes with `Text` component.
11-
4. **NEVER set border radius manually.** Use `skinVars.borderRadii.*` or components that handle it (`Boxed`,
12-
cards, etc.). For theme-level visual customization without a dedicated component prop, use a custom skin.
13-
5. **Always wrap your app** with `ThemeContextProvider` and import `@telefonica/mistica/css/mistica.css`.
14-
6. **Always namespace React hooks**: `React.useState`, `React.useEffect`, `React.useRef`, etc.
15-
7. **Add `'use client';`** directive to client components when using Next.js app router.
16-
8. **Set `font-family` and `body` background color.** See [llms.md](./llms.md) rules 9–10 and
17-
[fonts.md](./fonts.md) for the per-skin font table, `@font-face` setup, and the `GlobalStyles` pattern.
5+
See [Critical Rules in `llms.md`](./llms.md#critical-rules) — the single source of truth. These rules apply
6+
throughout this document.
187

198
## Page layout composition
209

@@ -501,10 +490,10 @@ return (
501490
<ResponsiveLayout>
502491
<Stack space={24}>
503492
<Stepper currentIndex={currentStep} steps={['Personal', 'Address', 'Payment', 'Confirm']} />
504-
{currentStep === 0 && <PersonalInfoForm />}
505-
{currentStep === 1 && <AddressForm />}
506-
{currentStep === 2 && <PaymentForm />}
507-
{currentStep === 3 && <ConfirmationScreen />}
493+
{currentStep === 0 && <PersonalInfoForm />}
494+
{currentStep === 1 && <AddressForm />}
495+
{currentStep === 2 && <PaymentForm />}
496+
{currentStep === 3 && <ConfirmationScreen />}
508497
</Stack>
509498
</ResponsiveLayout>
510499
</Stack>

0 commit comments

Comments
 (0)