# Use assets

In the previous tutorial you learned how to describe UI with `Emerge.UI`.

The next step is to bring real media into that UI: images, SVGs, background
images, and custom fonts.

Assets in Emerge are still declarative. You describe a source in your UI tree,
and the renderer resolves it for you.

## Static assets live under `priv/`

Logical asset paths resolve from your app's `priv/` directory.

For example:

- `priv/images/logo.png`
- `priv/images/hero.jpg`
- `priv/icons/check.svg`
- `priv/fonts/Inter-Regular.ttf`

You can refer to those files by logical path string, or you can use the `~m`
sigil for compile-time verification.

## Start with logical path strings

The simplest source form is a logical path string:

```elixir
image([width(px(120)), height(px(120))], "images/logo.png")

svg([width(px(24)), height(px(24))], "icons/check.svg")

el(
  [
    width(px(320)),
    height(px(180)),
    Background.image("images/hero.jpg", fit: :cover)
  ],
  none()
)
```

Logical paths are resolved from the `otp_app` you pass to `EmergeSkia.start/1`
or that the viewport infers for you.

## Prefer `~m` in app code

Import `Emerge.Assets.Path` in the module where you describe UI:

```elixir
defmodule MyApp.UI do
  use Emerge.Assets.Path, otp_app: :my_app
  use Emerge.UI
end
```

Then you can write compile-time verified paths:

```elixir
~m"images/logo.png"
~m"images/hero.jpg"
~m"icons/check.svg"
```

`~m` verifies that the file exists under `priv/` at compile time and tracks it
as an external resource.

## Show an image and a background image

This example uses a normal image element and a background image on a framed
element:

```elixir
column(
  [
    width(fill()),
    height(fill()),
    padding(16),
    spacing(16),
    Background.color(color(:slate, 900)),
    Border.rounded(14)
  ],
  [
    column([spacing(8)], [
      el([Font.color(color(:slate, 50)), Font.size(14)], text("image/2")),
      el(
        [
          padding(10),
          Background.color(color(:slate, 800)),
          Border.rounded(12)
        ],
        image([width(px(120)), height(px(120)), Border.rounded(10)], "sample_assets/static.jpg")
      )
    ]),
    column([spacing(8)], [
      el([Font.color(color(:slate, 50)), Font.size(14)], text("Background.image/2")),
      el(
        [
          width(px(288)),
          height(px(160)),
          padding(12),
          Background.image("sample_assets/fallback.jpg", fit: :cover),
          Border.rounded(12)
        ],
        column([height(fill()), spacing(8)], [
          el(
            [
              padding_xy(10, 6),
              Background.color(color_rgba(15, 23, 42, 0.7)),
              Border.rounded(999),
              Font.color(color(:slate, 50))
            ],
            text("Featured trail")
          ),
          el(
            [
              align_bottom(),
              padding(10),
              Background.color(color_rgba(15, 23, 42, 0.58)),
              Border.rounded(10),
              Font.color(color(:slate, 50))
            ],
            column([spacing(4)], [
              el([Font.size(18)], text("Background image host")),
              el([Font.size(12), Font.color(color(:slate, 200))], text("Foreground content sits on top."))
            ])
          )
        ])
      )
    ])
  ]
)
```

<img src="assets/assets-image-and-background.png" alt="Rendered image and background asset example" width="320">

`image/2` creates an image element.

`Background.image/2` paints an image inside another element's frame.

## Use SVG files

Use `svg/2` when the source is an SVG:

```elixir
row(
  [
    width(fill()),
    height(fill()),
    padding(16),
    spacing(12),
    Background.color(color(:slate, 900)),
    Border.rounded(14)
  ],
  [
    el(
      [
        width(fill()),
        padding(12),
        Background.color(color(:slate, 800)),
        Border.rounded(12)
      ],
      column([center_x(), spacing(8)], [
        svg([width(px(48)), height(px(48))], "sample_assets/template_cloud.svg"),
        el([Font.color(color(:slate, 50)), Font.size(13)], text("Original SVG"))
      ])
    ),
    el(
      [
        width(fill()),
        padding(12),
        Background.color(color(:slate, 800)),
        Border.rounded(12)
      ],
      column([center_x(), spacing(8)], [
        svg(
          [width(px(48)), height(px(48)), Svg.color(color(:sky, 500))],
          "sample_assets/template_cloud.svg"
        ),
        el([Font.color(color(:slate, 50)), Font.size(13)], text("Svg.color/1"))
      ])
    )
  ]
)
```

<img src="assets/ui-assets-svg-example.png" alt="Rendered SVG original and tinted example" width="320">

By default, SVGs keep their original colors.

Use `Svg.color/1` when you want template-style tinting.

## Background image fit modes

`Background.image/2` defaults to `fit: :cover`.

Use:

- `fit: :cover` to fill the frame and crop if needed
- `fit: :contain` to keep the whole image visible
- `Background.tiled/1`, `Background.tiled_x/1`, and `Background.tiled_y/1` for repeat modes

Example:

```elixir
el(
  [
    width(px(220)),
    height(px(120)),
    Background.image("images/logo.png", fit: :contain),
    Border.rounded(12)
  ],
  none()
)
```

## Configure fonts at renderer startup

Fonts work a little differently from images.

Images and SVGs are referenced directly in the UI tree.

Fonts are registered once when the renderer starts, and then selected in UI code
by `family`, `weight`, and `italic`.

If you want multiple variants of the same family, register each variant:

```elixir
{:ok, renderer} =
  EmergeSkia.start(
    otp_app: :my_app,
    title: "My App",
    assets: [
      fonts: [
        [family: "Inter", source: "fonts/Inter-Regular.ttf", weight: 400],
        [family: "Inter", source: "fonts/Inter-Bold.ttf", weight: 700],
        [family: "Inter", source: "fonts/Inter-Italic.ttf", weight: 400, italic: true]
      ]
    ]
  )
```

After that, use the configured family in UI code:

```elixir
column([spacing(8)], [
  el([Font.family("Inter"), Font.size(22), Font.bold()], text("Release notes")),
  el([Font.family("Inter"), Font.regular()], text("Design system updated")),
  el([Font.family("Inter"), Font.italic(), Font.color(color(:slate, 300))], text("Beta"))
])
```

<img src="assets/ui-font-overview.png" alt="Rendered font family, weight, and style example" width="320">

The key idea is:

- `family` selects the registered family
- `Font.bold/0` or `Font.weight(700)` selects the bold variant
- `Font.italic/0` selects the italic variant

If you want a family to support multiple weights or italics, register those
variants in `assets.fonts`.

## Runtime filesystem paths

Emerge also supports runtime filesystem paths:

```elixir
image([width(px(160)), height(px(96))], {:path, "/data/photos/photo.jpg"})
```

Runtime path loading is disabled by default.

Enable it only when needed, and use an explicit allowlist in
`EmergeSkia.start/1`:

```elixir
assets: [
  runtime_paths: [
    enabled: true,
    allowlist: ["/data/photos"],
    follow_symlinks: false
  ]
]
```

## What happens while assets load

Asset loading is asynchronous.

While a source is still loading, Emerge shows a loading placeholder. If loading
fails, Emerge shows a failed placeholder.

You do not need to block rendering while assets are being resolved.
