Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Vue stripping out styles from inline SVG #11697

Closed
francois-pasquier opened this issue Oct 1, 2020 · 14 comments
Closed

Vue stripping out styles from inline SVG #11697

francois-pasquier opened this issue Oct 1, 2020 · 14 comments

Comments

@francois-pasquier
Copy link

francois-pasquier commented Oct 1, 2020

Version

2.6.11

Reproduction link

https://jsbin.com/lurayuxofo/edit?html,js,console,output

Steps to reproduce

  • Include inline SVG with some styles within a Vue instance
  • Init vue instance

What is expected?

SVG to be styled according to the style element

What is actually happening?

Vue is giving a warning "[Vue warn]: Templates should only be responsible for mapping the state to the UI. Avoid placing tags with side-effects in your templates, such as <style>. " and also stripping the style element so the SVG is appearing with no style.


I am opening the exact same issue as this one because it's been ignored and this should be fixed by now.

It's been plaguing vue since 2016.

The fix so far:

In the meantime, this can be fixed by adding this attribute xmlns:svg="http://www.w3.org/2000/svg" to your <svg> tag and writing your style as this : <svg:style type="text/css">

@posva
Copy link
Member

posva commented Oct 1, 2020

While putting a style tag inside of an svg element is valid HTML, it still has the same effect as putting it outside of it: it leaks its CSS definitions into the global scope, potentially affecting every other element in the application. This is notably the case with generated classes inside .svg files: copying the svg content into a component and leaving the style tag will override existing classes and change existing svgs because of generated classes for different SVG sharing the same name.
When using an img to display an svg, this is not a problem, because the style definitions do not leak globally.

The warning was introduced because it was common for users to fall for this. It's still possible for users to ignore the warning and explicitly inject style tags by using a duplicate namespace or by using v-html="<style>...</style>".

@posva posva closed this as completed Oct 1, 2020
@francois-pasquier
Copy link
Author

It's not possible when using svg files directly.

@Justineo
Copy link
Member

Justineo commented Oct 2, 2020

How are you using SVG files directly as inline SVG?

@francois-pasquier
Copy link
Author

How are you using SVG files directly as inline SVG?

Using this config:

// vue.config.js
module.exports = {
  devServer: {
    overlay: false
  },
  chainWebpack: (config) => {
    const svgRule = config.module.rule('svg')

    svgRule.uses.clear()

    svgRule
      .use('vue-loader')
      .loader('vue-loader-v16')
      .end()
      .use('vue-svg-loader')
      .loader('vue-svg-loader')
  }
}

the lib

@Justineo
Copy link
Member

Justineo commented Oct 2, 2020

You can use raw-loader to import the SVG file as a string and then use it in v-html. This would introduce an extra wrapper element though.

@francois-pasquier
Copy link
Author

You can use raw-loader to import the SVG file as a string and then use it in v-html. This would introduce an extra wrapper element though.

Sure, that would be a solution 👍

How about adding an optional rule to vue-loader regarding style preservation from svg files?
(is it even the culprit regarding style stripping ?)

Would the team be firmly against it?

I could make a PR.

Coming from a react and vue background I tend to use as much svg files as I can as they are lighter than pngs.

@elevatebart
Copy link
Contributor

@MeisterTea I agree 100% it would be awesome to not have to think when we use SVGs in our apps.

Using styles tags in SVG represents a significant risk of conflict.
If you are using Illustrator to export your SVG assets, the classes that AI creates are almost always repeated (.st1, filter_2). If two SVGs are displayed on the same page, they might fight each others styles resulting in non-predictable results.

Depending on where your SVG files come from, you might have the option of exporting them with attributes instead of <style>.
Illustrator does it well, sketch does not, Boxy Svg is awesome at it.
With sketch, you can also use the SVGO plugin. The inlineStyles option will move all the content of classes into the object they represent.

@JessicaSachs
Copy link

JessicaSachs commented Oct 3, 2020

I would really like to see better support for SVGs in the Vue ecosystem. I struggled with issues (like style tags getting stripped) when implementing our icon system at work. I implemented svgo-loader + vue-svg-loader to generate uniquely prefixed selectors for the style tags. Here's the prefixIds PR for SVGO which adds that functionality.

@posva The main issue with this solution is that not all styles can be inlined. You can't do anything with pseudo selectors or at-rules (media queries, feature detection). This means that implementing an icon system with interactive svgs becomes very difficult. You can pull out the svg into a v-html but that kind of defeats the purpose of componetizing the SVG in the first place.

SVGO is also available as a webpack loader so I think implementing this inside of vue-cli could be doable and alleviate the namespacing concerns

@posva
Copy link
Member

posva commented Oct 3, 2020

@posva The main issue with this solution is that not all styles can be inlined. You can't do anything with pseudo selectors or at-rules (media queries, feature detection). This means that implementing an icon system with interactive svgs becomes very difficult. You can pull out the svg into a v-html but that kind of defeats the purpose of componetizing the SVG in the first place.

My comment is only accounting for what was said initially: allowing style tags inside svg.

If there is a webpack loader involved, it will be the loader's job to automate any process to avoid inlining style. It could be extracting the styles to a separate CSS file, inlining styles, wrapping the svg with props to change styles. All of this is way beyond Vue's scope and should live in the loader itself.

I think with the upcoming css vars mode in SFC, there is a lot of room for improvement for loaders to simplify working with svgs and Vue

@Justineo
Copy link
Member

Justineo commented Oct 3, 2020

Inlining styles are not always possible. Eg. things like @keyframes cannot be inlined. As @elevatebart pointed out including all these <style>s from SVG files introduces high risks of naming conflicts. Is there any chance that we preprocess SVGs in vue-svg-loader to rewrite identifiers (ids, classes, animation names, …) into unique ones to eliminate naming conflicts?

@JessicaSachs
Copy link

JessicaSachs commented Oct 3, 2020

Thanks for the response.

I only care about this issue being acknowledged as a real one and re-opened -- not to imply any scheduling or priority.

My comment is only accounting for what was said initially: allowing style tags inside svg.

This is notably the case with generated classes inside .svg files: copying the svg content into a component and leaving the style tag will override existing classes and change existing svgs because of generated classes for different SVG sharing the same name.

@posva I was trying to address your concern RE: namespace risk, so I brought up SVGO + loaders.

If there is a webpack loader involved, it will be the loader's job to automate any process to avoid inlining style.

You mentioned that the loader can handle this. I agree, this is a good stop-gap. However, if loaders are the final solution, we will diverge from the SVG spec, but only in SFC-compiled components... not template-compiled ones. Keeping template-compiled markup equivalent to Vue SFCs seems like it would be a requirement.

@Justineo I think a webpack-loader approach is possible and an OK near-term solution for many users. Using the prefixIds plugin for SVGO would work to generate unique ids.

Again, I would love this feature to be in the core Vue compiler due to the template-compilation issue outlined above. We seem to be interested in diversifying our build stack from Webpack so if we choose a loader-based solution, this issue may become more apparent as time goes on.

Vue just shouldn't strip style tags in SVG. It's against spec, we should figure out a solution

@coolsoftwaretyler
Copy link

Joining in here because I also want to handle this a little better. I'm using Mermaid, which generates these style tags for me, and in every other tool I use it with, no issues. But if I generate SVG with Mermaid and use the output in Vue, I get the side-effect warnings. Would love some kind of escape hatch or handling for this that doesn't introduce another build step in the chain.

@zumm
Copy link

zumm commented Apr 25, 2022

It would be very nice to have options. Stripping styles or preserving them. Also vue could try to make styles scoped if user wants.

@DrJume
Copy link

DrJume commented Jun 23, 2022

https://github.com/jpkleemans/vite-svg-loader

It also uses SVGO, which extracts the classes in the <style> tag into inline styles.

By default it did not strip classes which are used more than once, as it increases output size. But it's needed to be able to apply the styles, because Vue strips the style tag away. Thats why I needed to set the onlyMatchedOnce option to false.

svgLoader({
    svgo: true,
    svgoConfig: {
        plugins: [
        {
            name: 'preset-default',
            params: {
                overrides: {
                    inlineStyles: {
                        onlyMatchedOnce: false,
                    },
                },
            },
        },
        ],
    },
})

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

8 participants