
# Structured Data & Favicons

Three parts of `meta` are less about words and more about machines: the JSON-LD that describes the page to search engines, the icons that represent it in a browser, and the hints that make it load faster.

## `structuredData`

Emitted as a `<script type="application/ld+json">` block. It accepts raw Schema.org JSON-LD verbatim:

```yaml
meta:
  structuredData:
    '@context': https://schema.org
    '@type': Article
    headline: '$record.title'
    author: { '@type': Person, name: '$record.author' }
    image: '$record.cover'
    datePublished: '$record.published_at'
```

:::callout
**`structuredData` is passed through unvalidated.** Unlike the rest of `meta`, its contents are not schema-checked — a malformed `@type` or a misspelled property ships silently. Validate the emitted block with Google's Rich Results Test rather than relying on `sovrium validate`.
:::

### Auto-synthesised article schema

A [content directory](/en/docs/pages-collections#contentdir) can generate JSON-LD per article from frontmatter instead, which is how this documentation does it. Supply the object form and the synthesiser takes over:

| Property       | Description                                                        |
| -------------- | ------------------------------------------------------------------ |
| `enabled`      | Must be exactly `true`. Any other value leaves synthesis off.      |
| `type`         | `TechArticle` (default) or `Article`.                              |
| `breadcrumbs`  | Emit a `BreadcrumbList` alongside the article. Defaults to `true`. |
| `organization` | Organization name, emitted as the article's `publisher`.           |

```yaml
meta:
  structuredData:
    enabled: true
    type: TechArticle
    breadcrumbs: true
    organization: Sovrium
```

Headline, description and publication date come from each file's frontmatter, so one declaration covers every article in the directory.

## `favicons`

Favicons accept two shapes. The **object form** is the short one:

| Property         | Description                                 |
| ---------------- | ------------------------------------------- |
| `icon`           | Default icon path.                          |
| `appleTouchIcon` | iOS home-screen icon.                       |
| `sizes`          | Size-specific icons, each `{ size, href }`. |

```yaml
meta:
  favicons:
    icon: ./favicon.svg
    appleTouchIcon: ./apple-touch-icon.png
    sizes:
      - { size: '32x32', href: ./favicon-32.png }
      - { size: '16x16', href: ./favicon-16.png }
```

The **array form** gives one entry per link tag, and is the only way to declare a Safari pinned-tab mask icon:

| Property | Description                                                           |
| -------- | --------------------------------------------------------------------- |
| `rel`    | **Required.** `icon`, `apple-touch-icon`, `manifest`, or `mask-icon`. |
| `href`   | **Required.** Path, which must start with `./`.                       |
| `type`   | MIME type.                                                            |
| `sizes`  | Size in `WxH` form.                                                   |
| `color`  | Tint colour, used by `mask-icon`.                                     |

```yaml
meta:
  favicons:
    - { rel: icon, href: ./favicon.svg, type: image/svg+xml }
    - { rel: mask-icon, href: ./mask.svg, color: '#6366f1' }
```

For a single icon and nothing else, the singular `meta.favicon` takes a bare path.

## Performance Hints

### `preload`

Critical resources to fetch early, as an array.

| Property      | Description                                                                                 |
| ------------- | ------------------------------------------------------------------------------------------- |
| `href`        | **Required.** Resource URL.                                                                 |
| `as`          | **Required.** `style`, `script`, `font`, `image`, `video`, `audio`, `document`, or `fetch`. |
| `type`        | MIME type.                                                                                  |
| `crossorigin` | `true`, `anonymous`, or `use-credentials`.                                                  |
| `media`       | Media query restricting when the preload applies.                                           |

Preloading a font is the highest-value case: it removes a round trip from the critical path that would otherwise start only after the stylesheet parses.

### `dnsPrefetch`

A plain array of origins to resolve ahead of time — no object wrapper:

```yaml
meta:
  preload:
    - { href: /fonts/inter.woff2, as: font, type: font/woff2, crossorigin: anonymous }
  dnsPrefetch:
    - 'https://cdn.example.com'
```

### `customElements`

Anything else the head needs, as an array of tags.

| Property  | Description                                                 |
| --------- | ----------------------------------------------------------- |
| `type`    | **Required.** `meta`, `link`, `script`, `style`, or `base`. |
| `attrs`   | Attributes as a key-value record.                           |
| `content` | Text content, for `script` and `style`.                     |

```yaml
meta:
  customElements:
    - { type: meta, attrs: { name: theme-color, content: '#0f172a' } }
```

## Related Pages

- [SEO & Metadata](/en/docs/seo-meta) — title, description, Open Graph, Twitter cards.
- [Collection & Markdown Pages](/en/docs/pages-collections) — the content directories synthesis applies to.
- [Scripts](/en/docs/interactivity-scripts) — loading JavaScript, as opposed to hinting at it.
- [Ecoconception](/en/docs/ecoconception) — the footprint case for fewer, earlier requests.
