# `MishkaCmsMigration.Sources.WordPress.Elementor`
[🔗](https://github.com/mishka-group/mishka_cms_migration/blob/v0.0.1/lib/mishka_cms_migration/sources/__wordpress/elementor.ex#L5)

A WordPress page or post laid out with Elementor as its item's content pieces
(`MishkaCmsMigration.Source.Item`), and the widgets in it the CMS has no equivalent for.

Elementor keeps an entry's layout as JSON in its `_elementor_data` meta, and marks an entry it
lays out with the `_elementor_edit_mode` meta `"builder"`. The REST API answers both from
Elementor 3.27 on, and an export carries them. `call/3` reads an entry so marked whose data is a
list of elements. Any other entry is `:none`, and so is one whose data is not JSON: it is read
from its `content` (`MishkaCmsMigration.Sources.WordPress.Content`), where Elementor keeps a rough
HTML fallback of the layout. A site on an older Elementor answers no data over the REST API, so
its Elementor entries are read from that fallback; its export carries the data.

The data is a tree of elements: sections holding columns, and containers (Elementor 3.6 on)
holding containers, each holding widgets. A section or a container at the top is drawn across the
page; one nested in another (`"isInner"`) is drawn inside it.

## A page

A page is laid out with the plain HTML the CMS's page builder places
(`MishkaCmsCore.Runtime.Authoring.Elements`), each element with the builder's own classes, so it
opens in the builder as though it had been built there:

  * a section or a container at the top — a Section, holding a Container (centred and width
    capped) unless it is full width;
  * the columns of a section, and the children of a container laid out in a row — a grid with a
    column for each, 12 at most (the widest grid Tailwind has), side by side from the `md`
    width, as the builder's 2, 3 and 4 columns are;
    one column alone, and the children of a container laid out in a column — a Stack, one under
    the next;
  * a heading — the H1 to H6 its size names (H2 by default; a paragraph for a `div`, `span` or
    `p`), linked when it links, centred, right-aligned or justified as it is;
  * a text editor — its HTML in a box, converted as classic content is (paragraphs, shortcodes,
    a YouTube or Vimeo address alone on its line);
  * an image — an Image, or a Figure with its caption (its own, or its file's), linked to its
    file or to its own address;
  * a button — a link drawn as the builder's button, centred or right-aligned as it is;
  * a video — the player of a YouTube or Vimeo video
    (`MishkaCmsMigration.Sources.WordPress.Content.embed/1`), a link to a Dailymotion or
    VideoPress one, and a `<video>` of a file the site hosts or of another address; a video
    whose address was never set is Elementor's demo video, as Elementor draws it, and one whose
    address was cleared is none;
  * an icon list — a Bulleted list, each item linked when it links, without its icons;
  * an image gallery, and Elementor Pro's gallery — a grid of as many columns as it says (1 to
    12, and 4 when it says none), of Figures each linked to its file or to its attachment page;
  * a spacer — a Spacer, and a divider a Divider;
  * HTML — the author's markup as it is;
  * a shortcode — its shortcodes, converted as classic content's are.

The paragraphs, lists, quotes, links, images, figures and rules of these take the classes of the
builder's element of the same tag (Paragraph, Bulleted list, Link, ...), with the builder's slate
colours swapped for the site's colour tokens (`text-fg dark:text-fg-dark`, ...): slate does not
change in the dark theme, and the tokens do.

## What of Elementor's styling is carried over

Through the builder's own style settings (`MishkaCmsCore.Runtime.Authoring.Tree.Styles`, what its
Inspector writes and reads back), never an inline `style`: a property and a value for each width
(Elementor's own, tablet and mobile settings are the builder's base, tablet and mobile), compiled
to the Tailwind classes the Inspector would have written, after the element's own.

  * alignment (`align`) — a heading's, a text editor's and a button's `text-align`;
  * the `padding` and `margin` of a section and a container (on the Section at the top, on the
    grid or the stack an inner one becomes), of a column (on the Stack its cell becomes, wrapped
    in one even when it holds one widget), of a widget (its Advanced tab's `_padding` and
    `_margin`, on the element it draws, or on a box around it when it draws more than one, or a
    link) and a button's own (`text_padding`) — by side (`py`/`px` when both sides match, else
    `pt`, `pb`, `pl`, `pr`), exact, not rounded to a spacing step;
  * a section's `content_width` and a container's `boxed_width` — the Container's `max-width`;
  * a container's `flex_gap` — the `gap` of the grid or stack it becomes; a section's named
    column `gap` — the grid's `gap` in the pixels Elementor documents for it (no 0, narrow 5,
    default 10, extended 15, wide 20, wider 30), or its `gap_columns_custom`;
  * an image's `width`, and its own size (`width: auto`) when Elementor gives none, in place of
    the builder's full width, and its `align` — its margins (`mx: auto` centred, `ml: auto` to
    the right, `mx: 0` to the left), centred when Elementor says nothing, as Elementor draws it;
  * a heading's, a text editor's and a button's typography — its font's `font-size`, in the unit
    Elementor has (px, em, rem, %, vw, vh), `font-weight` (300 to 800), `line-height`,
    `letter-spacing` and `text-transform`, each at the widths Elementor has them;
  * colours — a heading's (`title_color`), a text editor's (`text_color`) and a button's
    (`button_text_color`) `color`, and the `background` of a button, a section, a column and a
    container whose background is a colour (`background_background` `classic`).

