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

Every icon set the inspector can offer, as one catalogue.

## What changed and why

This module used to BE the heroicon set. It is now a facade over
`MishkaCmsCore.Runtime.PageBuilder.IconLibrary` implementations, of which
`MishkaCmsCore.Runtime.PageBuilder.Icons.Heroicons` is the first — because an install that wants
Lucide as well as Heroicons had nowhere to say so, and the class name already namespaces itself
(`hero-check`, `lucide-check`), so several sets can coexist in one document without colliding.

With one library registered, every function here answers exactly what that library answers. The
picker looks and behaves as it did.

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

## Routing

By longest matching PREFIX, so `svg/2` and `css/1` reach the set that owns the class and nothing
else has to be told which set a value came from. A class no registered set claims is not an error:
`known?/1` answers false and the control keeps the value anyway, because an author may be naming an
icon from a set this editor has never been told about — see
`MishkaCmsCoreWeb.Live.Admin.Builder.Panels`, where the icon control accepts free text for exactly
that reason.

# `all`

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

Every icon class every registered set ships.

# `available?`

```elixir
@spec available?() :: boolean()
```

Whether any registered set has a catalogue. False means the picker degrades to a text box.

# `classes_in`

```elixir
@spec classes_in(String.t()) :: [String.t()]
```

Every icon class named anywhere in `source`, for callers holding markup rather than a tree.

AN ICON IS A CLASS, NOT A GLYPH — `<span class="hero-bell">` plus a CSS mask — so a token in the
text is all there is to find. Anchored on the REGISTERED prefixes rather than on "anything
kebab-shaped": `css/1` would drop the unclaimed ones anyway, and scanning every hyphenated token
means offering tens of thousands of utilities a chance to match.

Shared by `MishkaCmsCore.Runtime.Compilers.TailwindCompiler`, which asks it of a site's templates
to decide what the bundle carries, by `MishkaCmsCore.Runtime.RuntimePreview`, which asks it of
one rendered component so the preview can carry its icons without borrowing a bundle, and by
`MishkaCmsCore.Runtime.ContentComponents.icons/1`, which asks it of stored content.

# `count`

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

How many match, uncapped — what tells a reader their query is too broad.

# `css`

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

CSS rules for these classes, each from the set that owns it, inside Tailwind's `components` layer.

The canvas emits these for the icons it is about to render: the site's compiled stylesheet is
generated from the classes its own pages RENDER, so a component's default icon — or one used only
by an example in the modal — has no rule and draws as an empty box.

In the `components` layer so any utility on the icon (`size-3`, `[&_.icon]:size-5`) beats the
set's default size. Opens with `layer_order/0`, so the block keeps that order wherever it lands
first; put it ahead of any CSS that may declare a layer. `""` when no set ships any of them.

# `known?`

```elixir
@spec known?(String.t() | nil) :: boolean()
```

Whether a value names an icon some registered set actually ships.

# `layer_order`

```elixir
@spec layer_order() :: String.t()
```

Tailwind's own cascade-layer order, the statement `@import "tailwindcss"` opens with.

For CSS that declares layers before any Tailwind stylesheet does; after one it changes nothing.

# `libraries`

```elixir
@spec libraries() :: [module()]
```

The registered libraries, in the order the picker offers them.

Read at runtime so a host can register its own; each library's catalogue is still built at compile
time inside that library, for the reason in `MishkaCmsCore.Runtime.PageBuilder.IconLibrary`.

# `search`

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

The icons matching a query, capped, across every registered set.

`:library` restricts to one set; `:limit` caps the result. The cap is applied to the MERGED list so
a second set cannot push the first out of the picker.

# `svg`

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

One icon as inline SVG at `size` pixels — `nil` when no registered set ships it.

# `variants`

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

The variant suffixes to offer, as `{suffix, label}`.

One library's own variants when only one is registered. With several, only the suffixes they SHARE
are offered, because a variant button that half the results cannot honour reads as broken — a set
with a single weight answers `[{"", "All"}]` and narrows the row to that.

---

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