How to add inject CSS class in inline SVG

Just stubled over this old thread and want to share my approach.

As I’m using Alpine a lot, I need to be able to inject other attributes like @click or :class.

(In addition I replace stroke and fill color values to currentColor – but that’s quite experimental. Just using it for now and see how it works for me. Makes the SVG sync to text-color without further configuration)

This is how it’s used in a template / snippet (semi-nonsense example):

<a class="text-blue-200 hover:text-red-400" x-data="{count: 0}" @click="count++">
  Link
  <?= svge('assets/arrow.svg', ['class' => 'w-20 duration-150', ':class' => "{'rotate-90': count > 3}"]) ?>
</a>
// e.g. in plugins/my-plugin/index.php
function svge(string $filepath, array $attrs = [])
{
	// get svg content
	if (!$content = svg($filepath)) {
		throw new Exception("SVG filepath not found: $filepath");
	}

	// replace fill / stroke
	$content = preg_replace_callback(
		'/(fill|stroke)="([^"]*)"/i',
		function ($matches) {
			$attribute = $matches[1];
			$value = strtolower($matches[2]);

			if ($value !== 'none' && $value !== 'transparent') {
				return "$attribute=\"currentColor\"";
			}

			return $matches[0];
		},
		$content
	);

	// extand <svg> tag
	// TODO: handle extending existing attributes 
	foreach ($attrs as $attr => $value) {
		$content = Str::replace(
			$content,
			'<svg',
			Str::template('<svg {{ attr }}', ['attr' => Html::attr($attr, $value)])
		);
	}

	return $content;
}
1 Like

Can definitely recommend using icomoon.io to bundle your SVGs into one symbol definition file, that you inline on the page.

Then you can just reference the SVG where you need it, e.g.
<svg class="icon"><use xlink:href="#icon-name-here"></use></svg>

Results in a nicer loading, as there’s no individual HTTP requests per SVG and you can just apply your CSS class per symbol placement without affecting the base symbol.