From b138c8b1fc5938b83ed1251e3590666b9c62431c Mon Sep 17 00:00:00 2001 From: Harsh Mehta Date: Thu, 20 Aug 2026 17:00:04 +0530 Subject: [PATCH 01/23] feat(aem): add figma-to-content skill for EDS page migration from Figma Orchestrates existing public EDS skills (da-auth, da-content, block-collection-and-party, snowflake, content-driven-development, testing-blocks) to turn a Figma frame into a Document Authoring content page. Reads the design via a Figma MCP, resolves each section to an existing block / new isolated block / default content (annotation-first, else inferred and confirmed), generates DA-compliant body-fragment HTML, and deploys via the DA Source API + preview. No proprietary tooling required. Draft (v0.1.0); annotation contract still to be locked with adopters. Co-Authored-By: Claude Opus 4.8 --- .../skills/figma-to-content/.releaserc.json | 1 + .../skills/figma-to-content/CHANGELOG.md | 7 + .../skills/figma-to-content/README.md | 85 ++++ .../skills/figma-to-content/SKILL.md | 458 ++++++++++++++++++ .../skills/figma-to-content/package.json | 5 + .../references/annotation-contract.md | 151 ++++++ 6 files changed, 707 insertions(+) create mode 100644 plugins/aem/edge-delivery-services/skills/figma-to-content/.releaserc.json create mode 100644 plugins/aem/edge-delivery-services/skills/figma-to-content/CHANGELOG.md create mode 100644 plugins/aem/edge-delivery-services/skills/figma-to-content/README.md create mode 100644 plugins/aem/edge-delivery-services/skills/figma-to-content/SKILL.md create mode 100644 plugins/aem/edge-delivery-services/skills/figma-to-content/package.json create mode 100644 plugins/aem/edge-delivery-services/skills/figma-to-content/references/annotation-contract.md diff --git a/plugins/aem/edge-delivery-services/skills/figma-to-content/.releaserc.json b/plugins/aem/edge-delivery-services/skills/figma-to-content/.releaserc.json new file mode 100644 index 00000000..c4109d12 --- /dev/null +++ b/plugins/aem/edge-delivery-services/skills/figma-to-content/.releaserc.json @@ -0,0 +1 @@ +{"extends": "../../../../../release.config.cjs"} \ No newline at end of file diff --git a/plugins/aem/edge-delivery-services/skills/figma-to-content/CHANGELOG.md b/plugins/aem/edge-delivery-services/skills/figma-to-content/CHANGELOG.md new file mode 100644 index 00000000..95474452 --- /dev/null +++ b/plugins/aem/edge-delivery-services/skills/figma-to-content/CHANGELOG.md @@ -0,0 +1,7 @@ + + +# figma-to-content + +Draft (v0.1.0) — not yet released. The changelog will be generated on the +first semantic-release once the skill is merged into `adobe/skills`. diff --git a/plugins/aem/edge-delivery-services/skills/figma-to-content/README.md b/plugins/aem/edge-delivery-services/skills/figma-to-content/README.md new file mode 100644 index 00000000..49042bdc --- /dev/null +++ b/plugins/aem/edge-delivery-services/skills/figma-to-content/README.md @@ -0,0 +1,85 @@ +# figma-to-content + +Turn a **Figma design** into an Adobe Edge Delivery Services +(EDS / AEM / Franklin / Helix) **content page** and deploy it to Document +Authoring (`da.live`). Works whether the design is **annotated** (each section +declares its block) or **not** ("just migrate this page") — when annotations +are absent the skill infers each section's mapping against the project's +existing blocks and confirms the plan with you before building. + +Runs standalone in plain Claude / Coworker with just a **Figma MCP** (to read +the design) and a **DA IMS token** (to write content) — no proprietary web app +or migration tooling required. + +## Two paths + +The skill classifies each section of the design and follows the matching path: + +- **Content only** — every section maps to a block the project **already + has**. Author content into existing blocks and deploy. No code changes. +- **Content + code** — a section needs a block the project **does not have**, + or an existing block matches structurally but its **styling diverges**. The + block is created as a **new, isolated block** (via the public block-building + skills), its code is pushed, then content is authored and deployed. **Existing + blocks are never skinned** — the only global change is retargeting the + project's design tokens to the design system. + +A single design usually mixes both, plus default content. + +## How it works + +``` +Figma frame (annotations optional) + │ Figma MCP (get_metadata, get_design_context, get_screenshot, get_variable_defs) + ▼ + section inventory ──► resolve each section (annotation-first, else infer) + │ ├─ existing block (structure + visual fit) ─► author content + │ ├─ needs one / look diverges → new block ───► build isolated block (snowflake / + │ │ content-driven-development), push code + │ └─ default content ──────────────────────────► author prose/media, no block + ▼ + resolution plan ──► confirm with user (ask on low-confidence) + ▼ + content/.html ──► PUT admin.da.live/source ──► POST admin.hlx.page/preview + (da-content contract) (+ Code Sync first, for new blocks) + ▼ + da.live edit URL + aem.page preview URL +``` + +It **orchestrates existing public skills** rather than reinventing them — the +net-new logic is reading the Figma design, resolving each section to a block +(from annotations, or inferred by structure + visual fit and confirmed with the +user), and mapping content into it. DA rules, auth, and block-building all defer +to the skills below. + +## What's included + +``` +skills/figma-to-content/ +├── SKILL.md Entry point (agent reads first) +├── README.md This file +├── package.json +└── references/ + └── annotation-contract.md The annotation spec (optional path; proposed) +``` + +## Depends on (public skills) + +- [`da-auth`](../da-auth) — DA IMS token +- [`da-content`](../da-content) — DA HTML rules + Source API + preview/publish + media +- [`block-collection-and-party`](../block-collection-and-party) — whether a block exists + its authoring model +- [`block-inventory`](../block-inventory) — survey the available block palette +- [`content-modeling`](../content-modeling) — design a content model for a new block +- [`content-driven-development`](../content-driven-development) — full dev workflow for a reusable new block (invokes [`building-blocks`](../building-blocks), [`testing-blocks`](../testing-blocks)) +- [`testing-blocks`](../testing-blocks) — render a block + screenshot and compare to the design; reused directly for the **reuse gate's visual check** +- [`snowflake`](../snowflake) — turn a bespoke Figma-derived section into an isolated block + +## Status + +**Draft (v0.1.0).** Before v1.0.0, close the open questions in `SKILL.md` — +chiefly the **annotation contract** (see +[`references/annotation-contract.md`](./references/annotation-contract.md)), +which must be agreed with the design team. + +Intended destination: +`adobe/skills → plugins/aem/edge-delivery-services/skills/figma-to-content`. diff --git a/plugins/aem/edge-delivery-services/skills/figma-to-content/SKILL.md b/plugins/aem/edge-delivery-services/skills/figma-to-content/SKILL.md new file mode 100644 index 00000000..384abe41 --- /dev/null +++ b/plugins/aem/edge-delivery-services/skills/figma-to-content/SKILL.md @@ -0,0 +1,458 @@ +--- +name: figma-to-content +description: "Use this to turn a Figma design into an AEM Edge Delivery Services (EDS / AEM / Franklin / Helix) content page in Document Authoring (DA, da.live). Triggers: \"build this Figma frame in EDS\", \"turn this Figma design into a DA page\", \"publish this design to da.live\", or providing a figma.com URL for a page to author. Reads the Figma frame and its annotations via a Figma MCP, maps each section to an EDS block (or to default content), generates DA-compliant body-fragment HTML, and deploys it via the DA Source API (PUT admin.da.live/source) + preview (POST admin.hlx.page/preview). Handles two cases: (1) content-only — every section maps to a block that already exists in the project or to default content; (2) content + code — a section needs a block that does not exist (or an existing block matches structurally but its styling diverges), which is created as a new, isolated block via the block-building skills without modifying existing blocks (project-level design-token theming aside)." +license: Apache-2.0 +metadata: + version: "0.1.0" +--- + +# figma-to-content — Figma design → EDS content page in DA + +Read a Figma frame, assemble a page from EDS blocks and default content, and +publish it to Document Authoring. Runs with a **Figma MCP** (to read the +design) and a **DA IMS token** (to write content) — no proprietary tooling +required. + +This skill **orchestrates existing skills**; it does not reimplement DA rules, +block knowledge, or block-building. When a rule is unclear, read the +referenced skill. + +## Two paths + +Classify each section of the design, then follow the matching path: + +- **Content only** — every section maps to a block that **already exists** in + the target project, or to **default content** (plain headings/paragraphs/ + images/buttons — no block). Author content and deploy. No code changes. +- **Content + code** — a section needs a block the project **does not have**, + or an existing block matches structurally but its **styling diverges** (the + look lives in block-specific CSS you'd have to edit). Create it as a **new, + isolated block** (via the block-building skills), push the code, then author + content and deploy. **Never skin an existing block or add per-section rules + to global CSS** — new, additive blocks only. (Retargeting the project's + global design tokens is a separate, allowed theming step; see Guardrails.) + +A single design usually mixes all three (known blocks + default content + one +or two new blocks). + +## When to use + +- The user has a Figma frame representing a page and wants it as an EDS page in DA. + The common case: a customer **already on EDS**, with their own blocks, gets a + new design for a new page — some sections reuse existing blocks, some need new + ones. +- **Annotations are recommended but optional.** If the frame is annotated (each + section declares its block / default content / new block — see + [references/annotation-contract.md](./references/annotation-contract.md)), + those annotations are authoritative. If it is **not** (e.g. the user just says + "migrate this page"), the skill **infers** each section's mapping against the + project's existing block palette and **confirms the plan** before building, + asking whenever a section is ambiguous (Phase 2). + +### When NOT to use + +- **Redesign / restyle an existing EDS site**, or convert arbitrary generated + static HTML (Mobirise, Relume, v0, exported Figma HTML). Use **snowflake**. +- **Universal Editor or AEM Cloud Service (Java/OSGi/JCR).** Out of scope. + +## Related skills — orchestrated by this one + +| For | Use skill | +|---|---| +| DA IMS token (`DA_TOKEN`) | **da-auth** | +| DA body-fragment HTML rules, Source API, preview/publish, media | **da-content** | +| Whether a block exists + its authoring model & examples | **block-collection-and-party** | +| Surveying the whole available block palette | **block-inventory** | +| Designing a content model for a **new** block | **content-modeling** | +| Building a **new** block (full dev workflow) | **content-driven-development** (invokes **building-blocks**, **testing-blocks**) | +| Rendering a block + **visual comparison to the design** (the reuse gate) | **testing-blocks** (browser/Playwright screenshot + "compare implementation to design") | +| Turning a bespoke Figma-derived section into an isolated block | **snowflake** | + +The DA-write contract in Phase 5 is the same one **da-content** documents +(see its `references/html-content.md` and `references/platform.md`). + +--- + +## Inputs (gather before Phase 1; ask if missing — never guess) + +- **Figma reference** — file key + node id of the page frame (from the + figma.com URL or the current Figma MCP selection). +- **Target project** — a local checkout of the EDS project repo (needed to see + existing blocks under `blocks/`, and required for the content+code path to + add block code). Its GitHub `{owner}`/`{repo}` and the deploy `{branch}`. +- **DA location** — `daOrg`, `daRepo` (the DA namespace), page `PATH` (no + extension, lowercase/dash only — see da-content platform rules). In the + standard EDS+DA setup `daOrg`/`daRepo` **equal** the GitHub `{owner}`/`{repo}`; + confirm, because Phase 5 writes to `daOrg`/`daRepo` but previews/renders on + the GitHub `{owner}`/`{repo}`/`{branch}`. +- **`DA_TOKEN`** — via **da-auth** (cached at `~/.aem/da-token.json`, valid + ~1h). A `401` with an empty body means it expired → re-auth. + +--- + +## Phase 1 — Read the Figma design (Figma MCP) + +Use a Figma MCP (Claude desktop / IDE / external). **Introspect the actual tool +schemas** — signatures differ between MCP implementations (the local Dev Mode +server often works off the current selection and may not take a `fileKey`; the +remote/desktop server takes `fileKey` + optional `nodeId`). The tools you need, +by capability: + +- **Structure** (e.g. `get_metadata`) — the frame's section/layer tree; node + ids, names, positions, sizes. Enumerate top-level sections in visual order + (sort by `y`). Usually `fileKey` required, `nodeId` optional. +- **Visual** (e.g. `get_screenshot`) — a per-section reference image to + sanity-check the block/content mapping. +- **Content & assets** (e.g. `get_design_context`) — text, links, and image + asset download URLs for a node. For the content+code path this also provides + the layout/structure a new block must reproduce. +- **Design tokens** (e.g. `get_variable_defs`) — colors, spacing, type. Read + annotation values and, for new blocks, source token values. + +Produce an ordered **section inventory**: `{ sectionNodeId, annotation, +screenshot, content }`. Read annotations per +[references/annotation-contract.md](./references/annotation-contract.md). + +> Site chrome (nav bar, footer) is usually **not page body** — in EDS it is +> sourced from separate `/nav` and `/footer` documents via the header/footer +> blocks. Don't author it into the page unless the user asks. + +--- + +## Phase 2 — Resolve each section + +Every section resolves to exactly one of: **existing block** (→ 3A), +**default content** (→ 3C), or **new block** (→ 3B). How that decision is +reached depends on whether the section is annotated. + +### 2.0 — Know the project's block palette (always) + +Before resolving anything, enumerate what the project **already has**: +`ls -d blocks/*/` plus **block-inventory** / **block-collection-and-party** for +each block's **authoring model** (row/cell structure, variants) **and a +rendered example** — the block's `liveExampleUrl` when it comes from the Block +Collection, or the project's own block rendered at `localhost:3000`. That +rendered example is the "block side" of the 2.1 / Phase 3A visual check. This +is the reuse-candidate set — essential when the customer is already on EDS with +their own blocks. + +### 2.1 — Resolve each section (annotation-first, else infer) + +**If the section is annotated** (see +[references/annotation-contract.md](./references/annotation-contract.md)), the +annotation is **authoritative**: named block that exists → existing block (3A); +marked `new` (or absent-and-user-confirmed) → new block (3B); plain prose/media +→ default content (3C). + +**If it is not annotated** (e.g. "just migrate this page"), **infer** the +mapping — do not dump it as unresolved: + +1. Plain prose/media (headings, paragraphs, images, a standalone link) with no + repeating structure → **default content** (3C). +2. Otherwise match it against the 2.0 palette using the **reuse gate (structure + AND visual, Phase 3A)**: does its content model fit an existing block *and* + does that block's rendered example — under the project theme — look like the + section, allowing only token differences and variants the block defines? + - **Both fit → existing block** (3A). + - **Structure fits but the look diverges** (bespoke card/layout/decoration + the block's CSS can't produce without editing it), **or nothing fits → + new block** (3B). +3. Attach a **confidence** to every inference: `high` (clear reuse match, or + clearly novel) or `low` (structure fits but styling is borderline; two + blocks plausibly fit; new-variant-vs-new-block; content model ambiguous). + +### 2.2 — Confirm the plan before deploying (never deploy a guess) + +Present a **resolution plan** — one line per section: decision (reuse `X` / +default content / new block `Y`), confidence, and a one-clause rationale. + +- **High-confidence sections auto-proceed through building** (Phases 3–4) — + don't block on them. +- **Stop and ask before building** any `low`-confidence section or genuine + ambiguity, offering the concrete choice (reuse this block vs. new block; + which block; new variant vs. new block). Wait for the answer. +- **Pause once before deploying (Phase 5)** whenever the plan contains any + **inferred** (unannotated) mapping: show the final plan and get a single + confirmation before the da.live write/preview — deploy is outward-facing and + hard to reverse. Skip this pause only if the user pre-authorized an + unattended run. A **fully annotated** plan needs no pause — the annotations + are the authorization. +- The user can override any line. + +Never silently drop a section, and never deploy an **inferred** mapping the +user has not seen. + +--- + +## Phase 3A — Map content into an EXISTING block + +**Reuse gate — structure AND visual.** An existing block is a valid target +only when the section both (a) **fits the block's authoring model** (its +row/cell structure and field types) *and* (b) **matches the block's rendered +appearance** under the project theme, using only tokens and variants the block +already defines. Structural fit alone is **not** enough: if the section's +visual identity — bespoke layout, corner radius, shadow, decorative treatment — +lives in that block's own CSS, you cannot reproduce it without editing the +block (forbidden), so route the section to **Phase 3B** (new block). Global, +token-level differences (palette, fonts, type scale) do **not** break reuse — +they are absorbed once by retargeting the project's design tokens (see +Guardrails). **How to run the visual check — reuse testing-blocks, don't invent +one:** get a rendered example of the candidate block — its `liveExampleUrl` +(block-collection-and-party / block-inventory) or the project's own block +rendered at `localhost:3000` with representative content — then follow +**testing-blocks**' browser/Playwright-MCP screenshot pass (mobile/tablet/ +desktop) and its "compare implementation to design" step, comparing that +screenshot against the Figma **section screenshot** from Phase 1. Divergence +beyond what the token retheme explains ⇒ new block, not reuse. + +Once the gate passes, learn the block's authoring model from +**block-collection-and-party** (its examples show the row/cell structure and +variants). Then pour the Figma content into that structure: + +- **Text** → matching cells; preserve heading levels from the design. +- **Variants** → extra class tokens on the block (e.g. `cards highlight`). + Only apply a variant the block actually defines. (Adding a *new* variant = + modifying an existing block = Phase 3B, not 3A.) +- **Links/buttons** → a **standalone link** (the only content of its + paragraph) auto-promotes to a button; wrap in `` for a primary + button, `` for secondary. Do not add `target="_blank"` (decoration + handles external links). *(da-content html-content.md §8)* +- **Images** → Phase 4 (they need real URLs). + +--- + +## Phase 3B — Create a NEW block (content + code) + +Only for sections in bucket 3. **Guardrails (strict):** + +- Create **new, isolated block folders** only (`blocks//`). +- **Do NOT** skin this block by editing an existing block, `scripts.js`, or + `head.html`, or by adding block-specific rules to global CSS — keep it + self-contained under `blocks//`. *(Retargeting the project's + global design tokens in `styles/styles.css` — the `:root` custom properties + and base typography — is a separate, allowed project-theming step, not part + of building this block; see Guardrails.)* +- New block **names and variant tokens** must obey EDS block-name rules + (da-content html-content.md §3.3): lowercase alphanumeric + single hyphens, + **no underscores, no double dashes, must not start with a digit** + (`pricing-table` ✓, `pricing_table` / `2col` / `promo--wide` ✗). Names must + be unique and not collide with existing blocks. + +Choose the build route: + +- **snowflake** — best for reproducing a **bespoke Figma section** as an + independent, self-contained EDS block (per-block CSS scoped under the block + class). Matches the "isolated new block, don't touch globals" constraint. +- **content-driven-development** — the full dev workflow for a **reusable** + block; it invokes **content-modeling** (design the authoring model from the + Figma structure/tokens) then **building-blocks** and **testing-blocks**. + +Use the Figma design context/tokens from Phase 1 as the source of truth for +layout and styling. New-block **CSS must target structure, not authored +classes** — inline wrappers like `` are stripped inside block +cells at delivery (da-content html-content.md §3.9), so a class you emit in a +cell will not survive. + +The new block's code must be **committed and pushed to the deploy branch on +GitHub and built by Code Sync** before the page can render it — see Phase 5 +(content+code). + +--- + +## Phase 3C — Author DEFAULT CONTENT (no block) + +For bucket 2, emit standard document elements directly inside the section +`
` (see Phase 4 skeleton) — no block wrapper: + +- Headings `

`–`

` (preserve levels), paragraphs, lists, images. +- A **standalone link** in its own `

` becomes a button (``/`` + for primary/secondary) — same rule as 3A. +- Do **not** add `class`, `id`, or `style` — decoration adds them at delivery. + +*(da-content html-content.md §6)* + +--- + +## Phase 4 — Generate DA body-fragment HTML (da-content) + +Emit a **body fragment** (not a full HTML document) per **da-content**. Write +one file per page to `content/.html`. + +**Mandatory skeleton** (da-content html-content.md §1–§2): wrap everything in +`` with an (empty) `

`/`