# Adding material symbols to a Phoenix project

# Using Material Symbols with Phoenix and Tailwind CSS

[Material Symbols](https://fonts.google.com/icons) is the variable font version of Google’s Material Icons. Unlike traditional icon fonts, variable fonts allow a single file to contain multiple stylistic variations—significantly reducing the number of assets you need to manage.

Material Symbols replaces the now-deprecated Material Icons fonts and is actively maintained with regular updates.

## Why Use Material Symbols in Phoenix?

By default, new Phoenix projects include a set of built-in components, including an `icon/1` component that renders [Heroicons](https://heroicons.com). While Heroicons are elegant and often sufficient, Material Symbols offer a far larger collection. They're widely adopted by UI designers, which means many mockups and design systems use them by default.

To preserve visual fidelity with the design, substituting icons between sets (like Heroicons and Material Symbols) is not ideal. Having Material Symbols available directly in your Phoenix app makes it easier to match designs precisely.

## Material Symbols Overview

Material Symbols are based on 24px icons and come in three styles:

- **Outlined**
- **Rounded**
- **Sharp**

These are variable fonts with four axes of customization:

- **Optical Size**: Default is `24`
- **Weight**: Default is `400` (Regular)
- **Grade**: Default is `0`
- **Fill**: Default is `0`

For simplicity, we'll use the default values and only include the `400` weight icons.

## Installing Material Symbols via Dependency

To avoid manually downloading and maintaining icon files, we can pull them directly from GitHub using a sparse checkout in `mix.exs`. This setup fetches only the necessary `svg/400` folder, avoids compiling the dependency, and reduces disk usage by limiting Git history.

Add the following to your `mix.exs` dependencies:

```elixir
{:material_icons,
  github: "marella/material-symbols",
  sparse: "svg/400",
  app: false,
  compile: false,
  depth: 1
}
```

Then run:

```bash
mix deps.get
```

## Extending the icon/1 Component

Phoenix comes with a built-in `icon/1` component for Heroicons. We’ll add support for Material Symbols by introducing a new clause that matches icon names with a `material-` prefix.

In core_components.ex, add this function:

```elixir
def icon(%{name: "material-" <> _} = assigns) do
  ~H"""
  <span class={[@name, @class]} aria-hidden="true" {@rest} />
  """
end
```

This allows you to use Material Symbols like so:

```elixir
<.icon name="material-home" />
```

## Configuring Tailwind to Recognize Material Symbols

Next, we need Tailwind to generate CSS classes for the Material Symbols. We’ll scan the SVG files in the deps/material_icons directory and create utility classes based on their filenames.

Update your tailwind.config.js with the following plugin:

```js
plugin(function({ matchComponents, theme }) {
  const path = require("path");
  const fs = require("fs");

  const iconsDir = path.join(__dirname, "../deps/material_icons/svg/400");
  const values = {};

  const styles = [
    ["", "outlined"],
    ["_rounded", "rounded"],
    ["_sharp", "sharp"],
  ];

  styles.forEach(([suffix, dir]) => {
    fs.readdirSync(path.join(iconsDir, dir)).forEach(file => {
      let name = path.basename(file, ".svg") + suffix;
      name = name.replaceAll("_", "-");

      values[name] = {
        name,
        fullPath: path.join(iconsDir, dir, file),
      };
    });
  });

  matchComponents({
    "material": ({ name, fullPath }) => {
      let content = fs.readFileSync(fullPath, "utf8").replace(/\r?\n|\r/g, "");
      content = content.replace(' width="48" height="48"', "");

      const size = theme("spacing.6");

      return {
        [`--material-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
        "-webkit-mask": `var(--material-${name})`,
        "mask": `var(--material-${name})`,
        "mask-repeat": "no-repeat",
        "background-color": "currentColor",
        "vertical-align": "middle",
        "display": "inline-block",
        "width": size,
        "height": size,
      };
    }
  }, { values });
})
```

With this setup, Tailwind will generate classes like material-home based on your icons. You can then use them as utility classes in your components.

## Keeping Icons Up to Date

Since the Material Symbols repository is frequently updated via GitHub Actions, your app can stay current with new icons by simply running:

```bash
mix deps.update material_icons
```

## Conclusion

Integrating Material Symbols with Phoenix and Tailwind gives you access to a vast and regularly updated icon set that aligns with modern UI design standards. By automating the dependency, icon rendering, and Tailwind class generation, you streamline both development and design fidelity—ensuring your front end looks just like the mockups.
