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

The heroicon set, as a `MishkaCmsCore.Runtime.PageBuilder.IconLibrary`.

## Why this exists

An `icon` attribute's value is a **class name** — `hero-check`, `hero-check-solid` — resolved by
the Tailwind heroicons plugin at `priv/static/temp/js_assets/vendor/heroicons.js`, which reads the
SVGs out of `deps/heroicons/optimized` and emits one `hero-*` component class per file. Nothing in
the kit bundle carries that list: Chelekom's `icon` component declares `attr :name, :string` with
no values and no docs, so the editor's only options were to guess or to make the author type
`hero-exclamation-triangle` from memory, with a typo producing a silently missing glyph.

So the list is read from the same directory the plugin reads, **at compile time**: the `heroicons`
dependency in the deps of the project being built (`Mix.Project.deps_path/0`) — this library's
own when it is built alone, its host's when it is a dependency. Compile time is the point: `deps/`
is a build-time artefact and a release does not ship it, while the class names the release renders
are baked into its CSS — reading at runtime would work in dev and return an empty picker in
production.

## The four variants

The plugin's own table, which this mirrors exactly:

    ""       → 24/outline    (the default weight)
    "-solid" → 24/solid
    "-mini"  → 20/solid
    "-micro" → 16/solid

A name is offered in every variant the set actually ships it in — `16/solid` carries eight fewer
icons than the others, so a blanket suffix list would advertise glyphs that render as nothing.

## When the catalogue is empty

If the directory cannot be found the module compiles to an empty catalogue rather than failing the
build, and `available?/0` answers false for this set: every variant lists no names, `svg/2` is
`nil` and `css/1` is `""`. The picker then falls back to a plain text box — a host that does not
use heroicons loses the picker, not the editor.

# `all`

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

Every icon class the set ships, sorted — `hero-academic-cap`, `hero-academic-cap-micro`, …

# `available?`

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

Whether a catalogue was found at compile time. False means the picker degrades to a text box.

# `count`

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

How many icons the query matches in a variant, for the picker's counter.

Counted rather than `length(search(...))` because the search is capped — the count is what tells a
reader their query is too broad, and a capped list cannot say so.

# `css`

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

CSS rules for the named icon classes, mirroring what the Tailwind heroicons plugin emits.

The editor cannot rely on the site's compiled stylesheet for these. That stylesheet is generated
from the classes the site's own pages RENDER, and a component's *default* icon appears in no page's
HTML — so `chelekom-alert`'s `hero-chat-bubble-bottom-center-text` had a 28×28 box on the canvas
with `mask-image: none` inside it, which reads as "the icon is broken" rather than "this class was
never compiled".

The rule is the plugin's own, restated: the SVG as a data URI mask, `currentColor` as the paint, and
the plugin's size table (24px, or 20px for `-mini`, 16px for `-micro`). Only the classes actually
in front of the reader are emitted — a few per page, not all 1,288.

# `known?`

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

Whether a value names an icon this set actually ships.

# `names`

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

The bare names in one variant, without the `hero-` prefix or the suffix.

# `search`

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

The icons matching a query, capped.

The query is matched against the name with its `hero-` prefix and variant suffix stripped, so
"arrow" finds `hero-arrow-up` and `hero-arrow-up-solid` alike, and a reader who types the whole
class name still finds it. An empty query returns the head of the list rather than nothing: an
empty picker looks broken, and the first page of icons is a reasonable place to start browsing.

# `svg`

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

One icon as inline SVG, sized to `size` pixels — `nil` when the set does not ship it.

The source file's own `<svg>` attributes are kept (viewBox, fill, stroke-width), because a 20px
solid icon and a 24px outline icon are drawn differently and a single hardcoded viewBox would
squash half the set.

# `variants`

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

The variant suffixes, in the plugin's own order — outline first, because it is the default.

---

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