primitives.html. Use it to verify inference, inline edit, and overlay panels before shipping markup. How inference works
When the SDK finds a data-macro-key, it resolves a typeId in this order:
- Explicit
data-macro-type(short form likerichtextor fullmacro/richtext/@acme/hero). - Registered custom element
tagfrom aMacroNodeDefinition. - Built-in tag inference (for example
<img>→macro/image). - Fallback:
macro/textfor unmatched tags.
<div> or <section> becomes text, not richtext. Always set data-macro-type="richtext" (or macro/richtext) for HTML bodies. Markup patterns
<h1 data-macro-key="home.title">Welcome</h1>
<img data-macro-key="home.hero" alt="" />
<a data-macro-key="home.cta" href="/pricing">Pricing</a>| Name | Type | Required | Description |
|---|---|---|---|
data-macro-key | string | required | Stable content key for the website (no locale suffix). |
data-macro-type | string | optional | Builtin short name, macro/* id, or custom @scope/name typeId. |
Type matrix
Payload shapes below are the fields editors see in the overlay / Dashboard. The SDK seeds and hydrates them via DOM bindings — you usually only mark the host element.
| Name | Type | Description |
|---|---|---|
macro/text | TEXT | h1–h6, p, span, strong, em, label, button, li, td, th, a without href, … → { text } |
macro/richtext | RICHTEXT | Only with data-macro-type="richtext" (or macro/richtext) → { html } |
macro/image | IMAGE | img → { assetId, alt, caption } |
macro/link | LINK | a[href] → { text, href, target, rel }; Shift+click to navigate while editing |
macro/video | VIDEO | video → asset + poster + playback flags |
macro/audio | AUDIO | audio → asset + controls/loop |
macro/embed | EMBED | iframe / embed → { url, title } |
macro/picture | PICTURE | picture → asset, alt, sources JSON |
macro/icon | ICON | svg → svg markup and/or assetId |
macro/attr-text | ATTR_TEXT | input (text-like), textarea → value, placeholder, label, … |
macro/select | SELECT | select → selected + options JSON |
macro/checkbox | CHECKBOX | input[type=checkbox] → checked, value, label |
macro/radio | RADIO | input[type=radio] → checked, value, label |
macro/group | REPEATER | HTML repeaters / items-slot runtime host |
Text
macro/text covers headings, paragraphs, spans, buttons, list items, table cells, and anchors without href. Payload: { text }. Inline editing is on by default.
<h1 data-macro-key="home.title">Welcome</h1>
<p data-macro-key="home.lead">One sentence under the hero.</p>
<button type="button" data-macro-key="home.ctaLabel">Get started</button>Richtext
Payload: { html }. Use for multi-paragraph or formatted copy. The overlay offers a rich editor; inline editing is available when the host is selected.
<div
class="prose"
data-macro-key="about.body"
data-macro-type="richtext"
>
<p>Long-form copy with <strong>formatting</strong>.</p>
</div>Image
Inferred from <img>. Payload: assetId, alt, caption. Store the Macro asset UUID — never bake CDN URLs into content keys. The context bar exposes “Choose image”.
<img
data-macro-key="home.heroImage"
alt="Hero"
width="1600"
height="900"
/>Link
Inferred from <a href>. Payload: text, href, target, rel. While the editor is open, a normal click selects the link; editors navigate with Shift+click.
<a data-macro-key="nav.pricing" href="/pricing">Pricing</a>Media: video, audio, embed, picture, icon
macro/video
Tag <video>. Fields: assetId (video MIME), posterAssetId, controls, loop, muted.
<video
data-macro-key="home.reel"
controls
playsinline
></video>macro/audio
Tag <audio>. Fields: assetId, controls, loop.
macro/embed
Tags <iframe> / <embed>. Fields: url, title. Prefer trusted embed URLs; your site still owns CSP.
<iframe
data-macro-key="home.map"
data-macro-type="embed"
title="Map"
loading="lazy"
></iframe>macro/picture
Tag <picture>. Fields: assetId, alt, sources (JSON for art-direction sources).
macro/icon
Tag <svg>. Fields: inline svg markup and/or assetId for an image asset. Prefer SVG when you need color inheritance via currentColor.
Form field primitives
Macro edits field values only. Keep action, method, name, and server handling in your HTML. Full walkthrough: Form primitives.
<form action="/api/contact" method="post">
<label>
<span data-macro-key="contact.emailLabel">Email</span>
<input
name="email"
type="email"
data-macro-key="contact.email"
data-macro-type="attr-text"
/>
</label>
<button type="submit" data-macro-key="contact.submit">Send</button>
</form>Groups / repeaters
macro/group powers HTML repeaters and component items slots. Prefer Groups & repeaters for page-level lists, and Slots: items when the list lives inside a custom component.
Keys, locales, and draft/live
data-macro-keyis unique per website. Do not put locale codes inside the key — locale is a save/view dimension, not part of the key string.- Draft and live payloads share the same keys; publish promotes draft → live.
- Sitewide keys (header/footer) use the same primitives — see Sitewide content.
Payload cheat sheet
{
"macro/text": { "text": "Welcome" },
"macro/richtext": { "html": "<p>Hello</p>" },
"macro/image": { "assetId": "<uuid>", "alt": "Hero", "caption": "" },
"macro/link": { "text": "Pricing", "href": "/pricing", "target": "", "rel": "" },
"macro/attr-text": { "value": "", "placeholder": "[email protected]", "label": "Email" },
"macro/select": { "selected": "de", "options": [{ "value": "de", "label": "Deutsch" }], "label": "Language" },
"macro/checkbox": { "checked": false, "value": "1", "label": "Subscribe" }
}Do
- Rely on tag inference for text, images, and links when the HTML already expresses intent.
- Mark richtext explicitly every time.
- Keep one key = one content concern (one heading, one image, one CTA label).
- Use Shift+click to follow links while editing.
Don’t
- Don’t wrap a single H1 in a custom component — use
macro/text. - Don’t put locale codes or environment names inside
data-macro-key. - Don’t store permanent CDN URLs in content when an asset UUID exists.
- Don’t expect Macro to own form
action/ submission endpoints.