← Files Email LoveARCHIVED FILE

skills/email-love-figma-builder/references/path-a.md

10.2 KB · Oct 5, 2026 · 18:30 UTC

↓ Download file

# PATH A: the customer has an Email Love design system

## Contents

- A1: Inventory the library
- A2: Choose components
- A3: Duplicate and vet a donor root
- A4: Assemble by instancing
- A5: Handle a confirmed component gap

Instance-only discipline. The components are the ground truth; your job is selection, copy,
and imagery.

## A1: Inventory the library properly

A shallow inventory produces every email as a re-skin of one existing campaign. A real one
produces emails whose sections fit their content.

1. **Enumerate the components.** From `list_components` if the Email Love MCP is connected
   (it returns them grouped by the customer's own categories, which are the names you should
   reuse everywhere), otherwise by listing every page in the file and searching each for
   COMPONENT and COMPONENT_SET nodes, one call per page. Email Love design systems usually
   keep the library on dedicated pages (Heroes, Cards, Lists, Copy Blocks, Data, Footer)
   separate from the campaigns page.
2. **Read the file's structure, and leave it as you found it.** A library built through Email
   Love's migration route carries a prescribed page frame, and recognizing it saves you a lot of
   searching:

   ```
   Cover
   Getting Started
   --- Foundations
   Foundations
   Type
   Buttons
   --- Components
   one page per component category, in the customer's own category names
   --- Templates
   Campaigns
   ```

   The `---` pages are dividers standing in for the page folders Figma does not have, so they are
   empty on purpose. A file shaped like that is telling you where everything lives: the color and
   spacing tokens on Foundations, the type ramp on Type, the button components on Buttons, the
   modules on the category pages, and finished emails on Campaigns beside the existing ones, which
   is where this build belongs. **Read Cover and Getting Started when they exist.** Cover states
   the email width the system is built at, and that is the width your root frame has to match.
   Getting Started states the file's own conventions, which outrank any habit of yours.

   **Respect the structure you find rather than imposing one.** Do not rename a page, do not
   reorder the page list, do not add a Cover or a divider that is not there, and do not open a new
   page for this build when the file already has the page this work belongs on. Plenty of libraries
   look nothing like the frame above, because they were built by hand or built before it existed.
   That is not a defect for you to fix mid-build: reshaping someone's library while they asked for
   one email is a change they did not ask for. Say in one line what shape you found, put your work
   where that shape puts it, and if the file genuinely has nowhere for a finished email, ask before
   creating a page.
3. **Use the tokens the file already has.** Where the library carries Figma variables, a semantic
   set (names like `color/bg/page`, `color/text/primary`, `spacing/md`) usually sits on top of a
   primitive set named by value. For anything you create outside an instance, the root frame fill
   above all, bind to the SEMANTIC variable rather than typing a hex: that is how the file was
   built, and it is what keeps a later brand-color change to one edit. Never repoint or rename a
   token, and never bind to a primitive directly. Instances are already bound, so leave them
   alone. Two things a variable cannot reach: the root frame's eight theme keys, which are shared
   plugin data strings and carry literal hex (R2.1), and `fontSize` or `lineHeight`, which are not
   bindable, so type comes from the file's text styles instead. Where there are no variables, take
   the values from the palette as usual.
4. **Study 2 or 3 of their past emails.** Screenshot and read the frames the user named as
   their best, or the most recent. Learn voice, copy length, section rhythm, imagery habits,
   and footer conventions, including whether the footer uses an `mj-raw` token block. These
   are also your donor candidates for the root frame.
5. **Report the palette** to the user in one compact list.

## A2: Ask who picks the components

Every build, unless the Step 1 questions already answered it or the brief already dictates
the exact sections (then confirm that list in one line). If they defer, pick by content fit
and say what you chose and why.

Codex runs in a terminal, so the strongest picker available to you is the canvas itself:

- **Pick in Figma (preferred here).** Lay out a temporary top-level frame named
  "Component menu, delete me" beside the build area, containing labeled instances of the 3 or
  4 candidates for the current section. Ask the user to click their choice in Figma and say
  "picked". Read `figma.currentPage.selection`, confirm what you saw by name, and move to the
  next section. Delete the menu frame when the picking is done. The user judges components at
  full size and never squints at a thumbnail.
- **Numbered list fallback.** If the user would rather not switch to Figma, list candidates
  per section with a one-line note on fit and your recommendation tagged, and let them answer
  with numbers. You can also save `get_screenshot` output to local files and tell the user
  the paths so they can open them.

