# `Mob.Renderer`
[🔗](https://github.com/genericjam/mob/blob/master/lib/mob/renderer.ex#L1)

Serializes a component tree to JSON and passes it to the platform NIF in
a single call. Compose (Android) and SwiftUI (iOS) handle diffing and
rendering internally.

## Node format

    %{
      type: :column,
      props: %{padding: :space_md, background: :surface},
      children: [
        %{type: :text,   props: %{text: "Hello", text_size: :xl, text_color: :on_surface}, children: []},
        %{type: :button, props: %{text: "Tap", on_tap: self()},    children: []}
      ]
    }

## Token resolution

Atom values for color props, spacing props, radius props, and text sizes are
resolved at render time through the active `Mob.Theme` and the base palette.

**Color props** (`:background`, `:text_color`, `:border_color`, `:color`,
`:placeholder_color`): resolved via theme semantic tokens first, then the
base palette. E.g. `:primary` → theme's primary → `:blue_500` → `0xFF2196F3`.

**Spacing props** (`:padding`, `:padding_top`, etc., `:gap`): accept spacing
tokens (`:space_xs`, `:space_sm`, `:space_md`, `:space_lg`, `:space_xl`)
that are scaled by `theme.space_scale`.

**Radius props** (`:corner_radius`): accept `:radius_sm`, `:radius_md`,
`:radius_lg`, `:radius_pill` from the active theme.

**Border** (currently honored on `:box` only): set both `:border_color`
(a color token like `:primary` or `:border`) and `:border_width` (an
integer pt/dp value, e.g. `1`). When width is 0 or color is unset, no
border draws.

**Text size props** (`:text_size`, `:font_size`): token atoms (`:xl`, `:lg`,
etc.) are multiplied by `theme.type_scale`.

## Component defaults

When a component's props omit styling keys, the renderer injects sensible
defaults from the active theme. Explicit props always win over defaults.

    # Gets primary background, white text, medium radius automatically:
    %{type: :button, props: %{text: "Save", on_tap: {self(), :save}}, children: []}

## Style structs

A `%Mob.Style{}` value under the `:style` key is merged into the node's
own props before serialisation. Inline props override style values.

## Platform blocks

Props scoped to one platform are silently ignored on the other:

    props: %{padding: 12, ios: %{padding: 20}}
    # iOS sees padding: 20; Android sees padding: 12

## Injecting a mock NIF

    Mob.Renderer.render(tree, :android, MockNIF)

# `colors`

```elixir
@spec colors() :: %{required(atom()) =&gt; non_neg_integer()}
```

Return the full color palette map (token → ARGB integer).

# `render`

```elixir
@spec render(map(), atom(), module() | atom(), atom()) ::
  {:ok, :json_tree} | {:error, term()}
```

Render a component tree for the given platform.

Loads the active `Mob.Theme`, clears the tap registry, serialises the tree
to JSON, and calls `set_root/1` on the NIF. Returns `{:ok, :json_tree}`.

`transition` is an atom (`:push`, `:pop`, `:reset`, `:none`) for the nav
animation. Defaults to `:none` (instant swap).

# `text_sizes`

```elixir
@spec text_sizes() :: %{required(atom()) =&gt; float()}
```

Return the text-size scale map (token → float sp).

---

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