Mob.Renderer (mob v0.8.3)

Copy Markdown View Source

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_5000xFF2196F3.

Spacing props (:padding, :padding_top, etc., :gap, :spacing, :run_spacing): 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.

Font props (:font): an atom (:heading) resolves through the active theme's fonts map to a platform-specific family name; an unresolved atom or a raw string passes through unchanged. When the theme declares a :default font token, any node without its own font: gets it injected automatically — see Mob.Theme's "Font tokens" section.

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)

Summary

Functions

Return the set of prop keys resolved as colors (theme token or ARGB integer).

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

Render a component tree for the given platform.

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

Functions

color_props()

@spec color_props() :: [atom()]

Return the set of prop keys resolved as colors (theme token or ARGB integer).

colors()

@spec colors() :: %{required(atom()) => non_neg_integer()}

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

render(tree, platform, nif \\ :mob_nif, transition \\ :none)

@spec render(map(), atom(), module() | atom(), atom() | {atom(), :replace}) ::
  {: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 for the nav animation: :push, :pop, :reset or :none, defaulting to :none (instant swap).

For a navigation that REPLACES the stack, the router passes {animation, :replace} instead. The animation half still reaches set_transition/1 as a plain atom — the vocabulary native matches on stays closed — and the replacement half is emitted as a "replaces_stack" key on the JSON root, which tells native the outgoing screen is unreachable and its retained view tree can be released.

The flag is deliberately NOT folded into the transition atom. That atom's name reaches Android as a raw string and MainActivity.kt matches "push"/"pop"/"reset" with an exact when, so any value outside that set falls through to no animation at all. An unknown JSON key is skipped by every parser on both platforms, so a host that does not read it keeps its behaviour.

Callers never construct the tuple; Mob.Socket validates the public :push | :pop | :reset vocabulary.

text_sizes()

@spec text_sizes() :: %{required(atom()) => float()}

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