Compose with a clear split: past emails teach voice and polish, the palette plus the content
decides structure. Statistics want a stats card, steps want a list component, social proof
wants a testimonial card, a single announcement wants a hero plus copy block. If your section
stack is identical to an existing campaign's, you matched the donor rather than the content.

## A3: Root frame from a donor, then vet what you inherited

**Duplicate an existing Email Love email frame.** That gives you a root carrying every plugin
setting (marker, theme colors, subject and preheader slots). The donor's value is its root
settings, not its body:

- Keep inherited sections only if they are component instances (or a raw footer block).
- **A hand-built section inside the donor** (a plain frame that is not an instance) is
  invisible to the exporter and must be replaced with a library instance or removed. This is
  the most common way an inherited email silently loses content.
- Delete inherited sections you do not need and instantiate fresh ones from the palette.

If no donor exists in the file, build the root per
[shared-rules.md](shared-rules.md#root-frame) and append the instances straight into it, in
order. Never wrap an instance in a frame of your own: an untagged frame between the root and
an instance flattens everything below it into one image.

## A4: Assemble by instancing

The complete list of edits you may make to an instance:

- **Text content.** Load the node's current fonts, await, then mutate. Read the fonts off the
  node rather than assuming. Skipping the font load is the most common build failure.
- **Image fills** on the component's image blocks, at their existing dimensions, 2x
  resolution, watching crop and focal point. The plugin picks up image fills at export and
  handles hosting. If a geometry write inside the instance is ever unavoidable (an image band
  whose height has to match a photo's aspect), R0.8 is the rule you need: `resize()` on a node
  nested inside an instance silently does nothing, no error and the value unchanged on
  read-back, so FILL the descendant chain and resize the INSTANCE, and read every geometry
  write back.
- **Component properties**: toggle booleans to hide optional regions, swap instance-swap
  slots, set text properties. Because the plugin exports what is visible, a boolean that
  hides a region genuinely removes it from the sent email.
- **Plugin data**: `href`, `altText`, and mobile style keys, per
  [shared-rules.md](shared-rules.md).

Everything else is forbidden: **never detach**, never add, delete, or reparent layers inside
an instance, never retag anything inside it, never change its internal auto-layout, never
apply a fill to a structural frame inside it. Detaching severs the structure the exporter
reads, and restructuring internals reintroduces exactly the hand-authoring this workflow
forbids.

**Naming inside an instance is not your problem, so leave it alone.** A component the plugin
built carries the plugin's own naming on every node, the MJML tag in plugin data and the
friendly display name on the layer, and an instance surfaces the main component's plugin
data. Do not rename layers inside an instance to "clarify" them, and do not write plugin data
onto instance internals. The R6 naming rules in
[render-components-validation.md](render-components-validation.md) are for nodes you create,
and on Path A the only node you create is the root. If a component's internals look wrong,
that is a design-system fix in the source component, not something to patch per instance.

Also: **one visible CTA button per email** unless the user asks otherwise (hide competing
buttons via component properties); **leave final CTA URLs to the plugin** unless the user
gave you real URLs; **placeholder missing imagery** as flat gray fills at the existing
dimensions and say so in the report; **lay multiple emails side by side**, each in its own
frame, so the team can review a sequence at a glance.

## A5: When no component fits, stop

In order:

1. Reconsider. Most "no component fits" moments are a copy problem, not a component problem.
   Fit the content to the closest component and check with the user.
2. Ask the user directly, showing what you have and what the section needs. They often know a
   component you did not find, on a page you did not check.
3. Only if they confirm nothing exists: build that one section through **Path B** (generate
   and transcribe, not freehand), then offer to save it into their design system so it exists
   next time (see B6). A gap-fill section is a design-system asset by definition, which means
   it is a **module**, not a tiny email: build it as an `mj-wrapper` COMPONENT with **no**
   `nodeType = 'mainFrame'` marker, friendly layer names inside, the module name on the
   component itself, and properties for the parts that will change (R2.2 in
   [render-geometry.md](render-geometry.md)). It should be indistinguishable from the
   components around it.

Never assemble the section by hand, and never flatten it to an image to make the problem go
away. An image in place of a section is a decision for the customer to make, not for you.

SHA-256: 8fd4236e0136c58bbe80d2952300938555cf8f0985b9904990ad6570a4c1066c