---
name: clickfunnels-page-markup
description: >
Reference for PML (Page Markup Language), the XML-like DSL that ClickFunnels 2.0
uses to describe page content. Read this skill when authoring or editing markup
for any surface that accepts a `markup` field - currently pages and blog posts.
Writing `markup` replaces the entire page and PML covers only a subset of what
the ClickFunnels editor can build, so read the approval guardrail before
touching a page that already exists.
This is a language reference; for the HTTP APIs that consume it, see the
Pages Skill (for funnel/site pages) or the Blogs Skill (for blog posts).
version: "1.0"
author: ClickFunnels
tools: []
references:
- path: https://accounts.myclickfunnels.com/llms.txt
description: Quick-reference index - product overview, login, funnels, broadcasts, API docs.
- path: https://accounts.myclickfunnels.com/.well-known/pages/skill.md
description: The Pages API - read this for authentication, the page CRUD endpoints, and the checkout-products attachment shape.
- path: https://accounts.myclickfunnels.com/.well-known/blogs/skill.md
description: The Blogs API - read this to create or update blog posts whose body is PML.
- path: https://accounts.myclickfunnels.com/.well-known/funnels/skill.md
description: Funnel workflow APIs (split tests, conditional splits) that compose pages built with this DSL.
---
# ClickFunnels Page Markup Language (PML)
PML is an XML-like DSL compiled by the CF2 page engine into the full page tree the
editor renders. It is the canonical authoring format for both funnel/site pages
(via the Pages API) and blog posts (via the Blogs API).
This document is a **language reference** - elements, attributes, design system,
patterns, common errors. It contains no HTTP details. For that, follow the
references above.
## When to read this skill
- Authoring markup for a new page or blog post from scratch
- Editing existing markup returned by `expand[]=markup` on any surface
- Debugging a `400 Markup is not valid PML` response
- Designing a new conversion-oriented page and choosing fonts, colors, gradients
- Composing a multi-section page that follows CF2's design system
## STOP: get approval before overwriting an existing page
PML covers a subset of what the ClickFunnels page editor can build. Four
properties of that subset make writing `markup` to an existing page dangerous:
- **Reading is lossy.** `expand[]=markup` runs the stored page through a
serializer that has an equivalent for the elements documented in this skill
and nothing else. Any other element the editor supports is dropped from the
string you get back: the element, its settings and its styling are simply
absent. There is no error, no warning, and no placeholder marking the hole.
- **An empty read does not mean an empty page.** Because dropped elements leave
no trace, a page built entirely from elements PML cannot express reads back as
an empty string. `""` is therefore not evidence that the page is blank, and it
is not a reason to skip the approval below.
- **A failed read looks like content.** If the read comes back as
``, the
serializer could not handle the page at all. Treat the page as unreadable and
do not write. Sending that string back replaces the page with nothing.
- **Writing replaces everything.** Sending `markup` rebuilds the page from your
string and overwrites both the published page and the draft the editor opens.
Nothing is merged, and there is no partial write.
Put together, a read-modify-write cycle on a page a person built in the editor
silently destroys whatever PML could not express, in the live page and in the
editor, and the API cannot put it back.
**The rule:** if the page or post could have been created or edited in the
ClickFunnels editor, STOP and ask the person for explicit approval before you
send `markup`. Tell them plainly that you are replacing the whole page and that
editor-only content will not survive. A general instruction such as "update the
page" or "improve the headline" is NOT that approval.
You do not need to ask when:
- you are creating a brand new page or post and passing `markup` on create
- you created the page through the API in this session and nobody has opened it
in the editor since
If you cannot tell how a page was authored, assume the editor built it and ask.
When you have an additive route, prefer it: create a new page and position it,
rather than rewriting a page that is already live and converting. If approval is
refused, the edit belongs in the editor, not in this API.
## DSL Quick Rules
```
<- full-width band
<- horizontal split (1-4 columns for content)
<- content container (NO padding attribute)
Text with accent
Body copy here.
```
**Critical nesting:** section -> row -> column -> content. Never skip a level.
**Colors:** #hex only. No rgba(), no named colors.
**fontsize:** Must include unit - "72px" not "72".
## Choosing a Design Style
Six canonical styles (full hex palettes in the DSL reference below):
| Style | Use when | Fonts |
|-------------------|-------------------------------------------|------------------------------|
| modern_tech | Software, apps, developer tools | Poppins + Inter |
| luxury_premium | High-end services, coaching, consulting | Playfair Display + Lato |
| bold_striking | High-urgency, sales, attention-grabbing | Anton + Roboto |
| minimalist_clean | Clean SaaS, B2B, professional services | Work Sans + Work Sans |
| playful_creative | Consumer apps, youth, creative services | Montserrat + Open Sans |
| warm_inviting | Health, wellness, coaching, local biz | Libre Baskerville + Roboto |
## Standard Page Structure
For a sales/landing page (most common):
1. **Hero** - big headline, subheadline, single CTA (padding 100-120px)
2. **Stats strip** - 4-col numbers + labels (padding 0 0)
3. **How it works / Features** - 3-col cards
4. **Benefits split** - 2-col with lists
5. **Final CTA** - gradient section, mirror hero CTA
## Example (minimal valid markup)
```xml
Your Headline Here
Supporting copy in one or two sentences.
```
## Common Validation Errors
These are the DSL parser errors returned at validate time. HTTP-level errors
(401, 404, etc.) live in the consuming API skill (Pages or Blogs).
| Error | Cause | Fix |
|-------|-------|-----|
| 400 "Markup is not valid PML: unknown top-level element(s)" | Unknown DSL tag at the top level | Check element name in the DSL reference below |
| 400 "Markup is not valid PML: no PML elements found" | Plain text or raw HTML in the `markup` field | Wrap content in valid PML elements (``, ``, etc.) |
| 400 "`` element must be inside a ``" | Nesting violation | Always nest section -> row -> column -> content |
| 400 "Invalid attribute(s) '...' on ``" | Attribute is not allowed on that element (e.g., `margintop` on ``) | Check the per-element attribute lists below |
| 400 "Markup appears to be truncated: ..." | The `markup` you sent stops mid-construct - a tag, attribute value, comment or `` body never closes, or elements are still open at the end of the document. Almost always means your markup was cut off before you finished writing it (an output token limit, a truncated buffer). | Re-send the complete document. Do NOT retry with the same truncated string, and do NOT try to "repair" it by sending only the tail - `markup` replaces the whole page, so a partial document deletes everything after the cut-off point. If the page is long, add the pixel or code with `head_code`/`footer_code` instead of rewriting the body. |
================================================================================
## 2. DSL OVERVIEW
================================================================================
PML is an XML-like format compiled by the CF2 page engine into a full
page tree. After a successful `POST`/`PATCH` to the consuming API, the
page is immediately live.
### Mandatory Nesting Rules
Every must contain >=1
Every must contain >=1
All content elements must live inside a
WRONG (400 error):
Text...
CORRECT:
Text
### Page Structure
A page is a flat sequence of elements.
Each section is a full-width horizontal band.
Sections stack vertically top to bottom.
================================================================================
## 3. STRUCTURE ELEMENTS
================================================================================
###
Full-width container. Top-level page band.
Attributes:
bg #hex color or CSS gradient string
bgimage Background image URL
bgprompt AI-generated background - describe in plain English
e.g. bgprompt="dark geometric grid pattern, subtle blue glow"
padding "vertical horizontal" px. Default "80 20". Single value = all sides.
padding-mobile Same format, applies at <=700px. Default "56 16"
shadow sm | md | lg
radius sm(4px) | md(8px) | lg(16px) | xl(24px)
border thin | medium | thick
opacity 0-100
id CSS/JS targeting
Examples:
<- gradient preset by name
###
Horizontal column divider within a section.
Attributes:
cols 1-12. Omit or use 1 for single centered column.
padding Same format as section
padding-mobile
id
Typical values:
single centered column
two-column (50/50)
three-column feature grid
four-column stat strip
###
Content container. NO padding attribute (use section/row padding instead).
Attributes:
bg Background color or gradient (for card-style columns)
shadow sm | md | lg
radius sm | md | lg | xl
border thin | medium | thick
opacity 0-100
marginhorizontal px (left/right buffer on column content)
marginhorizontal-mobile
id
###
Flexbox for horizontal layouts only. Place inside .
Attributes:
direction Always "row" (do NOT use "column" - that's a layout antipattern here)
justify flex-start | center | flex-end | space-around | space-between | space-evenly
align flex-start | center | flex-end | baseline | stretch
gap px between items. e.g. "16"
width "100%" | "400px"
height "auto" | "60px"
wrap nowrap | wrap | wrap-reverse
padding Same format as section
margintop px spacing above the flex container (also: margintop-mobile)
Mobile variants: direction-mobile, justify-mobile, align-mobile, wrap-mobile
Example:
================================================================================
## 4. TEXT ELEMENTS
================================================================================
All text elements share:
size xl(48px) | l(40px) | m(32px) | s(24px)
fontsize Custom size, overrides preset. Must include unit: "72px" "5rem" "100px"
align left | center | right
color #hex only (no rgba, no named colors)
font Google Font name (see full list below)
weight thin|light|normal|medium|semibold|bold|extrabold|black
transform uppercase | lowercase | capitalize
leading tight | snug | normal | relaxed | loose
spacing tight | normal | wide | wider | widest
margintop px spacing above this element
margintop-mobile
id
### Supported Google Fonts
DISPLAY / HEADLINE (character + impact):
Playfair Display - editorial elegance, serif
Cormorant Garamond - refined luxury, thin serif
DM Serif Display - warm sophistication, serif
Fraunces - expressive, variable-weight serif
Space Grotesk - modern tech, geometric sans
Outfit - clean geometric, versatile
Syne - bold contemporary, display sans
Bebas Neue - high impact, condensed caps
Oswald - strong, condensed sans
Anton - maximum impact, display caps
Lobster - playful script energy
Pacifico - friendly, rounded script
Libre Baskerville - warm, readable serif
Poppins - versatile, rounded geometric
Montserrat - professional, geometric
Merriweather - warm, readable serif
BODY / READABLE:
Inter - neutral, highly readable
Work Sans - clean, modern
Source Sans Pro - clean professionalism
Lato - approachable, humanist
Nunito - friendly, rounded
Quicksand - geometric, friendly
IBM Plex Sans - technical, modern
Open Sans - universal readability
Roboto - neutral, versatile
FONT PAIRINGS (proven combinations):
Playfair Display + Lato -> classic editorial elegance
Poppins + Inter -> modern product/tech
Space Grotesk + IBM Plex Sans -> developer/tech aesthetic
DM Serif Display + Source Sans Pro -> warm consultative
Bebas Neue + Roboto -> bold, high-energy
Montserrat + Open Sans -> professional versatile
NEVER pair fonts with similar character shapes (e.g., Roboto + Open Sans)
###
Primary heading. Size xl-s or override with fontsize.
Build Pages That Convert
###
Secondary heading. Sits below headline.
The API that writes itself
###
Body copy. Use leading="relaxed" for comfortable reading.
One HTTP call. DSL markup in. Full rendered page out.
### Inline Formatting
Works inside all text elements:
bolditaliccolored texthighlighted
Line One Line Two
### Text encoding (non-ASCII characters)
Type accented letters and punctuation as their literal UTF-8 characters
(umlauts, accents, em dash, curly quotes, arrows). Do NOT use HTML character
entities such as —, →, , or & inside text - they are not
decoded on a page and render literally (e.g. "—" shows up verbatim) or
are dropped entirely. Write the em dash character itself, "und" instead of
"&", and so on. (Raw HTML inside is the one exception - there,
normal HTML entities work.)
================================================================================
## 5. MEDIA ELEMENTS
================================================================================
###
Attributes:
src Hosted image URL (for existing images)
prompt AI-generated image - write a descriptive scene/mood prompt
alt Alt text for accessibility
align left | center | right
width "50%" | "300px" | "100%" (prefer % for responsiveness)
height "auto" | "300px"
radius sm | md | lg | xl | full | "20px"
Prompts - be specific for quality:
WEAK: prompt="person smiling"
STRONG: prompt="professional woman in her early 40s, warm confident smile,
soft natural light, modern office background, editorial style"
Include: lighting, emotion, environment, style direction.
Examples:
###