# `MishkaCmsMigration.Targets.CmsStylesheet`
[🔗](https://github.com/mishka-group/mishka_cms_migration/blob/v0.0.1/lib/mishka_cms_migration/targets/cms_stylesheet.ex#L5)

The `"stylesheet"` items of a source, such as Elementor's global colours and fonts as theme tokens
(`MishkaCmsMigration.Sources.WordPress.ElementorKit`), written as a stylesheet of the run's site
(`MishkaCmsCore.Runtime.Stylesheet`) through the `:create` and `:update` the Stylesheets page
runs, as the admin who pressed Import: the admin needs `admin:dashboard:runtime:stylesheet` (or
the code permission), and the core's own rules decide the rest. A stylesheet Tailwind will not
build is refused (`MishkaCmsCore.Runtime.Resources.Changes.StylesheetCompiles`), and each write
builds the site's CSS again.

An item's `"name"`, `"description"` and `"content"` are the stylesheet's own. It is written as a
`:theme` stylesheet, active, after the site's own `site-theme` (priority 20), so the tokens it
declares are there before Tailwind makes a page's classes. An admin edits it, turns it off or
deletes it on the Stylesheets page like any other.

## The site's own

A stylesheet of the same name the site already has, which no run wrote, is never written over on
the import's own say: read before anything is written, in the dry run too (`existing/2`), the
item waits for an admin's decision as `"site_has_own"` (`MishkaCmsMigration.MigrationItem`'s
Decide…). Keep the site's own skips it as `"site_own_kept"`; Use the WordPress one writes the
item over that stylesheet through the `:update`, which keeps its id.

A re-run is held to the rules every target is: a stylesheet the source did not change is
`"unchanged"`, one an admin changed in the CMS since is kept as the admin left it, one deleted for
good waits for the admin's decision (`"deleted_in_cms"`): Import it again writes it as a first
import would, and Leave it out leaves it deleted (`"target_missing"`), and one an admin archived
waits for the admin's decision (`"archived_in_cms"`, `MishkaCmsMigration.Workers.Helpers.Decision`):
Restore it brings it back through the Stylesheets page's own Restore, inactive until an admin
activates it.

## Google fonts

An item's `@import` of a Google Fonts stylesheet (`https://fonts.googleapis.com/css…`), such as
the one Elementor's global fonts make, is written as the `@font-face` rules Google answers for it.
The site's Content-Security-Policy (`MishkaCmsWeb.Runtime.Plugs.SecurityHeaders`) refuses a
stylesheet from Google on an admin page other than the builder's canvas, such as a page's
preview, while its `font-src` lets every page load the font files from `fonts.gstatic.com`, so
with the rules in the site's own CSS the fonts load wherever the CSS does, and a visitor's
browser asks Google for no stylesheet, only for the files of the characters a page shows.

In an import, the stylesheet is asked of Google before it is written (`prepare/3`), outside the
write's transaction: one request for each such `@import`, with `Req`, as a browser asks it (a
browser's `user-agent`, for Google to answer `woff2` files split by `unicode-range`), following
no redirect and retrying nothing. A dry run asks nothing, and nor does a re-run that finds the
stylesheet `"unchanged"`. Only the answer's `@font-face` rules are kept, each written again from
its font descriptors (`font-family`, `font-style`, `font-weight`, `font-stretch`,
`font-display`, `src`, `unicode-range`), read with igniter_css; an answer that is not a `200` of
`text/css`, no answer, and one without a rule keep the item's `@import`, which a public page's
policy allows, and its item records no hash (`recorded/2`), so the next run writes the stylesheet
again and asks once more.

`req_options` in the application environment are merged into every request, which is how the
suite answers them from what Google answered:

    config :mishka_cms_migration, MishkaCmsMigration.Targets.CmsStylesheet,
      req_options: [plug: {Req.Test, MishkaCmsMigration.Sources.WordPress}]

# `error_return`

```elixir
@type error_return() ::
  {:error, [%{action: atom(), field: atom(), message: String.t()}]}
```

# `okey_return`

```elixir
@type okey_return() :: {:ok, struct() | map() | module() | [any()]}
```

# `child_spec`

Returns a specification to start this module under a supervisor.

See `Supervisor`.

# `config`

```elixir
@spec config() :: keyword()
```

# `config`

```elixir
@spec config(atom()) :: any()
```

# `enable`

```elixir
@spec enable() :: okey_return() | error_return()
```

# `get`

```elixir
@spec get() :: keyword()
```

# `health_check`

```elixir
@spec health_check() :: :ok | {:degraded, term()} | {:error, term()}
```

Optional. See `c:MishkaInstaller.Event.Hook.health_check/0`. Override to report health.

# `on_dependency_error`

```elixir
@spec on_dependency_error(term()) :: term()
```

Optional. See `c:MishkaInstaller.Event.Hook.on_dependency_error/1`. Override to react.

# `register`

```elixir
@spec register() :: okey_return() | error_return()
```

# `restart`

```elixir
@spec restart() :: okey_return() | error_return()
```

# `start`

```elixir
@spec start() :: okey_return() | error_return()
```

# `start_link`

# `stop`

```elixir
@spec stop() :: okey_return() | error_return()
```

# `unregister`

```elixir
@spec unregister() :: okey_return() | error_return()
```

---

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