sage-svg icon indicating copy to clipboard operation
sage-svg copied to clipboard

Add optional parameter to prefix all IDs within SVG to avoid clashes

Open bekahmcdonald opened this issue 1 year ago • 2 comments

Purpose

Provide a way to prevent clashes between IDs used in SVGs.

The problem it solves

When an SVG is used more than once on a page, any IDs used within in the SVG will not be unique. This can cause problems when an SVG is relying on an ID (eg. for a clip-path or linear-gradient), as it can be referencing the wrong one. (Particularly a problem when you're animating SVGs!)

ID clashes often occur across multiple SVGs too, particularly when the IDs used are vague (eg. id="gradient1") or have been exported from another tool (eg. id="Layer1").

The solution

I've added another parameter to the @svg directive called $id_prefix. It allows the developer to provide a string that will be prefixed to all the IDs used within the SVG.

Usage and example

You may be using the same logo in the header and footer of the site. If the logo relies on an ID (eg. for a linear-gradient), there will be an ID clash. To get around this, you'd just pass different prefixes to the directive.

@svg('images.logo', 'w-32', ['aria-label' => 'Logo'], 'header-logo')

A detailed example

Without providing an ID prefix, my SVG markup looks like this. (This SVG was exported from Illustrator, and run manually through SVGOMG which simplified the ID to just a – optimised, but not super helpful...

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <defs>
    <linearGradient id="a" x1="0" x2="100" y1="50" y2="50" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#b6ff2a" />
      <stop offset=".995" stop-color="#0097ff" />
    </linearGradient>
  </defs>
  <path fill="url(#a)"
    d="M50 0C22.386 0 0 22.386 0 50s22.386 50 50 50 50-22.386 50-50S77.614 0 50 0Zm0 92.148C26.722 92.148 7.852 73.278 7.852 50S26.722 7.852 50 7.852 92.148 26.722 92.148 50 73.278 92.148 50 92.148Z"/>
</svg>
@svg('images.logo', 'w-32', ['aria-label' => 'Logo'], 'header-logo')

Passing an $id_prefix parameter to the @svg directive turns my SVG's unhelpful ID of a into the more specific header-logo-a, both where it's declared on the linearGradient in defs and where it's referenced as the fill on the path.

<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100">
  <defs>
    <linearGradient id="header-logo-a" x1="0" x2="100" y1="50" y2="50" gradientUnits="userSpaceOnUse">
      <stop offset="0" stop-color="#b6ff2a" />
      <stop offset=".995" stop-color="#0097ff" />
    </linearGradient>
  </defs>
  <path fill="url(#header-logo-a)"
    d="M50 0C22.386 0 0 22.386 0 50s22.386 50 50 50 50-22.386 50-50S77.614 0 50 0Zm0 92.148C26.722 92.148 7.852 73.278 7.852 50S26.722 7.852 50 7.852 92.148 26.722 92.148 50 73.278 92.148 50 92.148Z"/>
</svg>

tldr;

With the addition of an $id_prefix parameter to the @svg directive, the developer is back in control of how unique an SVG's IDs are, even if that SVG is stored in the CMS.

bekahmcdonald avatar Jan 04 '24 19:01 bekahmcdonald

This sounds good!

I changed your implementation slightly to instead be passed as an options array like ['idPrefix' => 'example-'] that way if there is more functionality that gets added down the road, the function doesn't start getting overloaded or introduce breaking changes.

Let me know if that's ok and that everything is still working and I can get this merged.

Thanks!

Log1x avatar Jan 04 '24 19:01 Log1x

I realised I committed without the vital part of the preg_replace replacement pattern... I've added it back in! It's all working now.

The options array makes sense, thanks for changing that. This is such a helpful package for me, and it's nice to be able to contribute!

bekahmcdonald avatar Jan 05 '24 10:01 bekahmcdonald