I need to create a number of SVG files and would love to keep a set of common symbols in one file and import them into the others.
I managed to do this using a <use> element:
<use href="common.svg#symbol1" />
The problem is that if common.svg has a CSS style that affects an element, the style has no effect in the file where the element is imported.
I uploaded two SVGs on svgur.com to show this:
https://svgur.com/i/bYv.svg
...defines a circle with id ball affected by a style that sets a red border around it.
<svg width="100" height="100" version="1.1" xmlns="http://www.w3.org/2000/svg">
<style>#ball { stroke:#ff0000; stroke-width:10; }</style>
<circle id="ball" cx="50" cy="50" r="45" />
</svg>
https://svgur.com/i/bXA.svg
...uses the circle. The circle is visible but the border is not.
<svg width="100" height="100" version="1.1" xmlns="http://www.w3.org/2000/svg">
<use href="bYv.svg#ball" />
</svg>
Questions:
Is this a bug of the SVG renderer I'm using or is it how it's supposed to behave?
Every renderer I tried (chrome, firefox, inkscape) shows the same result so I suspect this might be the intended behavior.
Is there any way to import an element from an external SVG file and the CSS styles that affect it too, so that it appears exactly like it does in its original document?
Is this a bug of the SVG renderer I'm using or is it how it's supposed to behave?
It is the intended behaviour. CSS rules do not apply across document boundaries. You are importing the SVG into your main document, but your CSS is in another document.
Is there any way to import an element from an external SVG file and the CSS styles that affect it too...
No.
Well I suppose you could technically write some Javascript to load the other file and extract the CSS rules. But I strongly suspect you don't want to do that.
You SVG "sprite document" should not have CSS rules in a <style> tag.
The best approach is to pre-prepare you SVGs to be used as sprites.
What I would do is import your common.svg into a vector editor and convert all your CSS attributes into presentation attributes. For example Illustrator lets you choose the method of styling when you export an SVG.
What you want is for something like this:
<svg>
<style>
.st0 {
fill: red;
}
</style>
<symbol id="whatever">
<path d="..." class="st0"/>
</symbol>
</svg>
to be converted to:
<svg>
<symbol id="whatever">
<path d="..." fill="red"/>
</symbol>
</svg>
According to the Scalable Vector Graphics (SVG) 2
specification (W3C Editor’s Draft, 08 June 2021) it seems that the style of the original document should be applied where an element is imported.
Section 5.5. The ‘use’ element:
The cloned content inherits styles from the ‘use’ element and can be the target of user events. However, these cloned element instances remain linked to the referenced source and reflect DOM mutations in the original. In addition, all style rules that apply in the scope of the referenced element also apply in the scope of the cloned shadow tree.
And 5.5.3. Style Scoping and Inheritance:
The use-element shadow tree, like other shadow trees, exhibits style encapsulation, as defined in the CSS Scoping module [css-scoping-1]. This means that elements in the shadow tree inherit styles from its host ‘use’ element, but that style rules defined in the outer document do not match the elements in the shadow tree. Instead, the shadow tree maintains its own list of stylesheets, whose CSS rules are matched against elements in the shadow tree.
When the referenced element is from an external document, the stylesheet objects generated when processing that document apply to the shadow tree, and are read-only. All URL references in the stylesheet, including fragment-only references, must be made absolute, relative to the URL of the document that contains the referenced element. User agents may re-use the same stylesheet objects for any shadow trees that reference that same external document.
So it's the browsers and SVG renderers I tried that are not conforming to the standard.
I'm still looking for a way to emulate this behavior on existing SVG user agents.
Your styles will be applied, provided your svg asset code is inlined
<svg style="display:none" class="svg-asset" width="100" height="100" version="1.1" xmlns="http://www.w3.org/2000/svg">
<style>
#inline-ball { stroke:#ff0000; stroke-width:10; }
</style>
<circle id="inline-ball" cx="50" cy="50" r="45" />
</svg>
<svg width="100" height="100" version="1.1" xmlns="http://www.w3.org/2000/svg">
<use xlink:href="#inline-ball" />
</svg>
If you prefer not to inline svg code then "fragment identifiers" might be the better embedding approach.
It's basically a sprite concept:
So your image assets(like symbols) need to be positioned with some x or y offset on sprite pane (unlike symbols who could be positioned with overlapping).
You're actually loading a complete svg including embedded css style elements but choose a particular view frame.
See this articel on css.tricks.com
css.tricks.com: How SVG Fragment Identifiers Work
Your svg would be something like this
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 32 96" >
<g id="icon01">
<path d="M20.6,23.3L14,16.7V7.9h4v7.2l5.4,5.4L20.6,23.3z M16-0.1c-8.8,0-16,7.2-16,16s7.2,16,16,16s16-7.2,16-16S24.8-0.1,16-0.1z M16,27.9c-6.6,0-12-5.4-12-12s5.4-12,12-12s12,5.4,12,12S22.6,27.9,16,27.9z"/>
</g>
<g id="icon02">
<path d="M32,43.2c0,2.7-1.2,5.1-3,6.8l0,0l-10,10c-1,1-2,2-3,2s-2-1-3-2l-10-10c-1.9-1.7-3-4.1-3-6.8c0-5.1,4.1-9.2,9.2-9.2c2.7,0,5.1,1.2,6.8,3c1.7-1.9,4.1-3,6.8-3C27.9,33.9,32,38.1,32,43.2z"/>
</g>
<view id="icon-clock-view" viewBox="0 0 32 32" />
<view id="icon-heart-view" viewBox="0 32 32 32" />
</svg>
For using the fragment identifiers we need to add <view> elements with corresponding sprite area coordinates (defined in viewBox attribute).
Unlike symbol definitions we don't nest the actual path data in view elements.
Embedding in html works by adding the target ID to our src url.
<img class="svg-fragment" src="fragment-ready-1.svg#icon-heart-view">
However, inlining svg graphics in your html, still offers the best styling controls.
(e.g styling elements in your websites css file)
Maybe we'll see additional support for styling remotely embedded svg assets in the future ... Until then we still have to cope with some browser quirks.
I am learning about SVG and came across this MDN resource which states "Note that all SVG presentation attributes can be used as CSS properties." In other words, in my stylesheet, this should work:
svg { color-interpolation: sRGB }
Note that the list of presentational attributes does not include all SVG attributes, as shown here (e.g., fx and fy are not included in the Presentational Attributes list)
Based on these, I believed the following should NOT work.
Go to this MDN resource and open the codepen for this ellipse. You will see this SVG:
<svg width="120" height="120" viewBox="0 0 120 120"
xmlns="http://www.w3.org/2000/svg">
<ellipse cx="60" cy="60" rx="50" ry="25"/>
</svg>
Based on the Presentational Attributes, notice none of the ellipses attributes (e.g., rx) are presentational. So my (incorrect) belief was the following should NOT work:
<ellipse cx="60" cy="60" ry="25"/> // remove inline rx attribute
and move this into my stylesheet:
ellipse { rx: 50 }
However, based on the codepen, this clearly DOES work, and you can even move ALL these non-presentational attributes into the stylesheet:
<ellipse /> // removed all "non-presentational" attributes
ellipse { cx:60; cy:60; rx:50; ry:25; } // written into the stylesheet -> this regenerates the ellipse
So, unlike the MDN document suggests, is it true that all SVG attributes can be moved from inline to a CSS stylesheet?
I am examining how to add external SVG files in a responsive manner, and fell for the SVG image tag trick, because it doesn't require JavaScript.
(The SVG has been 'washed' with scour, thus being stripped of height/width attributes, and viewBox being added, as recommended.)
The problem is that this technique seems to require a height and width attribute to work, on the image tag, which isn't responsive. Suggested syntax is:
<svg width="200px" height="100px">
<image xlink:href="logo.svg" src="logo.png" width="200px" height="100px"/>
</svg>
However, setting relative dimensions, like so:
<svg style="width:100%; height:100%">
<image xlink:href="logo.svg" src="logo.png" width="100%" height="100%"/>
</svg>
.. makes the SVG responsive, however renders the <image> element incorrectly (or, not as expected anyway). This can be fixed by adding preserveAspectRatio and viewBox attributes:
<svg style="width:100%; height:100%" preserveAspectRatio="xMidYMid meet" viewBox="0 0 200 100">
<image xlink:href="logo.svg" src="logo.png" width="100%" height="100%"/>
</svg>
Now everything works as expected in all major browsers, except that in IE9-11, the problem now lies with <svg> tag: it's not wrapped around the <image> tag.
Been playing around with various combinations, like omitting the <svg>'s height attribute, but to no avail.
Has anyone solved this without using JavaScript or conditional statements?
Note: Other methods to achieve the same is of course welcome (that is, responsive, external SVG file, working fallback, and without using JavaScript)
Note 2: The described method does not fallback gracefully in Safari on IOS 5 either.
I was working on the same issue today and ran across your question hoping for an answer.
Check out this code for the answer: http://jsfiddle.net/ECTBL/
The trick was having the right attributes in my SVG file i.e.
height="..."
width="..."
viewBox="..."
xml:space="preserve"
When I saved the file from illustrator, it was missing a height and width attributes in the SVG file.
Finally, you'll need the following CSS for it to work:
This is for a bug in webkit
svg { max-height: 100%; }
And of course, this makes the image stretch.
img { width: 100%; }
From the Mozilla Developer page, I am trying to recreate their SVG mask for CSS using the mask property.
CSS:
#canvasPreview {
mask: url(images/masks/circle2.svg#circleMask);
}
HTML:
<img id="canvasPreview" src="placehold.it/100x100"></img>
SVG:
<svg>
<mask id="circleMask" maskContentUnits="objectBoundingBox">
<circle cx="50" cy="50" r="50" fill="white" />
</mask>
</svg>
The SVG is externally referenced, not embedded like the MDN example. I have not tried embedding it, but I don't see why that would help. There are a few things I'm not clear on.
In the CSS, why am I putting the #circleMask identifier after the URL? Removing it does not make my code work and its inclusion was found on the MDN site specs, so I put it in there.
Is my SVG correct? Do I have to use the <mask> tag and id and objectBoundingBox attributes? What are they used for?
The part after the # in the URL must be the same as id of the mask. So you have that part right.
You can use objectBoundingBox if you want but then your content seems wrong. Your mask radius is 50 times the size of the object it is masking. Perhaps you meant 50%. Same with cx and cy.
The SVG spec talks about Properties.. what are these? Can they be declared as attributes inline with the element? .. or can they only be declared in CSS stylesheets?
They can be set both inline and in a stylesheet, but to be standards compliant I would opt for declaration via an external stylesheet
Standards compliant are both. There are several reasons why to use the one or the other.
The spec says, that CSS style declared properties always take precedence before the ones declared in XML attributes
On the other hand, if you use attributes, you don't have the hassle to parse CSS declarations
you can also declare an external stylesheet and style yur SVG from there
Styling properties, in short, are all these props, that are necessary for a certain rendering result, mostly related to color.
Equivalent examples:
<svg xmlns="http://www.w3.org/2000/svg">
<rect fill="red"/>
<svg>
<svg xmlns="http://www.w3.org/2000/svg">
<rect style="fill: red"/>
<svg>
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<style type="text/css">
#fillme { fill: red; }
</style>
</defs>
<rect id="fillme"/>
<svg>
Just note, that these CSS declarations are not valid in the sence of CSS specs 1 through 3.
Cheers,