## Elementor's global colours and fonts

A colour or a font a widget names in its `__globals__` is one of the kit's, which the site has as
theme tokens (`MishkaCmsMigration.Sources.WordPress.ElementorKit`), and the page names the token:

  * a colour is the style setting `var(--color-el-<id>,<value>)`, and its dark twin the class
    `dark:text-[var(--color-el-<id>-dark,var(--color-el-<id>,<value>))]` (or `dark:bg-…` for a
    background) in place of the theme's own pair (`text-fg dark:text-fg-dark`, ...): the dark
    theme shows the twin, which an admin sets once in the stylesheet for every page;
  * a font is the class of its family,
    `font-[family-name:var(--font-el-<id>,'<family>',sans-serif)]`, and its weight, size, line
    height, letter spacing and case are the style settings above. Elementor applies a global font
    whole, so the widget's own typography is not read beside it.

Each token has the kit's own colour or family behind it, so a page reads while the stylesheet is
missing, not yet active, or archived.

A family a widget sets itself is the class of the kit's font in that family, the one the
stylesheet loads; any other family is left out, as nothing on the site would load it. A global the
kit does not have, such as a custom colour since deleted, draws nothing, as on WordPress.

A colour of the widget's own is the style setting with its value (`text-[#0f766e]`), in place of
the theme's light colour, and the element keeps the theme's dark twin, so it reads in the dark
theme (`dark:bg-page-dark` for the background of a section, a column or a container, which has
none); a colour that is exactly the light value of one of the theme's text tokens (fg, dim,
subtle, faint, brand) is that token's classes with their dark twins. A text editor's colour, size
and line height are its box's, so its paragraphs, lists and quotes leave theirs out and take the
box's.

Not carried over: a family no font of the kit has, gradients and pictures behind an element,
borders, shadows and hover colours. Elementor's tablet (1024) and mobile (767) widths become the
builder's (900 and 560).

## A post

A post is the Blog's HTML, as a post written in the block editor is: the same elements without
the builder's classes, Sections or Stacks, the columns of a grid as
`<div class="grid gap-6 md:grid-cols-N">`, a gallery as a `<figure>` of figures, a button as a
link in a paragraph, and no spacer.

## Widgets the CMS has no equivalent for

Any other widget, such as Elementor Pro's form or slides, an image carousel or a map, and an
element of another kind (Elementor 4's atomic elements), stands in its place as what Elementor
drew for it: its part of the entry's `content.rendered`, found by its `data-id`, without classes,
styles, `data-` attributes, the `srcset` of other sizes, scripts and decorative (`aria-hidden`)
parts. Its name (`widgetType`, or the element's `elType`) is among the names `call/3` answers,
each once, in the order they come, for the target to ask an admin before it writes the fallback
(`MishkaCmsMigration.Targets.Helpers.Widgets`). One Elementor drew nothing for has no fallback,
and nothing stands in its place: one whose `data-id` the rendering does not have, one whose
part holds nothing but empty boxes, such as a Pro widget on a site without Pro, and every such
widget read from an export, which carries no rendering. `call/3` names those apart.

The images of the image and gallery widgets are read from WordPress with `fetch`
(`/wp/v2/media`), all in one request, for their alt text, caption and attachment page.

# `call`

```elixir
@spec call(
  map(),
  String.t(),
  MishkaCmsMigration.Sources.WordPress.Content.fetch(),
  MishkaCmsMigration.Sources.WordPress.ElementorKit.globals()
) :: {:ok, [map()], [String.t()], [String.t()]} | :none
```

The content pieces of `row`, an entry Elementor laid out, made as a `kind` (`"page"` or
`"post"`) is, the names of the widgets in it the CMS has no equivalent for, and the names of
those of them Elementor drew nothing for, reading its images with `fetch` and naming the site's
`globals` (`MishkaCmsMigration.Sources.WordPress.ElementorKit`); `:none` for an entry Elementor
did not lay out.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
