figma-code-agent 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +133 -0
- package/bin/install.js +328 -0
- package/knowledge/README.md +62 -0
- package/knowledge/css-strategy.md +973 -0
- package/knowledge/design-to-code-assets.md +855 -0
- package/knowledge/design-to-code-layout.md +929 -0
- package/knowledge/design-to-code-semantic.md +1085 -0
- package/knowledge/design-to-code-typography.md +1003 -0
- package/knowledge/design-to-code-visual.md +1145 -0
- package/knowledge/design-tokens-variables.md +1261 -0
- package/knowledge/design-tokens.md +960 -0
- package/knowledge/figma-api-devmode.md +894 -0
- package/knowledge/figma-api-plugin.md +920 -0
- package/knowledge/figma-api-rest.md +742 -0
- package/knowledge/figma-api-variables.md +848 -0
- package/knowledge/figma-api-webhooks.md +876 -0
- package/knowledge/payload-blocks.md +1184 -0
- package/knowledge/payload-figma-mapping.md +1210 -0
- package/knowledge/payload-visual-builder.md +1004 -0
- package/knowledge/plugin-architecture.md +1176 -0
- package/knowledge/plugin-best-practices.md +1206 -0
- package/knowledge/plugin-codegen.md +1313 -0
- package/package.json +31 -0
- package/skills/README.md +103 -0
- package/skills/audit-plugin/SKILL.md +244 -0
- package/skills/build-codegen-plugin/SKILL.md +279 -0
- package/skills/build-importer/SKILL.md +320 -0
- package/skills/build-plugin/SKILL.md +199 -0
- package/skills/build-token-pipeline/SKILL.md +363 -0
- package/skills/ref-html/SKILL.md +290 -0
- package/skills/ref-layout/SKILL.md +150 -0
- package/skills/ref-payload-block/SKILL.md +415 -0
- package/skills/ref-react/SKILL.md +222 -0
- package/skills/ref-tokens/SKILL.md +347 -0
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ref-layout
|
|
3
|
+
description: "Reference: Interpret Figma Auto Layout properties and generate correct CSS Flexbox. Use this skill when the user has Figma Auto Layout JSON data (layoutMode, sizing modes, alignment, spacing, wrap, constraints) and needs the corresponding CSS Flexbox output with accurate sizing mode handling."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
@knowledge/design-to-code-layout.md
|
|
7
|
+
|
|
8
|
+
## Objective
|
|
9
|
+
|
|
10
|
+
Interpret Figma Auto Layout properties from node data and produce correct, production-grade CSS Flexbox. This skill handles the full pipeline: container detection, flex direction, alignment mapping, sizing modes (FIXED/HUG/FILL), gap and padding, wrap mode, min/max constraints, absolute positioning, non-Auto-Layout frames, and responsive multi-frame merging.
|
|
11
|
+
|
|
12
|
+
## Input
|
|
13
|
+
|
|
14
|
+
The user provides Figma node data as `$ARGUMENTS`. This may be:
|
|
15
|
+
|
|
16
|
+
- Raw Figma REST API JSON (a node or subtree with Auto Layout properties)
|
|
17
|
+
- Extracted Auto Layout properties (layoutMode, primaryAxisAlignItems, counterAxisAlignItems, itemSpacing, padding, layoutSizingHorizontal, layoutSizingVertical, layoutWrap, etc.)
|
|
18
|
+
- A description of the Figma Auto Layout configuration in plain language
|
|
19
|
+
- A screenshot or paste of Figma's design panel showing layout settings
|
|
20
|
+
|
|
21
|
+
If the input is incomplete, ask the user for the missing properties before generating CSS. At minimum you need: `layoutMode`, child sizing modes, and spacing.
|
|
22
|
+
|
|
23
|
+
## Process
|
|
24
|
+
|
|
25
|
+
Follow these steps in order. Consult `knowledge/design-to-code-layout.md` for all mapping tables and edge cases.
|
|
26
|
+
|
|
27
|
+
### Step 1: Detect Container Type
|
|
28
|
+
|
|
29
|
+
- Check if the node has `layoutMode` set to `HORIZONTAL` or `VERTICAL` (Auto Layout container).
|
|
30
|
+
- If `layoutMode` is `NONE` or absent, treat as non-Auto-Layout frame. Use absolute positioning with constraints (see Section 7 of the layout knowledge module).
|
|
31
|
+
- If the node is a `GROUP`, use absolute positioning with coordinate adjustment (subtract group position for child coordinates).
|
|
32
|
+
|
|
33
|
+
### Step 2: Map Container Properties
|
|
34
|
+
|
|
35
|
+
For Auto Layout containers, generate the base flex properties:
|
|
36
|
+
|
|
37
|
+
- `layoutMode: HORIZONTAL` -> `display: flex; flex-direction: row;`
|
|
38
|
+
- `layoutMode: VERTICAL` -> `display: flex; flex-direction: column;`
|
|
39
|
+
- `primaryAxisAlignItems` -> `justify-content` (MIN/CENTER/MAX/SPACE_BETWEEN)
|
|
40
|
+
- `counterAxisAlignItems` -> `align-items` (MIN/CENTER/MAX/BASELINE)
|
|
41
|
+
- Container sizing: `primaryAxisSizingMode` and `counterAxisSizingMode` determine if the container itself has explicit dimensions or hugs content.
|
|
42
|
+
|
|
43
|
+
### Step 3: Map Gap and Padding
|
|
44
|
+
|
|
45
|
+
- `itemSpacing` -> CSS `gap` (NOT padding -- this is spacing BETWEEN children)
|
|
46
|
+
- `counterAxisSpacing` -> only applies when `layoutWrap: WRAP`. Maps to `row-gap` or `column-gap` depending on direction.
|
|
47
|
+
- When both primary and counter gaps are equal, use `gap` shorthand. When different, use separate `row-gap` and `column-gap`.
|
|
48
|
+
- `paddingTop/Right/Bottom/Left` -> CSS `padding` with shorthand optimization.
|
|
49
|
+
- If any spacing or padding value has a variable binding, generate `var()` reference. External library variables get pixel fallbacks.
|
|
50
|
+
|
|
51
|
+
### Step 4: Handle Sizing Modes (CRITICAL)
|
|
52
|
+
|
|
53
|
+
This is the most error-prone step. For EACH child in the Auto Layout parent:
|
|
54
|
+
|
|
55
|
+
1. Determine which axis is PRIMARY (same as parent flex-direction) and which is COUNTER.
|
|
56
|
+
- Parent `HORIZONTAL` -> horizontal = primary, vertical = counter
|
|
57
|
+
- Parent `VERTICAL` -> vertical = primary, horizontal = counter
|
|
58
|
+
|
|
59
|
+
2. PRIMARY AXIS sizing:
|
|
60
|
+
- `FILL` -> `flex-grow: 1; flex-basis: 0;` (BOTH required -- never omit `flex-basis: 0`)
|
|
61
|
+
- `FIXED` -> `flex-shrink: 0;` + explicit dimension from bounds
|
|
62
|
+
- `HUG` -> omit sizing CSS, let content determine size
|
|
63
|
+
|
|
64
|
+
3. COUNTER AXIS sizing:
|
|
65
|
+
- `FILL` -> `align-self: stretch;`
|
|
66
|
+
- `FILL` + max constraint on counter dimension -> `width: 100%` / `height: 100%` + `max-width`/`max-height` (NOT `align-self: stretch`)
|
|
67
|
+
- `FIXED` -> explicit dimension from bounds
|
|
68
|
+
- `HUG` -> omit, let content determine size
|
|
69
|
+
|
|
70
|
+
4. Check for `layoutAlign` overrides on individual children -> `align-self`
|
|
71
|
+
|
|
72
|
+
### Step 5: Handle Wrap Mode
|
|
73
|
+
|
|
74
|
+
- `layoutWrap: WRAP` -> `flex-wrap: wrap;`
|
|
75
|
+
- `counterAxisAlignContent` -> `align-content` (AUTO -> `flex-start`, SPACE_BETWEEN -> `space-between`)
|
|
76
|
+
- Counter axis gap becomes relevant only in wrap mode.
|
|
77
|
+
|
|
78
|
+
### Step 6: Handle Min/Max Constraints
|
|
79
|
+
|
|
80
|
+
- `minWidth` (if > 0) -> `min-width`
|
|
81
|
+
- `maxWidth` (if < Infinity) -> `max-width`
|
|
82
|
+
- `minHeight` (if > 0) -> `min-height`
|
|
83
|
+
- `maxHeight` (if < Infinity) -> `max-height`
|
|
84
|
+
- These apply to both containers and children.
|
|
85
|
+
|
|
86
|
+
### Step 7: Handle Absolute Children
|
|
87
|
+
|
|
88
|
+
- If any child has `layoutPositioning: ABSOLUTE`, add `position: relative` to the parent.
|
|
89
|
+
- Absolute children get `position: absolute` with offset values from constraints.
|
|
90
|
+
- These children do not participate in flex flow or gap distribution.
|
|
91
|
+
|
|
92
|
+
### Step 8: Responsive Multi-Frame (if applicable)
|
|
93
|
+
|
|
94
|
+
If multiple frames represent breakpoints (detected via `#mobile`/`#tablet`/`#desktop` suffix or variant properties):
|
|
95
|
+
|
|
96
|
+
- Smallest frame provides base styles (no media query).
|
|
97
|
+
- Larger frames contribute override styles in `@media (min-width: ...)` blocks.
|
|
98
|
+
- Only emit properties that differ from the base.
|
|
99
|
+
- Reset layout properties (`align-self: auto`, `flex-grow: 0`, `flex-shrink: 1`, `flex-basis: auto`) when they exist in base but not in the larger breakpoint.
|
|
100
|
+
- Transform fixed pixel widths in responsive overrides to `width: 100%; max-width: Npx` for fluid behavior.
|
|
101
|
+
|
|
102
|
+
## Output
|
|
103
|
+
|
|
104
|
+
Generate CSS with explanatory comments showing the Figma-to-CSS mapping:
|
|
105
|
+
|
|
106
|
+
```css
|
|
107
|
+
/* Container: layoutMode=HORIZONTAL, primaryAxis=SPACE_BETWEEN, counterAxis=CENTER */
|
|
108
|
+
.component-name {
|
|
109
|
+
display: flex;
|
|
110
|
+
flex-direction: row;
|
|
111
|
+
justify-content: space-between;
|
|
112
|
+
align-items: center;
|
|
113
|
+
gap: 16px; /* itemSpacing: 16 */
|
|
114
|
+
padding: 24px 16px; /* paddingTop/Bottom: 24, paddingLeft/Right: 16 */
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/* Child: sizingH=FILL (primary), sizingV=HUG (counter) */
|
|
118
|
+
.component-name__content {
|
|
119
|
+
flex-grow: 1;
|
|
120
|
+
flex-basis: 0;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/* Child: sizingH=FIXED (primary, 200px), sizingV=FILL (counter) */
|
|
124
|
+
.component-name__sidebar {
|
|
125
|
+
width: 200px;
|
|
126
|
+
flex-shrink: 0;
|
|
127
|
+
align-self: stretch;
|
|
128
|
+
}
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
When variable bindings are present, use CSS custom properties:
|
|
132
|
+
|
|
133
|
+
```css
|
|
134
|
+
.component-name {
|
|
135
|
+
gap: var(--spacing-md); /* Variable: spacing/md */
|
|
136
|
+
padding: var(--spacing-lg, 24px); /* External variable with fallback */
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Output Checklist
|
|
141
|
+
|
|
142
|
+
Before returning CSS, verify:
|
|
143
|
+
|
|
144
|
+
- [ ] Every FILL on primary axis has BOTH `flex-grow: 1` AND `flex-basis: 0`
|
|
145
|
+
- [ ] STRETCH only appears on counter axis (never primary)
|
|
146
|
+
- [ ] `itemSpacing` maps to `gap`, not padding
|
|
147
|
+
- [ ] GROUP children use absolute positioning with adjusted coordinates
|
|
148
|
+
- [ ] FILL counter-axis + max constraint uses `width: 100%`/`height: 100%` instead of `align-self: stretch`
|
|
149
|
+
- [ ] Responsive overrides include layout property resets where needed
|
|
150
|
+
- [ ] Variable references use `var()` with fallbacks for external library variables
|
|
@@ -0,0 +1,415 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ref-payload-block
|
|
3
|
+
description: "Reference: Map a Figma component to a PayloadCMS block definition with config, renderer, types, and CSS Module. Use this skill when the user has a Figma component (JSON data, component description, or REST API node) and needs a complete PayloadCMS block implementation following the container-first architecture."
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
@knowledge/payload-blocks.md
|
|
7
|
+
@knowledge/payload-figma-mapping.md
|
|
8
|
+
@knowledge/payload-visual-builder.md
|
|
9
|
+
@knowledge/css-strategy.md
|
|
10
|
+
|
|
11
|
+
## Objective
|
|
12
|
+
|
|
13
|
+
Map a Figma component to a complete PayloadCMS block implementation: a block config (`.ts`), a React renderer (`.tsx`), TypeScript types, and a CSS Module (`.module.css`). The skill uses multi-signal confidence scoring to identify the correct block type from the 18-type catalog, maps component properties to PayloadCMS fields, generates container nesting rules, and produces CSS that follows the three-layer architecture (Tailwind layout + CSS Custom Properties tokens + CSS Modules visual skin).
|
|
14
|
+
|
|
15
|
+
## Input
|
|
16
|
+
|
|
17
|
+
The user provides Figma component data as `$ARGUMENTS`. This may be:
|
|
18
|
+
|
|
19
|
+
- **Figma REST API JSON** -- A component or component set node with properties, variants, children, fills, text content
|
|
20
|
+
- **Plugin API extracted data** -- Component properties, variant details, child nodes
|
|
21
|
+
- **Component description** -- Plain language description of the Figma component (structure, properties, variants, content)
|
|
22
|
+
- **Screenshot or design spec** -- Visual reference of the component with noted properties
|
|
23
|
+
|
|
24
|
+
If the input is insufficient, ask for:
|
|
25
|
+
- Component name and variant properties
|
|
26
|
+
- Child element structure (text nodes, images, buttons, nested components)
|
|
27
|
+
- Layout mode (Auto Layout direction, spacing, padding)
|
|
28
|
+
- Visual properties (colors, border radius, shadows)
|
|
29
|
+
|
|
30
|
+
## Process
|
|
31
|
+
|
|
32
|
+
Follow these steps in order. Consult the referenced knowledge modules for all mapping rules, field types, and architectural patterns.
|
|
33
|
+
|
|
34
|
+
### Step 1: Analyze Figma Component Structure
|
|
35
|
+
|
|
36
|
+
Identify the component's structure and classify its elements:
|
|
37
|
+
|
|
38
|
+
- **Root node**: component boundary, layout mode (Auto Layout direction), dimensions
|
|
39
|
+
- **Variant properties**: boolean toggles, enum options (size, style, impact level)
|
|
40
|
+
- **Text layers**: heading text, body text, labels, captions
|
|
41
|
+
- **Image fills/frames**: background images, thumbnails, avatars, icons
|
|
42
|
+
- **Interactive children**: button instances, link text, form inputs
|
|
43
|
+
- **Nested component instances**: child components that may map to separate blocks
|
|
44
|
+
- **Layout structure**: spacing, padding, alignment, wrap mode
|
|
45
|
+
- **Visual properties**: fills, strokes, effects, corner radius
|
|
46
|
+
|
|
47
|
+
### Step 2: Determine Block Type via Confidence Scoring
|
|
48
|
+
|
|
49
|
+
Consult `knowledge/payload-figma-mapping.md` Section 2 for the complete decision tree.
|
|
50
|
+
|
|
51
|
+
Apply multi-signal confidence scoring using two complementary strategies:
|
|
52
|
+
|
|
53
|
+
**Name-based detection (higher priority):**
|
|
54
|
+
- Check if component/frame name contains recognized keywords: hero, card, button, nav, accordion, tabs, stat, testimonial, carousel, cta, media, video, form, etc.
|
|
55
|
+
|
|
56
|
+
**Structure-based detection (fallback for generic names):**
|
|
57
|
+
- Analyze children, dimensions, properties, and layout patterns against block type heuristics
|
|
58
|
+
|
|
59
|
+
**18-type block catalog:**
|
|
60
|
+
|
|
61
|
+
| Block Type | Slug | Key Signals |
|
|
62
|
+
|-----------|------|-------------|
|
|
63
|
+
| Hero | `hero` | Full-width, bg image/color, large heading, CTA buttons |
|
|
64
|
+
| Card | `card` | Bounded width, image + heading + body, optional CTA, radius/shadow |
|
|
65
|
+
| Button | `button` | Small dimensions, bg fill, single text label, radius |
|
|
66
|
+
| Navigation | `subnavigation` | Horizontal layout, multiple link children, compact |
|
|
67
|
+
| Accordion | `accordion` | Vertical layout, repeating header + content, toggle icon |
|
|
68
|
+
| Tabs | `tabs` | Tab bar + content panel, active/inactive states |
|
|
69
|
+
| Stats | `stats` | Large numeric text, label below, compact |
|
|
70
|
+
| Testimonial | `testimonial` | Quote text, author info, optional avatar |
|
|
71
|
+
| Carousel | `carousel` | Horizontal overflow, nav dots/arrows, card children |
|
|
72
|
+
| CallToAction | `callToAction` | Full-width, strong background, title + description |
|
|
73
|
+
| Media | `media` | Single image, no text children |
|
|
74
|
+
| Video | `video` | Play button overlay, 16:9 ratio, thumbnail |
|
|
75
|
+
| Form | `form` | Input fields, submit button, labels |
|
|
76
|
+
| RichText | `richText` | Primarily text content, styled segments |
|
|
77
|
+
| Container | `container` | Auto Layout wrapper containing block-type children |
|
|
78
|
+
| Grid | `grid` | Grid-like child layout, uniform child sizes |
|
|
79
|
+
| Code | `code` | Code/monospace content |
|
|
80
|
+
| StickyCTA | `stickyCTA` | Fixed/sticky positioning, compact CTA |
|
|
81
|
+
|
|
82
|
+
**Confidence thresholds:**
|
|
83
|
+
- 0.9+ -- Auto-map, high confidence
|
|
84
|
+
- 0.8-0.89 -- Auto-map, flag for optional review
|
|
85
|
+
- 0.7-0.79 -- Moderate confidence, flag for human review
|
|
86
|
+
- Below 0.7 -- Require human confirmation
|
|
87
|
+
|
|
88
|
+
Report the matched block type and confidence score to the user.
|
|
89
|
+
|
|
90
|
+
### Step 3: Map Component Properties to Block Fields
|
|
91
|
+
|
|
92
|
+
Consult `knowledge/payload-figma-mapping.md` Section 3 and `knowledge/payload-blocks.md` Section 2.
|
|
93
|
+
|
|
94
|
+
Apply these property-to-field mapping rules:
|
|
95
|
+
|
|
96
|
+
| Figma Property Type | PayloadCMS Field Type | Mapping Rule |
|
|
97
|
+
|--------------------|----------------------|--------------|
|
|
98
|
+
| Text layer (heading) | `richText` (Lexical) | Extract text content into Lexical document structure |
|
|
99
|
+
| Text layer (single line) | `text` | Direct string mapping |
|
|
100
|
+
| Boolean variant | `checkbox` | `true`/`false` toggle |
|
|
101
|
+
| Enum variant (2-4 options) | `radio` | Radio buttons with variant option values |
|
|
102
|
+
| Enum variant (5+ options) | `select` | Dropdown with variant option values |
|
|
103
|
+
| Instance swap slot | `relationship` or `blocks` | Reference to another block type or media |
|
|
104
|
+
| Image fill / image frame | `upload` with `relationTo: 'media'` | Image upload field via `imageFields` factory |
|
|
105
|
+
| Color property | Token reference | Map to CSS Custom Property, not a hardcoded color field |
|
|
106
|
+
| Number property | `number` | Numeric input for dimensions, counts |
|
|
107
|
+
| URL/link text | `group` with link fields | Link group factory (type, url, label, newTab) |
|
|
108
|
+
| Button instance(s) | `array` of link groups | Button/CTA array using `linkGroup` factory |
|
|
109
|
+
|
|
110
|
+
**Field organization follows tab-based pattern:**
|
|
111
|
+
1. **Content tab** (named `content`): richText, text fields, arrays of content items
|
|
112
|
+
2. **Media/Image tab**: image upload fields via `imageFields` factory
|
|
113
|
+
3. **CTA tab**: button/link groups when the block has call-to-action elements
|
|
114
|
+
4. **Settings tab** (unnamed): className, layoutMeta, type/variant selection, HTML tag
|
|
115
|
+
|
|
116
|
+
### Step 4: Map Variants to CVA Configuration
|
|
117
|
+
|
|
118
|
+
If the component has enum variants that affect visual presentation (e.g., size: sm/md/lg, style: primary/secondary, impact: high/medium/low):
|
|
119
|
+
|
|
120
|
+
- Map variant property name to block settings field (radio or select)
|
|
121
|
+
- Generate CVA (class-variance-authority) configuration for the renderer:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
import { cva } from 'class-variance-authority';
|
|
125
|
+
|
|
126
|
+
const heroVariants = cva(styles.hero, {
|
|
127
|
+
variants: {
|
|
128
|
+
type: {
|
|
129
|
+
highImpact: styles['hero--high-impact'],
|
|
130
|
+
mediumImpact: styles['hero--medium-impact'],
|
|
131
|
+
lowImpact: styles['hero--low-impact'],
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
defaultVariants: {
|
|
135
|
+
type: 'highImpact',
|
|
136
|
+
},
|
|
137
|
+
});
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Step 5: Handle Container Nesting
|
|
141
|
+
|
|
142
|
+
Consult `knowledge/payload-blocks.md` and `knowledge/payload-figma-mapping.md` Section 4.
|
|
143
|
+
|
|
144
|
+
**Maximum nesting: 2 levels** (Container > NestedContainer).
|
|
145
|
+
|
|
146
|
+
- If the Figma component contains structural wrappers around other block-type children, map outer wrapper to `Container` and inner wrappers to a restricted nested container.
|
|
147
|
+
- Container block uses the `blocks` field type to hold child blocks.
|
|
148
|
+
- Map Figma Auto Layout properties to Container settings:
|
|
149
|
+
- `layoutMode: HORIZONTAL` --> `settings.layout = 'row'`
|
|
150
|
+
- `layoutMode: VERTICAL` --> `settings.layout = 'col'`
|
|
151
|
+
- `primaryAxisAlignItems` --> `settings.justifyContent` (Tailwind utility string)
|
|
152
|
+
- `counterAxisAlignItems` --> `settings.alignItems` (Tailwind utility string)
|
|
153
|
+
- `itemSpacing` --> `settings.gap` (snap to nearest option)
|
|
154
|
+
- Settings are stored as Tailwind utility class strings (Layer 1 of CSS architecture).
|
|
155
|
+
|
|
156
|
+
**Nesting rules:**
|
|
157
|
+
- Container (level 1) can contain any block type including NestedContainer
|
|
158
|
+
- NestedContainer (level 2) can contain content blocks but NOT another container
|
|
159
|
+
- Never nest deeper than 2 levels -- flatten if the Figma structure goes deeper
|
|
160
|
+
|
|
161
|
+
### Step 6: Configure Lexical Rich Text
|
|
162
|
+
|
|
163
|
+
Consult `knowledge/payload-blocks.md` for Lexical configuration patterns.
|
|
164
|
+
|
|
165
|
+
Restrict Lexical editor features based on block type:
|
|
166
|
+
|
|
167
|
+
| Block Type | Lexical Features Allowed |
|
|
168
|
+
|-----------|------------------------|
|
|
169
|
+
| Hero | Heading (h1-h2), paragraph, bold, italic, link, text color |
|
|
170
|
+
| Card | Heading (h3-h4), paragraph, bold, italic, link |
|
|
171
|
+
| RichText | Full feature set (all headings, lists, blockquote, code, media) |
|
|
172
|
+
| Button | None (use `text` field, not richText) |
|
|
173
|
+
| Stats | Heading (h2-h3), paragraph, bold |
|
|
174
|
+
| Testimonial | Paragraph, bold, italic |
|
|
175
|
+
| CallToAction | Heading (h2-h3), paragraph, bold, italic, link |
|
|
176
|
+
| Default | Paragraph, bold, italic, link |
|
|
177
|
+
|
|
178
|
+
### Step 7: Generate Block Config
|
|
179
|
+
|
|
180
|
+
Produce the PayloadCMS block config `.ts` file:
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
import { Block } from 'payload';
|
|
184
|
+
import { imageFields } from '@/admin/fields/imageFields';
|
|
185
|
+
import { linkGroup } from '@/admin/fields/linkGroup';
|
|
186
|
+
import { settingTabs } from './settingTabs';
|
|
187
|
+
|
|
188
|
+
export const Hero: Block = {
|
|
189
|
+
slug: 'hero',
|
|
190
|
+
labels: {
|
|
191
|
+
singular: 'Hero',
|
|
192
|
+
plural: 'Heroes',
|
|
193
|
+
},
|
|
194
|
+
fields: [
|
|
195
|
+
{
|
|
196
|
+
type: 'tabs',
|
|
197
|
+
tabs: [
|
|
198
|
+
{
|
|
199
|
+
label: 'Content',
|
|
200
|
+
name: 'content',
|
|
201
|
+
fields: [
|
|
202
|
+
{
|
|
203
|
+
name: 'richText',
|
|
204
|
+
type: 'richText',
|
|
205
|
+
label: false,
|
|
206
|
+
// Lexical config with restricted features
|
|
207
|
+
},
|
|
208
|
+
...linkGroup({ appearances: ['default', 'outline'] }),
|
|
209
|
+
],
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
label: 'Media',
|
|
213
|
+
fields: [...imageFields()],
|
|
214
|
+
},
|
|
215
|
+
settingTabs,
|
|
216
|
+
],
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
};
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
**Config conventions:**
|
|
223
|
+
- Export as named constant matching block name (PascalCase)
|
|
224
|
+
- Use `slug` in camelCase
|
|
225
|
+
- Organize fields in tabs (Content, Media, CTA, Settings)
|
|
226
|
+
- Use field factories (`imageFields`, `linkGroup`, `layoutMeta`) for reusable patterns
|
|
227
|
+
- Settings tab fields are stored at block root (no `name` on the tab)
|
|
228
|
+
|
|
229
|
+
### Step 8: Generate React Renderer
|
|
230
|
+
|
|
231
|
+
Produce the renderer `.tsx` file:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
import React from 'react';
|
|
235
|
+
import styles from './Hero.module.css';
|
|
236
|
+
import { RichText } from '@/components/RichText';
|
|
237
|
+
import { Media } from '@/components/Media';
|
|
238
|
+
import { CMSLink } from '@/components/Link';
|
|
239
|
+
import type { HeroBlock } from './types';
|
|
240
|
+
|
|
241
|
+
export function HeroRenderer({ block }: { block: HeroBlock }) {
|
|
242
|
+
const { content, image, settings } = block;
|
|
243
|
+
|
|
244
|
+
return (
|
|
245
|
+
<section
|
|
246
|
+
className={`flex flex-col items-center justify-center gap-6 p-8 ${styles.hero} ${styles[`hero--${settings?.type}`] || ''}`}
|
|
247
|
+
>
|
|
248
|
+
{image?.image && (
|
|
249
|
+
<div className={`absolute inset-0 ${styles.hero__media}`}>
|
|
250
|
+
<Media resource={image.image} fill />
|
|
251
|
+
</div>
|
|
252
|
+
)}
|
|
253
|
+
<div className={`relative z-10 flex flex-col items-center gap-4 ${styles.hero__content}`}>
|
|
254
|
+
{content?.richText && <RichText data={content.richText} />}
|
|
255
|
+
{content?.buttonGroup?.length > 0 && (
|
|
256
|
+
<div className="flex gap-4">
|
|
257
|
+
{content.buttonGroup.map((btn, i) => (
|
|
258
|
+
<CMSLink key={i} {...btn.link} />
|
|
259
|
+
))}
|
|
260
|
+
</div>
|
|
261
|
+
)}
|
|
262
|
+
</div>
|
|
263
|
+
</section>
|
|
264
|
+
);
|
|
265
|
+
}
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
**Renderer conventions:**
|
|
269
|
+
- Named export (not default) with `{BlockName}Renderer` naming
|
|
270
|
+
- Tailwind classes for layout (Layer 1)
|
|
271
|
+
- CSS Module classes for visual styling (Layer 3)
|
|
272
|
+
- CSS Custom Properties consumed via CSS Module (Layer 2)
|
|
273
|
+
- Null-safe access for all optional fields
|
|
274
|
+
- Semantic HTML elements based on block type
|
|
275
|
+
|
|
276
|
+
### Step 9: Generate CSS Module
|
|
277
|
+
|
|
278
|
+
Produce the `.module.css` file following the three-layer architecture:
|
|
279
|
+
|
|
280
|
+
```css
|
|
281
|
+
/* Hero Block — Visual Skin (Layer 3) */
|
|
282
|
+
/* Layout handled by Tailwind in JSX */
|
|
283
|
+
/* Tokens consumed from tokens.css (Layer 2) */
|
|
284
|
+
|
|
285
|
+
.hero {
|
|
286
|
+
background-color: var(--color-surface);
|
|
287
|
+
min-height: 60vh;
|
|
288
|
+
position: relative;
|
|
289
|
+
overflow: hidden;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
.hero--highImpact {
|
|
293
|
+
min-height: 80vh;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
.hero--mediumImpact {
|
|
297
|
+
min-height: 60vh;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
.hero--lowImpact {
|
|
301
|
+
min-height: 40vh;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
.hero__media {
|
|
305
|
+
opacity: 0.3;
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
.hero__content {
|
|
309
|
+
max-width: 48rem;
|
|
310
|
+
text-align: center;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
.hero__content h1,
|
|
314
|
+
.hero__content h2 {
|
|
315
|
+
color: var(--color-text-primary);
|
|
316
|
+
font-family: var(--font-heading);
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
.hero__content p {
|
|
320
|
+
color: var(--color-text-secondary);
|
|
321
|
+
font-size: var(--text-lg);
|
|
322
|
+
}
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**CSS Module rules:**
|
|
326
|
+
- Only visual properties (colors, backgrounds, borders, effects, typography skin)
|
|
327
|
+
- All color values via `var()` references to CSS Custom Properties (never hardcoded)
|
|
328
|
+
- BEM-style naming within the module (flat: `.block__element`, never `.block__element__sub`)
|
|
329
|
+
- Variant modifiers as `.block--variant` classes
|
|
330
|
+
- Layout (flexbox, gap, padding, sizing) stays in Tailwind classes in JSX
|
|
331
|
+
|
|
332
|
+
### Step 10: Generate TypeScript Types
|
|
333
|
+
|
|
334
|
+
```typescript
|
|
335
|
+
import type { Media as MediaType } from '@/payload-types';
|
|
336
|
+
|
|
337
|
+
export interface HeroBlock {
|
|
338
|
+
blockType: 'hero';
|
|
339
|
+
content?: {
|
|
340
|
+
richText?: any; // Lexical root node
|
|
341
|
+
buttonGroup?: Array<{
|
|
342
|
+
link: {
|
|
343
|
+
type: 'reference' | 'custom';
|
|
344
|
+
url?: string;
|
|
345
|
+
reference?: { relationTo: 'pages'; value: string };
|
|
346
|
+
label: string;
|
|
347
|
+
newTab?: boolean;
|
|
348
|
+
appearance?: 'default' | 'outline';
|
|
349
|
+
};
|
|
350
|
+
}>;
|
|
351
|
+
};
|
|
352
|
+
image?: {
|
|
353
|
+
image?: string | MediaType;
|
|
354
|
+
};
|
|
355
|
+
settings?: {
|
|
356
|
+
type?: 'highImpact' | 'mediumImpact' | 'lowImpact';
|
|
357
|
+
className?: string;
|
|
358
|
+
layoutMeta?: {
|
|
359
|
+
marginTop?: string;
|
|
360
|
+
marginBottom?: string;
|
|
361
|
+
paddingTop?: string;
|
|
362
|
+
paddingBottom?: string;
|
|
363
|
+
};
|
|
364
|
+
};
|
|
365
|
+
}
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
## Output
|
|
369
|
+
|
|
370
|
+
Generate four files for the mapped block:
|
|
371
|
+
|
|
372
|
+
### 1. `config.ts` -- PayloadCMS Block Definition
|
|
373
|
+
|
|
374
|
+
- Block slug, labels, tab-based field organization
|
|
375
|
+
- Field factories for reusable patterns (imageFields, linkGroup, layoutMeta)
|
|
376
|
+
- Lexical editor restricted per block type
|
|
377
|
+
- Settings fields with variant options stored as Tailwind utility strings
|
|
378
|
+
|
|
379
|
+
### 2. `Renderer.tsx` -- React Renderer Component
|
|
380
|
+
|
|
381
|
+
- Named export with `{BlockName}Renderer` naming
|
|
382
|
+
- Tailwind layout classes in JSX (Layer 1)
|
|
383
|
+
- CSS Module visual classes from companion `.module.css` (Layer 3)
|
|
384
|
+
- Null-safe field access
|
|
385
|
+
- Semantic HTML elements
|
|
386
|
+
|
|
387
|
+
### 3. `{BlockName}.module.css` -- CSS Module
|
|
388
|
+
|
|
389
|
+
- Visual skin only (colors, backgrounds, effects, typography overrides)
|
|
390
|
+
- All values via `var()` CSS Custom Property references (Layer 2)
|
|
391
|
+
- BEM naming (flat hierarchy, no deep nesting)
|
|
392
|
+
- Variant modifier classes
|
|
393
|
+
|
|
394
|
+
### 4. `types.ts` -- TypeScript Interfaces
|
|
395
|
+
|
|
396
|
+
- Block interface with `blockType` discriminant
|
|
397
|
+
- Optional fields for all non-required content
|
|
398
|
+
- Proper Media and reference types
|
|
399
|
+
|
|
400
|
+
### Output Checklist
|
|
401
|
+
|
|
402
|
+
Before returning the block implementation, verify:
|
|
403
|
+
|
|
404
|
+
- [ ] Block type matches with stated confidence score
|
|
405
|
+
- [ ] Container nesting does not exceed 2 levels
|
|
406
|
+
- [ ] Settings fields use Tailwind utility class strings (not raw CSS values)
|
|
407
|
+
- [ ] Lexical editor features are restricted appropriately for the block type
|
|
408
|
+
- [ ] Field factories are used for reusable patterns (not duplicated field definitions)
|
|
409
|
+
- [ ] CSS Module contains only visual properties (no layout)
|
|
410
|
+
- [ ] All color values use `var()` references (no hardcoded colors)
|
|
411
|
+
- [ ] BEM class names are flat (never `block__element__sub`)
|
|
412
|
+
- [ ] Renderer handles null/undefined for all optional fields
|
|
413
|
+
- [ ] Tab organization follows convention: Content, Media, CTA, Settings
|
|
414
|
+
- [ ] Block slug is camelCase and unique
|
|
415
|
+
- [ ] Variant mapping uses CVA pattern when applicable
|