# Icons

PUI separates icon intent from icon rendering. PUI-owned affordances use
semantic tokens through `PUI.Icon`; application-owned icons are rendered by the
host application and passed through component slots.

## Default provider

PUI ships a dependency-free Heroicons provider that renders the CSS classes
generated by Phoenix's default Heroicons integration:

```heex
<PUI.Icon.icon name={:calendar} />
<PUI.Icon.icon name={:close} class="size-5 text-muted-foreground" />
```

No configuration is required when the host application already includes the
Phoenix Heroicons Tailwind plugin.

The semantic tokens currently owned by PUI are:

| Token | Use |
|---|---|
| `:close` | Close and dismiss controls |
| `:chevron_down` | Disclosure and downward navigation |
| `:chevron_left` | Previous navigation |
| `:chevron_right` | Next navigation |
| `:chevron_up_down` | Select trigger indicator |
| `:search` | Search controls |
| `:calendar` | Date picker trigger |
| `:menu` | Layout/sidebar controls |
| `:success`, `:error`, `:warning`, `:info` | Feedback status icons |

<AppWeb.DocsDemo.icons_demo />

## Configure a provider

Select one provider for all PUI internals in the host application's config:

```elixir
config :pui, :icon_provider, MyApp.PUIIcons
```

Providers implement `PUI.IconProvider`:

```elixir
defmodule MyApp.PUIIcons do
  use Phoenix.Component

  @behaviour PUI.IconProvider

  @mapping %{
    close: "provider-close",
    calendar: "provider-calendar",
    chevron_down: "provider-chevron-down",
    chevron_left: "provider-chevron-left",
    chevron_right: "provider-chevron-right",
    chevron_up_down: "provider-chevrons-up-down",
    search: "provider-search",
    menu: "provider-menu",
    success: "provider-success",
    error: "provider-error",
    warning: "provider-warning",
    info: "provider-info"
  }

  @impl true
  def render(token, assigns) do
    assigns = assign(assigns, :provider_name, Map.fetch!(@mapping, token))

    ~H"""
    <MyAppWeb.CoreComponents.icon
      name={@provider_name}
      class={@class}
      {@rest}
    />
    """
  end
end
```

The adapter may call an existing host icon component, render an inline SVG, or
emit provider-specific classes. Unsupported semantic tokens should fail
clearly rather than render an invisible control.

## Lucide and Tabler

The provider module is intentionally independent of any icon package. A
Lucide adapter maps the PUI tokens to the Lucide names used by the host:

```elixir
@mapping %{
  close: "lucide-x",
  calendar: "lucide-calendar",
  chevron_down: "lucide-chevron-down",
  chevron_left: "lucide-chevron-left",
  chevron_right: "lucide-chevron-right",
  chevron_up_down: "lucide-chevrons-up-down",
  search: "lucide-search",
  menu: "lucide-menu",
  success: "lucide-circle-check",
  error: "lucide-circle-x",
  warning: "lucide-triangle-alert",
  info: "lucide-circle-help"
}
```

For Tabler, keep the same behaviour and change only the mapping and the host
renderer:

```elixir
@mapping %{
  close: "tabler-x",
  calendar: "tabler-calendar",
  chevron_down: "tabler-chevron-down",
  chevron_left: "tabler-chevron-left",
  chevron_right: "tabler-chevron-right",
  chevron_up_down: "tabler-selector",
  search: "tabler-search",
  menu: "tabler-menu-2",
  success: "tabler-circle-check",
  error: "tabler-circle-x",
  warning: "tabler-alert-triangle",
  info: "tabler-info-circle"
}
```

The exact provider names and Tailwind/SVG setup belong to the host application;
PUI only relies on the semantic token contract.

## Application-owned icons

Use slots when the icon represents application navigation, business meaning, or
an icon outside PUI's small semantic vocabulary:

```heex
<.sidebar_menu_item title="Dashboard" href="/dashboard">
  <:icon>
    <.icon name="hero-home" class="size-4" />
  </:icon>
</.sidebar_menu_item>
```

The slot can contain a Phoenix Heroicon, a Lucide component, a Tabler component,
or custom SVG markup. PUI owns the surrounding layout and accessibility
relationship, while the host owns the icon library.
