# `Macaw.Layers.Theme.Document`
[🔗](https://github.com/dcoai/macaw_layers/blob/v0.3.0/lib/macaw/layers/theme/document.ex#L8)

A theme as a document (spec/04 §5): stored and shipped as a value, and judged as one.

  * the value is `%{version: v, theme: node}` (`to_map/1`), and a store's fragment
    `%{theme: node}` (`fragment/1`), a library entry of kind `:theme`, reads as the same
    document;
  * the JSON form is `{"$version": v, "theme": {…}}` (`to_json/1`), each color a tag (a
    literal `$rgb`, `$color`, `$series`, `$ramp`, the transforms by name, `$blend`,
    `$contrast`) and pinned steps `$steps`, written and read by the JSON adapter
    (`Macaw.Layers.Form`, `Macaw.Layers.Color.Form`), every name in `ramps`, `colors`,
    `styles` and `defaults` a string, as the theme holds it, and every word (an entry's
    keys and atom values) read from the library's vocabulary and the registered
    extensions' words (`Macaw.Layers.Theme.Extension.words/0`).

`from_map/2` judges the value as terms, after the version check, with no form written
or read; `from_json/2` reads the JSON form through the adapter's gates (spec/01 §5: the
limits, the version, the migration) to the same value. A name given both as its atom and
as its string in one name map of a value is `{:duplicate, :name}` at its path. Then: a
node that is a **name** reads as the built-in theme of that name, any other name is
`{:unknown_theme, name}`; a variable anywhere is `{:unresolved, :var, name}`, since
nothing applies a theme; a key the theme does not have is `:unknown_key`; and every
contract a ramp, color or palette entry breaks is `{:contract, message}` at
`[:theme, :ramps | :colors | :styles, name]`.

The line is at version 2: a literal color is a term, tagged `$rgb` in the JSON form.
Version 1 wrote it as CSS text; its step tags each string at a color position that
`Macaw.Layers.Color.parse/1` reads, so a version 1 document, or a bundle's entry of one,
reads to the same theme.

As a fragment type (spec/02 §1), `ramps`, `colors`, `styles` and `defaults` are `:union`
name maps and every other key `:override`, so a theme document stacks over a built-in as
`Macaw.Layers.Theme.new/1` merges.

    iex> theme = Macaw.Layers.Theme.new(name: :house, axis: Macaw.Layers.Color.parse!("#555555"))
    iex> Macaw.Layers.Theme.Document.from_map(Macaw.Layers.Theme.Document.to_map(theme))
    {:ok, theme}
    iex> Macaw.Layers.Theme.Document.from_map(%{theme: :dark})
    {:ok, Macaw.Layers.Theme.dark()}

Ported from visualize's `Visualize.Chart.Theme`.

# `fragment`

```elixir
@spec fragment(Macaw.Layers.Theme.t()) :: %{theme: map()}
```

`%{theme: node}`: a store's fragment of kind `:theme`.

# `from_json`

```elixir
@spec from_json(String.t(), keyword()) ::
  {:ok, Macaw.Layers.Theme.t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The theme a JSON document holds: read through the JSON adapter (`Macaw.Layers.Form.read/3`:
every gate, then the value, judged), then built as `from_map/2` builds it. `limits:` as
`Macaw.Layers.Form.gates/3` takes them, and `extensions:` as `from_map/2`: the reader's
vocabulary is the library's words and these extensions' (`Extension.words/0`), so an
adopter's words read back only with its extension given, and no other atom is made.

# `from_map`

```elixir
@spec from_map(term(), keyword()) ::
  {:ok, Macaw.Layers.Theme.t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The theme a document value or a theme fragment holds, judged as terms, or every fault.
`extensions:` the extensions the host registers, which the theme carries and its
contracts hold. No form is written or read, so no limit applies: the limits bound what
reading an encoding costs (`from_json/2`).

# `node`

```elixir
@spec node(Macaw.Layers.Theme.t()) :: map()
```

The theme as its node: every field but `extensions`, an unset counterpart and empty
defaults left out. Its names are strings, as the theme holds them (spec/04 §2.1), and
its words as given.

# `to_json`

```elixir
@spec to_json(Macaw.Layers.Theme.t()) ::
  {:ok, String.t()} | {:error, [Macaw.Layers.Fault.t(), ...]}
```

The JSON document, `{:ok, json}`, stamped `"$version"`, its words written with the
theme's extensions' (`Macaw.Layers.Theme.Extension.words/0`).

# `to_map`

```elixir
@spec to_map(Macaw.Layers.Theme.t()) :: %{version: pos_integer(), theme: map()}
```

`%{version: current, theme: node}`.

---

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