Allow inline SVG instead of <use> tag

Created on 17 January 2022, almost 3 years ago
Updated 10 May 2024, 7 months ago

Problem/Motivation

The use of externally-sourced SVG using <use> tags is perfectly fine for most scenarios. However, it has a few shortcomings, including the following:

- The inability to target specific SVG child elements (like paths) using classes or IDs using CSS
- Handling color can be troublesome. You can have your SVG inherit the currentColor, but ¿what happens with icons with more than two colors? (i.e a SVG icon that on :hover contrasts its colors and needs to handle a foregroudn and background colors)

This is why I think a great addition to this module could be the ability to decide wether the SVG needs to be externally sourced or inlined.

Proposed resolution

I have developed a working solution, it is a quick workaround and might not be the "best" way to handle this, but it might serve as a starting point or proof-of-concept.

I'm just loading the markup on each Icon plugin definition and allowing the user to pass a '#use_markup' flag to the template. This way, you can pass a fourth parameter to the Twig function :

    {{ ex_icon(link.class, {width: 30, height: 30}, link.text, true) }}

...or to a render array:

    return [
      '#theme' => 'ex_icon',
      '#id' => $icon,
      '#title' => $title,
      '#attributes' => $attributes instanceof Attribute ? $attributes->toArray() : $attributes,
      '#use_markup' => $use_markup
    ];

The markup is loaded on SvgSpriteSheet:decode(), on a new getRawSvg() function

      $data['icons'][$id] = [
        'width'  => (double) $viewbox[2] - (double) $viewbox[0],
        'height' => (double) $viewbox[3] - (double) $viewbox[1],
        'label'  => $title ? $title->nodeValue : '',
        'markup' => self::getRawSvg($symbol),
        'viewbox' => $symbol->getAttribute('viewBox'),
      ];

There are several areas of concern, the first one being that this might pose a security risk if anybody can access and alter the sprite SVG
sheet. Drupal's sanitization filters strip SVG, so I have used an external dependency (enshrined/sanitizer) to perform a sanitization prior to output.

Also, due to Drupal's shortcomings on SVG rendering, the markup has to be rendered using 'inline_template'' in order to avoid the SVG strippiing.

As I said before, this is a quick workaround and there is clearly a lot of area for improvements. The code can be abstracted, there are several ugly string concatenations that might be improved, in general the SVG markup generation could be much better. Nevertheless, I think it is an interesting feature to support.

✨ Feature request
Status

Active

Version

1.0

Component

Code

Created by

🇪🇸Spain idiaz.roncero Madrid

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

Not all content is available!

It's likely this issue predates Contrib.social: some issue and comment data are missing.

  • First commit to issue fork.
  • 🇪🇸Spain frouco

    Here is patch to make this proof of concept applicable to >1.5.0 versions

  • 🇪🇸Spain frouco

    This solution is not really necessary.

    The way to apply colours to elements inside the SVG with use is with CSS custom properties (variables).

    .svg example

    <svg>
      <defs>
        <symbol id="icon-id" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
          <circle style="fill: var(--icon-bg, none);" fill="none" cx="15" cy="15" r="15"/>
          <path style="fill: var(--icon-ring, currentColor);" fill="currentColor" d="M15 2.5a12.5 12.5 0 1 1 0 25 12.5 12.5 0 0 1 0-25ZM15 0a15 15 0 1 0 0 30 15 15 0 0 0 0-30Z"/>
          <path style="fill: var(--icon-figure, currentColor);" fill="currentColor" fill-rule="evenodd" clip-rule="evenodd" d="M20.29 8.88c-2.5-.17-8.08-.17-10.58 0-2.7.18-3.02 1.82-3.04 6.12.02 4.3.33 5.94 3.04 6.12 2.5.17 8.08.17 10.58 0 2.7-.18 3.02-1.82 3.04-6.12-.02-4.3-.33-5.94-3.04-6.12Zm-1.82 6.11-5.97-2.77v5.56l5.97-2.79Z" />
        </symbol>
      </defs>
    </svg>
    

    CSS code example

    svg {
      --icon-bg: currentcolor;
      --icon-ring: red;
      --icon-figure: #fff;
    }
    
  • 🇪🇸Spain idiaz.roncero Madrid

    That's a really clever solution. I guess browser support is the same as CSS custom properties?

Production build 0.71.5 2024