All AEM as a Cloud Service skills: component development, Workflow model design, development, triggering, launchers, debugging, and triaging, Dispatcher config authoring, advisory, incident response, performance tuning, and security hardening, Rapid Development Environment (RDE) deployment, log inspection, snapshots, and troubleshooting via `aio aem rde`, Jackrabbit Oak query/index tuning (property, Lucene index definitions), and extracting/inventorying JCR queries (XPath, SQL2, QueryBuilder) across a codebase.
85%
Does it follow best practices?
Run evals on this skill
Adds up to 20 points to the overall score
Low
Low-risk findings worth noting
Rules for fetching Figma designs via the Figma MCP server and translating them into AEM component HTML, CSS, and JS files with pixel-perfect visual fidelity.
The Figma design integration requires the plugin-figma-figma MCP server to be enabled and connected in the IDE.
| MCP Server Status | Action |
|---|---|
| Available | Proceed with Rules 1–9 below |
| NOT available | Skip Figma integration entirely. Create basic structural CSS using clientlib-patterns.md instead. Inform the user: "Figma design integration requires the plugin-figma-figma MCP server to be enabled in your IDE. Please enable it and try again, or provide a mockup image as an alternative." |
Activate Figma design integration when the user provides ANY of these URL patterns:
| URL Pattern | Type |
|---|---|
figma.com/design/:fileKey/:fileName?node-id=:nodeId | Standard design file |
figma.com/design/:fileKey/branch/:branchKey/:fileName | Branch design file |
figma.com/make/:makeFileKey/:makeFileName | Figma Make file |
fileKey: The segment immediately after /design/ (or use branchKey for branch URLs)nodeId: The node-id query parameter value with - converted to :
node-id=4-103 → nodeId = "4:103"node-id=12-456 → nodeId = "12:456"branchKey (not fileKey) as the file identifiermakeFileKey as the file identifierInput: https://www.figma.com/design/nIRsoy3t8Jmu9NHSBvIgG4/Untitled?node-id=4-103&m=dev
Result: fileKey = "nIRsoy3t8Jmu9NHSBvIgG4", nodeId = "4:103"
Input: https://www.figma.com/design/abc123/branch/def456/MyFile?node-id=10-20
Result: fileKey = "def456", nodeId = "10:20"
Input: https://www.figma.com/make/xyz789/MyMakeFile
Result: fileKey = "xyz789", nodeId = "" (root)get_design_contextMCP Server: plugin-figma-figma
Tool Name: get_design_context
This is the PRIMARY and PREFERRED tool for fetching component designs. Always use this first.
{
"server": "plugin-figma-figma",
"toolName": "get_design_context",
"arguments": {
"nodeId": "<extracted-node-id>",
"fileKey": "<extracted-file-key>",
"clientLanguages": "html,css,javascript",
"clientFrameworks": "htl"
}
}| Parameter | Required | Value |
|---|---|---|
nodeId | Yes | Extracted from URL, format "123:456" |
fileKey | Yes | Extracted from URL |
clientLanguages | Recommended | "html,css,javascript" (for AEM projects) |
clientFrameworks | Recommended | "htl" (for AEM HTL templates) |
excludeScreenshot | No | Default false — ALWAYS include screenshot for visual reference |
forceCode | No | Only set true if initial response returned metadata-only due to size |
Use these when get_design_context alone is insufficient:
| Situation | Fallback Tool | Purpose |
|---|---|---|
| Large/complex frame returns metadata-only | get_design_context with forceCode: true | Force code generation for oversized nodes |
| Need structural overview of a page | get_metadata | Get node tree structure, then call get_design_context on specific sub-nodes |
| Need visual reference only | get_screenshot | Get a rendered screenshot without code output |
| Design uses Figma variables/tokens | get_variable_defs | Get reusable design token variable definitions (colors, spacing, etc.) |
If the Figma node is a full page or complex frame:
get_metadata to see the node treeget_design_context on that sub-node for better, more focused resultsWhy? Large selections slow the tools down, cause errors, or result in incomplete responses. Smaller sections produce faster, more reliable results.
The get_design_context tool returns:
| Response Element | Use For |
|---|---|
| JSX/HTML structure | Derive semantic HTML element hierarchy for HTL |
Tailwind inline classes (text-[#hex], text-[Npx], etc.) | Extract exact CSS values (colors, spacing, fonts, sizes) |
className values | Map to BEM CSS class names (cmp-{name}__{element}) |
data-name attributes | Understand Figma layer naming for semantic meaning |
data-node-id attributes | Reference back to specific Figma nodes if further detail needed |
| Screenshot image | Visual truth — compare your output against this |
| Image/SVG constants | Temporary placeholder URLs (do NOT hardcode in templates) |
data-name or data-node-id attributes in production HTLclassName (React syntax) — convert to class (HTML){variable}, && conditional rendering, .map())From Tailwind classes like text-[#58181d], bg-[#dc6e52], border-[#aaa]:
color: #58181d;, background-color: #dc6e52;From patterns like font-['Baskerville:Regular',sans-serif] and text-[32px]:
| Figma Token Pattern | CSS Property | Extraction Rule |
|---|---|---|
font-['Family:Weight',fallback] | font-family | Use exact family name, add proper fallback stack (see table below) |
| Weight qualifier in font name (Regular, Bold, Light, Semi Bold) | font-weight | Map: Light=300, Regular=400, Medium=500, Semi Bold=600, Bold=700, Extra Bold=800 |
text-[Npx] | font-size | Use exact pixel value |
leading-[Npx] | line-height | Use exact pixel value |
not-italic / italic | font-style | Map directly to normal / italic |
tracking-[N] | letter-spacing | Use exact value with unit |
whitespace-nowrap | white-space | nowrap |
uppercase / lowercase / capitalize | text-transform | Map directly |
Always add a fallback stack when extracting font families from Figma:
| Figma Font | CSS font-family Value |
|---|---|
| Baskerville | Baskerville, 'Baskerville Old Face', 'Book Antiqua', Georgia, serif |
| Helvetica | Helvetica, Arial, sans-serif |
| Georgia | Georgia, 'Times New Roman', Times, serif |
| Roboto / Open Sans / Lato / Montserrat | {Font}, 'Helvetica Neue', Arial, sans-serif |
| Playfair Display / Garamond | {Font}, Georgia, serif |
| Other/custom | Use exact name, add generic fallback (serif / sans-serif / monospace) |
From Tailwind classes like left-[40px], top-[30px], pt-[20px], gap-[16px]:
| Tailwind Pattern | CSS Property |
|---|---|
p-[N], px-[N], py-[N], pt-[N], pr-[N], pb-[N], pl-[N] | padding / padding-top / padding-right etc. |
m-[N], mx-[N], my-[N], mt-[N], mr-[N], mb-[N], ml-[N] | margin / margin-top / margin-right etc. |
gap-[N] | gap |
w-[N], min-w-[N], max-w-[N] | width, min-width, max-width |
h-[N], min-h-[N], max-h-[N] | height, min-height, max-height |
left-[N], top-[N], right-[N], bottom-[N] | Convert to padding/margin — NOT absolute position (see Rule 5) |
Figma frame dimensions are always outer dimensions that include padding. When a Figma frame reports 500×278px with 40px horizontal and 48px vertical padding, the 500×278px measurements already include the padding — they are not content-only measurements.
CSS defaults to content-box, which adds padding on top of width/height. This causes rendered components to be larger than the Figma design. To match Figma's sizing model, apply box-sizing: border-box to any element whose width, height, min-width, or min-height is derived from a Figma frame size. This ensures padding is subtracted from the specified dimensions rather than added on top.
/* Figma frame: 500×278px with 40px horizontal, 48px vertical padding */
.cmp-hero {
box-sizing: border-box;
min-height: 278px;
width: 100%;
max-width: 500px;
padding: 48px 40px;
}Why this matters: Without
border-box, the element above would render as 580px wide (500 + 40 + 40) and 374px tall (278 + 48 + 48) instead of the intended 500×278px.
| Figma Token Pattern | CSS Property |
|---|---|
border-[#hex] | border-color: #hex; |
border-solid / border-dashed | border-style: solid; / border-style: dashed; |
border-[Npx] / border-N | border-width: Npx; |
rounded-[Npx] | border-radius: Npx; (round to nearest integer if decimal) |
shadow-[...] | box-shadow: ...; |
opacity-[N] | opacity: N; |
| Tailwind Pattern | CSS Property |
|---|---|
flex | display: flex; |
flex-col | flex-direction: column; |
flex-row | flex-direction: row; |
items-center | align-items: center; |
items-start | align-items: flex-start; |
justify-center | justify-content: center; |
justify-between | justify-content: space-between; |
shrink-0 | flex-shrink: 0; |
grow | flex-grow: 1; |
overflow-hidden | overflow: hidden; |
get_design_context responsedata-name attributes and visual hierarchy<h2>, link containers → <a>, image wrappers → <img>, text blocks → <p> or <div>cmp-{component-name}__{element}${model.propertyName}data-sly-test, data-sly-use, data-sly-list@ context='html' for rich text, @ extension='html' for internal URLs, @ context='attribute' for attribute values| Figma/React Pattern | AEM HTL Equivalent |
|---|---|
className="..." | class="cmp-{name}__{element}" |
{variable} or {`text`} | ${model.property} |
<p> with heading font/size | <h2> or <h3> (use correct semantic level) |
<div> wrapping a link shape | <a href="..." class="cmp-{name}__link"> |
<img src={imageConst} /> | <img src="${model.image}" alt="${model.title @ context='attribute'}" loading="lazy"/> |
{condition && <Element>} | <sly data-sly-test="${model.condition}"><Element>...</Element></sly> |
{items.map(item => <El/>)} | <sly data-sly-list.item="${model.items}"><El>...</El></sly> |
style={{ color: '#fff' }} | Move to CSS file, reference via BEM class |
onClick={handler} | Move to component JS file, bind via data-cmp-is selector |
Figma often outputs absolute positioning (absolute, left-[40px], top-[30px]).
ALWAYS convert to proper CSS layout:
| Figma Pattern | Convert To |
|---|---|
| Vertically stacked absolute elements | display: flex; flex-direction: column; with gap or margin |
| Horizontally placed absolute elements | display: flex; flex-direction: row; with gap or margin |
| Grid-like arrangement of absolutes | display: grid; grid-template-columns: ...; |
| Single centered element | margin: 0 auto; or flexbox centering |
| Overlapping elements (genuine overlay) | position: absolute; (only for true overlays/decorations) |
NEVER use
position: absolutewith pixel offsets from Figma output for normal layout. Only use absolute positioning for genuine overlay or decorative elements.
Use data-name attributes from the Figma response to determine the correct HTML element:
data-name Pattern | HTML Element |
|---|---|
| "Heading", "Title", "H1"-"H6" | <h1>–<h6> (match heading level to visual hierarchy) |
| "Paragraph", "Body", "Description", "Copy" | <p> or <div> (for rich text: <div> with @ context='html') |
| "Link", "Button", "CTA" | <a> (with href) or <button> |
| "Image", "Photo", "Thumbnail" | <img> (with alt, loading="lazy") |
| "List", "Items" | <ul> + <li> or <ol> + <li> |
| "Container", "Wrapper", "Section" | <div> or <section> |
| "Card", "Tile" | <article> |
| "Navigation", "Nav" | <nav> |
ui.apps/src/main/content/jcr_root/apps/[project]/clientlibs/clientlib-{component-name}/css/{component-name}.cssget_design_context response using Rule 4.cmp-{component-name}, .cmp-{component-name}__{element}.cmp-{component-name}--empty)Follow the template in clientlib-patterns.md. Add a header comment referencing the Figma node ID and key colors. Organize sections in this order: Block → Elements (DOM order) → Interactive states → Empty state → Responsive breakpoints (768px, 1024px).
Before finalizing CSS, verify against the Figma screenshot:
box-sizing: border-box so rendered size matches the design specCreate component JavaScript ONLY when the Figma design implies interactivity beyond CSS (e.g., toggles, carousels, accordions, tabs, scroll behaviors, form validation). Do NOT create JS for simple hover effects, CSS transitions, or static content.
ui.apps/src/main/content/jcr_root/apps/[project]/clientlibs/clientlib-{component-name}/js/{component-name}.jsjs.txt in the clientlib folder (contents: #base=js + {component-name}.js)<sly data-sly-call="${clientlib.js @ categories='[project].components.{component-name}'}"/>Follow the standard AEM component JS pattern using the data-cmp-is selector:
(function() {
'use strict';
var COMPONENT_SELECTOR = '[data-cmp-is="{component-name}"]';
function init(element) {
if (!element || element.dataset.cmpInitialized) return;
element.dataset.cmpInitialized = 'true';
// Bind events and initialize behavior
}
document.addEventListener('DOMContentLoaded', function() {
document.querySelectorAll(COMPONENT_SELECTOR).forEach(init);
});
})();Image and SVG assets returned by get_design_context are stored on Figma's CDN
and expire after 7 days. These are placeholder references only.
${model.image}rootPath="/content/dam") allow authors to select real DAM images at runtimeicon.svg) and reference via data-sly-resourcestyle="background-image: url('${model.backgroundImage}');") or use a dedicated CSS classAfter generating all component files from a Figma design, verify:
className, {variable}, &&, .map()), Tailwind classes, or React patterns remain in any file.cmp-{component-name}__{element} pattern${model.*}) with correct XSS contextsalt attributes on images, aria-label on buttons/links, visible focus outlines| Figma Output | AEM Destination File |
|---|---|
| JSX structure (semantic elements) | {component-name}.html (HTL template) |
| Tailwind inline styles (colors, fonts, spacing) | clientlib-{component-name}/css/{component-name}.css |
| Interactive behavior hints | clientlib-{component-name}/js/{component-name}.js (only if needed) |
| Image asset URLs | Dialog pathfield → Sling Model property → ${model.image} in HTL |
| Component hierarchy and structure | Sling Model properties + HTL conditional rendering |
| Design token variables (if Figma variables are used) | CSS custom properties or direct values |
.tessl-plugin
skills
aem-rde
aem-workflow
code-assessment
content-distribution
create-component
assets
references
dispatcher
incident-response
references
dispatcher-foundation
performance-tuning
references
dispatcher-foundation
security-hardening
references
dispatcher-foundation
technical-advisory
references
dispatcher-foundation
workflow-orchestrator
references
dispatcher-foundation
ensure-agents-md
references
migration
references
dispatcher
legacy-ui
template-modernization
validation
scripts
dispatcher-tool
validate-migration