# `MishkaCmsCore.Runtime.PageBuilder.IconLibrary`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/runtime/page_builder/icon_library.ex#L5)

What an icon set has to answer so the inspector can offer it.

## Why a behaviour rather than one hardcoded set

An icon attribute's value is a **class name** — `hero-check`, and tomorrow `lucide-check` or
`tabler-check`. The class already carries its own namespace, so several sets can coexist in one
document without colliding; what was missing was anywhere for the editor to learn about the second
one. `MishkaCmsCore.Runtime.PageBuilder.Icons` is now a facade over every registered set, and
`MishkaCmsCore.Runtime.PageBuilder.Icons.Heroicons` is simply the first.

## Registering one

    config :mishka_cms_core, :icon_libraries, [
      MishkaCmsCore.Runtime.PageBuilder.Icons.Heroicons,
      MyApp.Icons.Lucide
    ]

The list is read at runtime, but each library's own catalogue must be built at COMPILE time. That
is not a style preference: `deps/` is a build artefact that a release does not ship, while the
class names a release renders are baked into its CSS. A library that walks `deps/` at runtime works
in dev and returns an empty picker in production.

## What a library owes the editor

Two things the panel cannot do for itself. `svg/2` draws the glyph in the picker — the picker
cannot render `<span class="hero-check">` and hope, because that class comes from a Tailwind plugin
that runs over the SITE's pages and the admin chrome has no reason to carry it. And `css/1` emits
the rule for a class the canvas is about to render, for the same reason: a component's *default*
icon appears in no page's HTML, so the site stylesheet never learned it.

## Prefixes

The facade routes by LONGEST matching prefix, so two libraries claiming `hero-` and `hero-solid-`
resolve unambiguously — the more specific one wins. Use the prefix the set's own Tailwind plugin
emits.

# `all`

```elixir
@callback all() :: [String.t()]
```

Every class this set ships, sorted.

# `count`

```elixir
@callback count(String.t() | nil, String.t()) :: non_neg_integer()
```

How many match, uncapped — the picker's counter cannot be derived from a capped list.

# `css`

```elixir
@callback css([String.t()]) :: String.t()
```

CSS rules for these classes, mirroring what the set's own Tailwind plugin emits.

Bare rules: `MishkaCmsCore.Runtime.PageBuilder.Icons.css/1` puts them in the `components` layer.

# `label`

```elixir
@callback label() :: String.t()
```

What to call the set in the picker when more than one is installed — `"Heroicons"`.

# `prefixes`

```elixir
@callback prefixes() :: [String.t()]
```

The class-name prefixes this library owns — `["hero-"]`.

A LIST because one module may serve several sets: `MishkaCmsCore.Runtime.PageBuilder.Icons.Bundled`
answers for every set that arrived inside an installed kit, and there is no reason for each of those
to need a module of its own.

# `search`

```elixir
@callback search(String.t() | nil, keyword()) :: [String.t()]
```

The classes matching a query in one variant, capped by `:limit`.

# `svg`

```elixir
@callback svg(String.t(), pos_integer()) :: {:safe, iodata()} | nil
```

One icon as inline SVG at `size` pixels, or `nil` when this set does not ship it.

# `variants`

```elixir
@callback variants() :: [{String.t(), String.t()}]
```

The weight variants, as `{suffix, label}` — `{"", "Outline"}`, `{"-solid", "Solid"}`.

A set with one weight answers `[{"", "All"}]`.

---

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