# Mappings

Mappings write the values of a row into the base page. Add them on the
group's **Mappings** page with **Add mapping**. Each mapping has a **Type**, a
**Target**, a **Record field** and, depending on the type, a **Mode** or an
**Attribute**.

Mappings work on the complete rendered HTML of the page, theme output
included, and run in the order they are listed. They use simple pattern
matching rather than a full HTML parser, and each mapping changes only the
**first** element it matches. A mapping whose field the row does not have
leaves the page unchanged.

The **Record field** names a field of the row. A dot reaches into nested
data, so `address.city` reads `city` inside the row's `address` object.

## HTML tag

Writes the value into the first element with a given tag name.

- **Target**: the tag name, such as `h1`, `h2` or `title`
- **Mode**: **Replace**, **Append** or **Prepend** the element's contents

A tag mapping on `h1` with the field `heading` turns the first `<h1>` into the
row's heading. Use a tag mapping on `title` to change the HTML `<title>`
element; its value is written as plain text, while other tags accept HTML.
Give the element placeholder contents: the pattern does not match an empty
one.

When **Replace** finds an element whose contents are a single child element,
such as `<h1><span>Text</span></h1>`, it keeps the child and replaces the
child's contents.

## CSS selector

Writes the value into the first element a selector names, or into one of its
attributes.

- **Target**: the selector
- **Mode**: **Replace**, **Append** or **Prepend** the element's contents
- **Attribute**: optional; when set, the value replaces that attribute instead
  of the contents

Three selector forms are supported:

- `.name` matches the first element that has the class `name`
- `#name` matches the element whose `id` is `name`
- a bare tag name, such as `h2`, matches the first element with that tag

Combined, nested and attribute selectors such as `p.lead`, `.hero .title` or
`[data-title]` match nothing.

The element's contents end at its own closing tag, even when the same tag
nests inside it. **Replace** keeps a single child element as a tag mapping
does, so `<p id="intro"><strong>Text</strong></p>` keeps its `<strong>`.

The **Attribute** option rewrites an attribute the element already has, on the
first element that matches the selector and carries that attribute; it never
adds one. Use it for an image's `src` or `alt`, a link's `href`, or a `data-`
attribute.

## List

Fills an existing list with the items of an array field. A list mapping
creates no elements: it reuses the `<li>` elements already in the base page.

- **Target**: the container, as `.class`, `#id` or a tag name
- **Record field**: the field that holds the array

For example, a base page with this markup:

```html
<div id="benefits">
  <ul>
    <li><span>First benefit</span></li>
    <li><span>Second benefit</span></li>
    <li><span>Third benefit</span></li>
    <li><span>Fourth benefit</span></li>
    <li><span>Fifth benefit</span></li>
  </ul>
</div>
```

with a list mapping whose target is `#benefits` and whose field is
`benefits`, and a row with `"benefits": ["Fast", "Reliable", "Secure"]`, keeps
the first three `<li>` elements with the three items and removes the other
two.

How the items are placed:

- the first `<li>` in the container gets the first item, the second the
  second, and so on
- in each `<li>`, the item replaces the contents of the first `<span>` that
  has no attributes; give that `<span>` placeholder text, because an empty one
  is not replaced. Other markup in the `<li>`, such as an icon, stays
- `<li>` elements beyond the length of the array are removed
- items beyond the number of `<li>` elements are dropped, so the base page
  needs as many placeholders as the longest list in your data
- a field that is missing or not an array leaves the list unchanged

The container is found more loosely than a selector mapping's element: `.name`
matches any element whose `class` attribute contains `name` anywhere, and the
container ends at the first closing tag with its name. Prefer an `id`, and do
not nest an element of the container's tag inside it.

The field must hold an array. JSON file, JSON URL and REST API sources can
provide one. CSV, Google Sheets and Notion rows hold only flat values (a
Notion multi-select arrives as one comma-separated string), so they cannot
feed a list mapping.

## Page metadata

Sets a meta tag in the page's head.

- **Target**: the meta tag, such as `description`, `keywords`, `og:title`,
  `og:image` or `twitter:title`

Targets that start with `og:` are written as `<meta property="…">`, all
others as `<meta name="…">`. Rank replaces the first existing tag whose
`name` or `property` and `content` are its only attributes, in either order;
when there is none, it adds the tag before `</head>`.

To change the page's `<title>`, use an HTML tag mapping on `title`.

## Changing values in code

Every value a tag, selector or meta mapping writes passes through the
`sleekRank/replacement/value` filter and then
`sleekRank/replacement/value/{type}`, and the page's HTML passes through
`sleekRank/replacements/before` and `sleekRank/replacements/after` around the
mappings. A plugin can also add its own mapping type through
`sleekRank/register/replacements`. See [Hooks](/docs/rank/hooks.md).
