Skip to content

Instantly share code, notes, and snippets.

@jaeyson
Forked from greven/core_components.ex
Created April 12, 2025 11:07
Show Gist options
  • Save jaeyson/32afc5bae20b9666c5f9fa0a9bb6e7f1 to your computer and use it in GitHub Desktop.
Save jaeyson/32afc5bae20b9666c5f9fa0a9bb6e7f1 to your computer and use it in GitHub Desktop.
Phoenix CSS Icons
@doc """
Renders an icon.
Supports three icon libraries:
- [Heroicons](https://heroicons.com) - prefixed with "hero-"
- [Lucide](https://lucide.dev) - prefixed with "lucide-"
- [Simple Icons](https://simpleicons.org) - prefixed with "si-"
You can customize the size and colors of the icons by setting
width, height, and background color classes.
Icons are extracted from their respective directories and bundled within
your compiled app.css by the plugins in `assets/vendor/`.
## Examples
<.icon name="hero-x-mark-solid" />
<.icon name="hero-arrow-path" class="ml-1 size-3 motion-safe:animate-spin" />
<.icon name="lucide-github" />
<.icon name="si-github" class="size-6" />
<.icon name="si-elixir" class="size-5 text-purple-600" />
"""
attr :name, :string, required: true
attr :class, :string, default: "size-4"
def icon(%{name: "hero-" <> _} = assigns) do
~H"""
<span class={[@name, @class]} />
"""
end
def icon(%{name: "lucide-" <> _} = assigns) do
~H"""
<span class={[@name, @class]} />
"""
end
def icon(%{name: "si-" <> _} = assigns) do
~H"""
<span class={[@name, @class]} />
"""
end
// Add lucide.js to assets/vendor
const plugin = require('tailwindcss/plugin');
const fs = require('fs');
const path = require('path');
module.exports = plugin(function ({ matchComponents, theme }) {
let iconsDir = path.join(__dirname, '../../deps/lucide_icons/icons');
let values = {};
fs.readdirSync(iconsDir).forEach((file) => {
if (file.endsWith('.svg')) {
let name = path.basename(file, '.svg');
values[name] = { name, fullPath: path.join(iconsDir, file) };
}
});
matchComponents(
{
lucide: ({ name, fullPath }) => {
let content = fs
.readFileSync(fullPath)
.toString()
.replace(/\r?\n|\r/g, '');
// Clean up SVG
content = content
.replace(/width="24"/g, '')
.replace(/height="24"/g, '')
.replace(/stroke-width="2"/g, 'stroke-width="1.5"')
.replace(/stroke="[^"]+"/g, 'stroke="currentColor"');
console.log(content);
content = encodeURIComponent(content);
let size = theme('spacing.6'); // 1.5rem / 24px by default
return {
[`--lucide-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
'-webkit-mask': `var(--lucide-${name})`,
mask: `var(--lucide-${name})`,
'mask-repeat': 'no-repeat',
'background-color': 'currentColor',
'vertical-align': 'middle',
display: 'inline-block',
width: size,
height: size,
};
},
},
{ values }
);
});
# ...
{:heroicons,
github: "tailwindlabs/heroicons",
tag: "v2.1.5",
sparse: "optimized",
app: false,
compile: false,
depth: 1},
{
:lucide_icons,
github: "lucide-icons/lucide",
tag: "0.487.0",
sparse: "icons",
app: false,
compile: false,
depth: 1
},
{:simple_icons,
github: "simple-icons/simple-icons",
tag: "14.12.1",
sparse: "icons",
app: false,
compile: false,
depth: 1
},
# ...
// Add simple-icons.js to assets/vendor
const plugin = require('tailwindcss/plugin');
const fs = require('fs');
const path = require('path');
module.exports = plugin(function ({ matchComponents, theme }) {
let iconsDir = path.join(__dirname, '../../deps/simple_icons/icons');
let values = {};
fs.readdirSync(iconsDir).forEach((file) => {
if (file.endsWith('.svg')) {
let name = path.basename(file, '.svg');
values[name] = { name, fullPath: path.join(iconsDir, file) };
}
});
matchComponents(
{
si: ({ name, fullPath }) => {
let content = fs
.readFileSync(fullPath)
.toString()
.replace(/\r?\n|\r/g, '');
content = content.replace(/fill="[^"]+"/g, 'fill="currentColor"');
content = encodeURIComponent(content);
let size = theme('spacing.5');
return {
[`--si-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
'-webkit-mask': `var(--si-${name})`,
mask: `var(--si-${name})`,
'mask-repeat': 'no-repeat',
'background-color': 'currentColor',
'vertical-align': 'middle',
'mask-size': 'contain',
'mask-position': 'center',
display: 'inline-block',
width: size,
height: size,
};
},
},
{ values }
);
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment