Skip to main content

Intrinsics: the only tags

There are no nav, main, ul, span, a, or button intrinsics; the compile error tells you this, believe it. Two rules cover the gaps:
  • Links and buttons are <text href> or <heading href>; they render real anchors. A bare href="#" is a placeholder: wire it or use a #section anchor (lint: placeholder-link).
  • Semantic containers come from the tag= override on any container: tag="header|footer|article|aside|a|button".
Special props on every intrinsic: tw, raw, cls, gcls (see Styling), id="anchor" (a real HTML id, so href="#anchor" works), and animate={{effect: 'fade'|'slide'|'scale', trigger: 'load'|'scrollIn'}}. Text intrinsics accept only <em>, <strong>, and <br> inline children; anything else throws at compile rather than silently dropping content. Exactly one <h1> per page, and heading levels step one at a time (lint: heading-structure).

Kit helpers

Kit helpers are free variables via the prelude; no imports needed. They return kit nodes you can mix freely with JSX children.
A complete header (desktop rail, dropdown mega-menus, mobile hamburger) as one self-contained html widget. It is immune to the burger-steals-a-flex-slot bug by construction (see Troubleshooting). Start here for navs; hand-roll only when the design demands it, and then follow the burger-wrapper rule religiously.

Forms (Elementor Pro): the complete recipe

All four parts are required:
  • Input types: text, email, number, tel, password, or textarea: true.
  • There is no radio widget in atomic Pro forms. Option-picker UIs are styled checkbox fields (one per option, distinct ids) or a select.
  • Use collect-submissions, not email, on Elementor 4.2.x with Pro 4.1.0: the email action is upstream-broken there (the validator and the send-runner disagree). Submissions land in the wp_e_submissions table, visible under Elementor, then Submissions in wp-admin.
  • Add formSuccess({ message, sub, accent }) anywhere on the page: it hides the form and shows a success banner when the Pro ajax submit succeeds. The atomic runner shows no sent-state on its own. One per page with a form.

button: the envelope rule

A real href is required (no '#'). The third argument takes atomic envelopes only; plain sx values throw, and the error carries recipes. For styled CTAs, prefer a styled <text href> anchor or box({...sx, tag: 'a'}).

Embeds and structure

  • divider(props)
  • tabs([{label, content}, ...], {active})
  • youtube(url) and video(url)

fontLoader: narrow scope

Google fonts named in a style prop (font= or an sx font-family) load natively: Elementor enqueues an elementor-gf-* stylesheet on render. Do not add fontLoader() for those; it double-loads. fontLoader('Family', [400, 700]) (placed first in the tree, one per family) is only for families Elementor cannot see: fonts referenced solely inside raw html widgets or raw= CSS. Without it those fall back silently and every measurement you take is wrong (lint: font-not-loaded). Custom font files go through the media manifest.

Next

Media and fonts

The manifest contract for images and font files.

Troubleshooting

The layout gotchas each of these rules prevents.