How to add inject CSS class in inline SVG

Hello.

I’m using inline SVGs most on my current project and most of the time I have to add a CSS class on the SVG tag because I’m using Tailwindcss to style the app. This works for me but obviously, my code is now full of gibberish SVG codes.
I’ve watched a video where the coder is using Laravel + Blade and he could easily include his SVGs like this

@include('logo.svg', ['class' => 'someclasshere'])

Is this a Blade feature? Can we do this with plain PHP or with some Kirby helpers?

Blade is the template language for Laraval. You can use it with Kirby but for that you need a plugin, and to re-write all the temples and snippets.

You could put svg helper in a snippet with a DIV round it and the class attribute on it, and pass the class through as a parameter, as well as the URL.

1 Like

You can achieve the same as the blade helper with string replacement like this:

<?= str_replace(
    '<svg',
    sprintf('<svg %s', 'class="someclass"'),
    svg($page->file('logo.svg')->root()));
?>

(They do the same)

1 Like

This would be the right solution for my needs. And I think it’s still possible to make it cleaner by making it as a snippet or something better on which I will just have to pass the filename, right?

As always, the fastest and most helpful person, thank you @texnixe

@sicnarf Yes, sure, you could hide it away in a function or snippet. You should check for a valid image, anyway, before calling root()

Depends where you want to store the SVG files as well, since the example above expects them to belong to the current page. You might need to make two snippets, one for pulling from the assets directory (useful for things like site footers & headers), and one specific page files.

@jimbobrjames That is not necessary if you pass a path to the snippet or function, then it doesn’t matter where the file comes from.

You can pass a path to another page with that?? Oh boy! I didnt know :slight_smile:

You beat me with this. I was about to ask as well coz’ I saw it was using the $page->

That’s not what I said, $page->file('somefile')->root() is a path to a file that you can pass on just the same line assets/images/logo.svg.

function renderSvgWithClass($filepath, $class) {
  return str_replace(
    '<svg',
    sprintf('<svg %s', 'class="' . $class . '"'),
    svg($filepath));
}

Then in template:

if ($file = $page->file('some.svg')) {
  echo renderSvgWithClass($file->root(), 'someclass');
}

Or with asset:

echo renderSvgWithClass('assets/images/some.svg', 'someclass');
1 Like

Did you mean like this?

<?= str_replace(
    '<svg',
    sprintf('<svg %s', 'class="someclass"'),
    svg('./assets/images/svg/file.svg'));
?>

Ah… now i see what you mean.

Can I do this? I’m pretty sure all my SVG will be on the same folder. I think I can save finger strains by typing less :grinning:

svg('assets/images/svg/' . $filename));

Template

echo renderSvgWithClass('filename.svg', 'someclass');

Sorry I’m not able to test all of this now so I just keep asking.

Yes, you can do that, makes it less versatile, though.

1 Like

Appreciate it.

Noob question: I will put the function inside the site/controllers, is it?

I’d put it in a plugin, you can put it into a controller, yes.

e.g. /site/plugins/helpers/index.php

Better to put it in a plugin, since controllers belong to a named template, so you can only get at it from pages using that template.

Alright, I’ll put it in a plugin. Thank you guys!

EDIT:
@texnixe @jimbobrjames

Sorry to reopen the thread. I did try the solution and it works great. One issue I have though is I cannot add multiple CSS classes. It only accepts a single $class value.

So doing this doesn’t work.

<?= renderSvgWithClass('assets/images/logo.svg', 'class-1 class-2'); ?>

Output

<svg class="class-1" class-2 xmlns... />

Btw, I put the function in my index.php as suggested in the Docs. I do think this is a simple plugin and thus belongs there.

There were the closing quotes missing above, but you can extend the above and pass an array of classes like this instead.


    function renderSvgWithClass(string $filepath, array $classes = []) {
      return str_replace(
        '<svg',
        sprintf('<svg %s', 'class="' . implode(' ', $classes) . '"'),
        svg($filepath));
    }

    if ($file = $page->file('logo.svg')) {
     echo renderSvgWithClass($file->root(), ['someclass', 'extraclass']);
}

Passing a string with multiple classes should have worked as well, though, at least with the modified solution above.

1 Like

And to finish this off, here is a more solid, Kirby-style version of the function:

function renderSvgWithClass(string $filepath, string $class) {
  if ($svg = svg($filepath)) {
    return Str::replace(
      $svg,
      '<svg',
      Str::template('<svg {{ class }}', ['class' => Html::attr('class', $class)])
    );
  }
  return false;
}
2 Likes