# `Iconex`
[🔗](https://github.com/paradox460/iconex/blob/main/lib/iconex.ex#L1)

Auto tree-shaken icons for Phoenix, from several icon sets.

## Usage

Each icon set has its own component:

    <Iconex.lucide name="house" class="w-5 h-5" />
    <Iconex.lucide name="circle" size={20} />
    <Iconex.carbon name="add" />
    <Iconex.carbon name="arrow--right" size={20} />

`import Iconex` lets you write `<.lucide name="house" />` and
`<.carbon name="add" />` instead.

## Icon sets

| Set | Component | Names |
|-----|-----------|-------|
| [Lucide](https://lucide.dev/icons/) | `lucide/1` | `"house"`, `"arrow-up-right"` |
| [IBM Carbon](https://carbondesignsystem.com/elements/icons/library/) | `carbon/1` | `"add"`, `"arrow--right"`, `"logo--github"` |

Every set is enabled by default. To compile in only some of them:

    # config/config.exs
    config :iconex, sets: [:lucide]

The component of a disabled set is not defined, and the compiler reports
every call to it.

## How it works

`Mix.Tasks.Compile.Iconex` reads your compiled modules after each build
and writes a pruned `priv/static/images/iconex.svg` containing only the
icons used, from every set. Register it after the default compilers in
`mix.exs`:

    compilers: Mix.compilers() ++ [:iconex]

Icons are rendered using [svg
sprites](https://css-tricks.com/svg-sprites-use-better-icon-fonts/) via
`<use>` references to symbols named `#<set>-<name>`, e.g. `#lucide-house`.

Icon names are validated against the bundled data in `priv/sets/`; run
`mix iconex.download SET` to use a newer release of a set.

In the case of icon names that are dynamically called at runtime, such as a
conditional switching between two icons, you must _hint_ the compiler, so it
emits both icons. See `hint/2` for details.

## Root layout setup

Place `Iconex.sprite_tag/0` once at the top of `<body>`:

    # root.html.heex
    <%= Iconex.sprite_tag() %>

## Delivery modes

The default delivery is `:external`: `sprite_tag/0` emits nothing and icons
reference `/images/iconex.svg`, which the browser fetches and caches once.

For development you can embed the sprite in the page instead, so no static
file needs serving:

    # config/dev.exs
    config :iconex, delivery: :inline, otp_app: :my_app

This has added advantage that hot reloads can render new sprites without
additional network requests.

In `:inline` mode `sprite_tag/0` reads the pruned sprite from `:otp_app`
(at `:output_path`) and re-reads it whenever the compiler rewrites it.
Without `:otp_app` it embeds every icon.

`:sets`, `:delivery`, `:sprite_url`, and each set's `:data_path` (and
Lucide's `:stroke_width`) are read at compile time, so run
`mix deps.compile iconex --force` after changing them.

## Per-set configuration

Options that apply to one set live under its id:

    config :iconex,
      lucide: [stroke_width: 1.5, extra_icons: ~w[loader]]

## Lucide stroke width

Every Lucide icon is drawn at stroke width `2`, Lucide's own, unless the
`stroke_width` attribute says otherwise. Set an app-wide default with:

    config :iconex, lucide: [stroke_width: 1.5]

The width is in the 24-unit viewBox, so it scales with the icon.

## Serving the static file

Phoenix's default `Plug.Static` setup serves `priv/static/images`, which is
where the sprite is written, so the defaults need no configuration.
If you serve static files from elsewhere, set both the file location and the
URL it is served at:

    config :iconex,
      output_path: "priv/static/assets/iconex.svg",
      sprite_url: "/assets/iconex.svg"

## Icon data

`mix iconex.download SET` fetches a set's data. To use a copy newer than
the one bundled with iconex, point the set's `:data_path` at it (the path
must be absolute):

    config :iconex, lucide: [data_path: Path.expand("../priv/iconex/lucide.json", __DIR__)]

# `carbon`

Renders an [IBM Carbon](https://carbondesignsystem.com/elements/icons/library/) icon.

Names are the `@carbon/icons` names, e.g. `"add"`, `"arrow--right"`,
`"logo--github"`, matched case-sensitively. The 32px master artwork is
used at every size.

## Attributes

* `name` (required) Carbon icon name
* `size` integer pixel size applied to `width` and `height` (default `16`)
* `class` CSS class string, added after `iconex iconex-carbon`
* Any other attribute is forwarded to the outer `<svg>` element.

## Examples

    <Iconex.carbon name="add" />
    <Iconex.carbon name="logo--github" class="text-gray-500" size={32} />

## Attributes

* `name` (`:string`) (required)
* `size` (`:integer`) - Defaults to `16`.
* `class` (`:string`) - Defaults to `nil`.
* Global attributes are accepted.

# `hint`

Marks icon names of `set` as used so the compiler includes them in the sprite.

Use it for names only known at runtime, next to the code that picks them:

    icon = if @open, do: "chevron-up", else: "chevron-down"
    Iconex.hint(:lucide, ~w[chevron-up chevron-down])

The compiler only counts calls whose set is a literal atom and whose names
are literal strings, so `~w[...]` works and names built at runtime don't.
Returns the list unchanged.

# `lucide`

Renders a [Lucide](https://lucide.dev/icons/) icon.

## Attributes

* `name` (required) kebab-case icon name, e.g. `"house"`, `"arrow-up-right"`
* `size` integer pixel size applied to `width` and `height` (default `24`)
* `class` CSS class string, added after `iconex iconex-lucide`
* `stroke_width` stroke width, any positive number (default `2`, or
  `config :iconex, lucide: [stroke_width: ...]`)
* Any other attribute is forwarded to the outer `<svg>` element.

## Examples

    <Iconex.lucide name="house" />
    <Iconex.lucide name="loader" class="loading-icon" size={20} />

## Attributes

* `name` (`:string`) (required)
* `size` (`:integer`) - Defaults to `24`.
* `class` (`:string`) - Defaults to `nil`.
* `stroke_width` (`:any`) - Defaults to `2`.
* Global attributes are accepted.

# `sprite_tag`

Emits the SVG sprite element to be placed once in your root layout.

In `:inline` delivery mode this embeds the sprite as a hidden `<svg>` block
so that `<use href="#lucide-...">` fragment references resolve without an
extra HTTP request. The sprite is read from
`priv/static/images/iconex.svg` of the app configured as `:otp_app`, and
re-read whenever that file changes. Without `:otp_app`, or before the
compiler has written the file, every icon is embedded.

In `:external` delivery mode (the default) this emits nothing; the sprite is
served from `:sprite_url` and components reference it with a full path.

Place this at the very top of `<body>` in `root.html.heex`:

    <body>
      <%= Iconex.sprite_tag() %>
      ...
    </body>

---

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