Skip to content

Instantly share code, notes, and snippets.

@willvincent
Created May 1, 2025 10:28
Show Gist options
  • Save willvincent/2b6d3226a7cb97cbd8e53543ca5b9d74 to your computer and use it in GitHub Desktop.
Save willvincent/2b6d3226a7cb97cbd8e53543ca5b9d74 to your computer and use it in GitHub Desktop.
Import Phosphor Icons as flux icons
<?php
declare(strict_types=1);
namespace App\Console\Commands;
use Illuminate\Console\Command;
use Illuminate\Filesystem\Filesystem;
use function Laravel\Prompts\confirm;
use function Laravel\Prompts\error;
use function Laravel\Prompts\info;
use function Laravel\Prompts\intro;
use function Laravel\Prompts\note;
use function Laravel\Prompts\text;
class Phosphoric extends Command
{
protected $signature = 'phosphor:icon {icons?*} {--all : Import all available icons}';
protected $description = 'Import third-party icons from Phosphor.';
protected string $iconSourcePath = './phosphor-icons'; // local SVG path
public function handle(): void
{
if ($this->option('all')) {
$this->importAllIcons();
return;
}
$icons = $this->argument('icons');
if (count($icons)) {
foreach ($icons as $icon) {
$this->publishIcon($icon);
}
return;
}
intro('Import icons from Phosphor');
note('https://phosphoricons.com');
prompt:
$icon = text(
label: 'Enter icon name to import:',
required: 'An icon name is required.',
placeholder: 'e.g. airplane'
);
$this->publishIcon($icon);
if (confirm('Import another icon?')) {
goto prompt;
}
}
protected function importAllIcons(): void
{
$fillDir = base_path("{$this->iconSourcePath}/fill");
$regularDir = base_path("{$this->iconSourcePath}/regular");
$fillIcons = collect(scandir($fillDir))
->filter(fn($file) => str_ends_with($file, '.svg'))
->map(fn($file) => pathinfo($file, PATHINFO_FILENAME));
$regularIcons = collect(scandir($regularDir))
->filter(fn($file) => str_ends_with($file, '.svg'))
->map(fn($file) => pathinfo($file, PATHINFO_FILENAME));
$shared = $fillIcons->intersect($regularIcons)->sort()->values();
if ($shared->isEmpty()) {
error('No icons found in both fill/ and regular/');
return;
}
info("Importing " . $shared->count() . " icons...");
foreach ($shared as $icon) {
$this->publishIcon($icon);
}
info("All icons imported.");
}
protected function publishIcon(string $icon): void
{
$fillPath = base_path("{$this->iconSourcePath}/fill/{$icon}.svg");
$regularPath = base_path("{$this->iconSourcePath}/regular/{$icon}.svg");
if (!file_exists($fillPath) || !file_exists($regularPath)) {
error("Icon '{$icon}' not found in both styles.");
return;
}
$fillSvg = $this->prepareSvg(file_get_contents($fillPath));
$regularSvg = $this->prepareSvg(file_get_contents($regularPath));
$blade = $this->generateBlade($regularSvg, $fillSvg);
$target = resource_path("views/flux/icon/{$icon}.blade.php");
(new Filesystem)->ensureDirectoryExists(dirname($target));
file_put_contents($target, $blade);
info("Published icon: {$target}");
}
protected function prepareSvg(string $svg): string
{
preg_match('/<svg[^>]*viewBox="([^"]+)"[^>]*>/i', $svg, $viewBoxMatch);
$viewBox = $viewBoxMatch[1] ?? '0 0 256 256';
$bladeSvgOpen = <<<SVG
<svg {{ \$attributes->class(\$classes) }} xmlns="http://www.w3.org/2000/svg" viewBox="{$viewBox}" fill="currentColor" aria-hidden="true" data-slot="icon" data-flux-icon>
SVG;
return str($svg)
->replaceMatches('/<svg[^>]*>/i', $bladeSvgOpen)
->trim()
->toString();
}
protected function generateBlade(string $regularSvg, string $fillSvg): string
{
return <<<BLADE
{{-- Credit: Phosphor Icons (https://phosphoricons.com) --}}
@props([
'variant' => 'outline',
])
@php
\$classes = Flux::classes('shrink-0')
->add(match(\$variant) {
'outline' => '[:where(&)]:size-6',
'solid' => '[:where(&)]:size-6',
'mini' => '[:where(&)]:size-5',
'micro' => '[:where(&)]:size-4',
});
@endphp
<?php switch (\$variant): case ('outline'): ?>
{$regularSvg}
<?php break; ?>
<?php case ('solid'): ?>
{$fillSvg}
<?php break; ?>
<?php case ('mini'): ?>
{$fillSvg}
<?php break; ?>
<?php case ('micro'): ?>
{$fillSvg}
<?php break; ?>
<?php endswitch; ?>
BLADE;
}
}
@willvincent
Copy link
Author

Usage

  1. Download the icon set from Phosphor Icons.
  2. Extract the zip, and copy SVGs Flat to wherever you want to load them from in the script - its' set to pull from the root of your laravel install in a phosphor-icons directory. All you need from the SVGs Flat directory are the fill and regular subdirectories.

So you final form will look like this, somewhere on disk (update the script to point at it):

/path/to/phosphor-icons
     |
     +--- fill
     |
     +--- regular

Once there, and the artisan command pointed at the directory, and installed into app/Console/Commands/Phosphoric.php...

# Import one icon interactively
php artisan phosphor:icon

# Import specific icons
php artisan phosphor:icon airplane camera shield

# Import *every* icon that exists in both `fill/` and `regular/`
php artisan phosphor:icon --all

Usage with flux is then exactly the same as all the heroicons flux ships with:

<flux:icon.camera varient="outline" color="yellow" />

Screenshot 2025-05-01 at 5 35 48 AM

<flux:button icon="virus" varient="ghost" />

Screenshot 2025-05-01 at 5 36 59 AM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment