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?
Related
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 have a javascript app (CAD like) build in SVG that have tools (objects build in SVG with Inkscape) that are inserted with the use tag. Everything works fine on Chrome and Safari (never tested in IE), but in Firefox, all objects that fill with gradients did not render. Like image below:
Firefox
Chrome
The tools are loaded like this (using external files)
<use
id="SvgjsUse1448"
xmlns:xlink="http://www.w3.org/1999/xlink"
xlink:href="assets/images/neomap/tools/armchair.svg#armchair">
</use>
The gradients are defined in a separate svg inside the html page
...
<linearGradient xmlns="http://www.w3.org/2000/svg" id="armchair_SVGID_1_" y2="1911.6" gradientUnits="userSpaceOnUse" x2="1201.2" gradientTransform="matrix(1.0799475,0,0,1.2039969,15.269894,2.997636)" y1="467.91" x1="1201.7">
and apply like this
<svg xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:dc="http://purl.org/dc/elements/1.1/" height="100%" width="100%" version="1.1" >
<defs>
<symbol id="armchair" viewBox="0 0 72000 54000">
<path style="stroke:#000000;stroke-width:6.84171867;stroke-miterlimit:10;fill:url(#armchair_SVGID_1_);" d="m2234.8,743.7s168.9,1406.4,0,1487.4c-188.77,90.42-1655.6,92.708-1844.3-0.4817-168.8-83.316,0-1488.9,0-1488.9v-182.09h1841l3.2398,184.09z"/>
...
</symbol>
</defs>
</svg>
It's a bug on Firefox? How can I workaround?
I had a similar problem and it turned out that mine was a real edge case, so I'm posting my solution here for the benefit of others.
My goal was to define a set of icons for later reuse in the document. I had defined the icons as nested svg documents inside a defs element:
<svg id="icons">
<defs>
<g id="atom-icon">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 256 256">
<defs>
<linearGradient id="atom-41-a" x1="50%" x2="50%" y1="3.961%" y2="100%">
<stop offset="0%" stop-color="#FDFDFD"/>
<stop offset="100%" stop-color="#CBCBCB"/>
</linearGradient>
<linearGradient id="atom-41-b" x1="50%" x2="50%" y1="3.961%" y2="100%">
<stop offset="0%" stop-color="#B2E198"/>
<stop offset="100%" stop-color="#04A171"/>
</linearGradient>
</defs>
<g fill="none">
<circle cx="128" cy="128" r="127.858" fill="url(#atom-41-a)"/>
<circle cx="127.858" cy="128.142" r="116.907" fill="url(#atom-41-b)"/>
<path fill="#F2F8F6" d="M100.453 110.097a221.783 221.783 0 0 0-3.988 6.382 164.44 164.44 0 0 1-3.684-11.377c3.73-.535 7.695-.957 11.874-1.249a224.886 224.886 0 0 0-4.202 6.244zm103.138 7.475c-4.132-3.967-10.04-7.602-17.558-10.804-14.926-6.356-34.918-10.474-56.295-11.595a234.82 234.82 0 0 0-7.529-.277C138.53 75.415 154.292 65.05 163.472 64.7c1.944-.074 3.576.31 4.854 1.141 4.296 2.792 5.488 11.453 3.189 23.166a4.267 4.267 0 0 0 8.373 1.644c3.953-20.137-1.576-28.497-6.911-31.965-2.785-1.81-6.093-2.655-9.83-2.513-14.12.536-33.904 15.592-51.956 38.803-7.148.247-14.038.864-20.483 1.827a106.106 106.106 0 0 1-1.625-9.522c-1.619-13.344.367-22.816 5.312-25.337.794-.405 1.662-.639 2.654-.713 4.418-.332 10.652 2.637 17.542 8.359a4.267 4.267 0 0 0 5.452-6.565c-8.85-7.35-16.8-10.817-23.633-10.303-2.112.158-4.095.703-5.892 1.62-4.593 2.342-7.732 6.828-9.33 13.331-1.368 5.57-1.562 12.513-.576 20.636.39 3.213.96 6.556 1.695 9.998-5.04 1.062-9.71 2.356-13.897 3.869-18.316 6.616-22.544 15.337-22.866 21.488-.308 5.876 2.7 14.62 18.775 22.91a4.267 4.267 0 0 0 3.911-7.583c-9.247-4.77-14.41-10.193-14.165-14.88.346-6.605 11.417-13.596 30.274-17.535 1.747 6.309 3.99 12.861 6.68 19.505-6.777 12.723-11.722 25.262-14.286 36.416-1.833 7.97-2.369 14.89-1.592 20.567.906 6.63 3.558 11.418 7.881 14.228 2.205 1.433 5.176 2.553 9.079 2.553 5.658 0 13.275-2.357 23.343-9.538a4.267 4.267 0 1 0-4.955-6.948c-9.925 7.08-18.454 9.613-22.816 6.778-4.652-3.023-5.633-12.642-2.624-25.728 1.995-8.678 5.555-18.296 10.383-28.21a220.995 220.995 0 0 0 3.283 6.731c9.717 19.087 21.612 35.694 33.494 46.762 5.984 5.573 11.708 9.497 17.011 11.663 3.218 1.314 6.24 1.971 9.045 1.971 2.596 0 5.008-.563 7.216-1.69 4.43-2.259 7.511-6.52 9.157-12.665 1.41-5.263 1.726-11.827.942-19.512-1.543-15.11-7.253-33.539-16.077-51.891a4.267 4.267 0 0 0-7.69 3.697c8.407 17.485 13.833 34.908 15.278 49.06 1.27 12.447-.78 21.31-5.486 23.71-4.942 2.52-13.756-1.438-23.58-10.588-11.17-10.404-22.43-26.168-31.706-44.388a210.555 210.555 0 0 1-5.851-12.483 210.936 210.936 0 0 1 7.143-11.836 213.301 213.301 0 0 1 7.877-11.333c.7-.01 1.405-.017 2.115-.019 3.664-.012 7.592.09 11.688.305 20.404 1.07 39.368 4.95 53.399 10.925 12.34 5.256 19.534 11.704 19.244 17.25-.246 4.708-5.967 9.575-15.696 13.352a4.267 4.267 0 0 0 3.088 7.955c16.905-6.563 20.821-14.967 21.13-20.861.27-5.151-2.04-10.111-6.865-14.743v.001z"/>
<path fill="#F1F8F3" d="M119.194 129.133c0 5.153 4.174 9.33 9.323 9.33s9.323-4.177 9.323-9.33c0-5.154-4.174-9.331-9.323-9.331s-9.323 4.177-9.323 9.33"/>
</g>
</svg>
</g>
</defs>
</svg>
Which I used elsewhere in the document like so:
<li title="Atom">
<svg><use xlink:href="#atom-icon"/></svg>
</li>
In order to prevent the big list of icons references from rendering on screen, I had applied the following styles:
#icons {
display: none;
}
This caused, of all things, the url references to linearGradients to stop working. Probably the browsers removed them from the DOM tree because of display: none or some such effect.
The solution was the change the styles:
#icons {
height: 0;
}
That's a Chrome/Safari bug you're relying on.
url(#armchair_SVGID_1_);
is actually shorthand for
url(<this file>#armchair_SVGID_1_);
but there's no gradient in the armchair.svg file.
The definition of what a base URI is is in RFC3986 section 5 which says
Within certain media types, a base URI for relative references can be
embedded within the content itself so that it can be readily obtained
by a parser.
There's a note in the CSS specification referring to this which I think makes a clearer statement (at least for CSS since this is the CSS specification):
For CSS style sheets, the base URI is that of the style sheet, not that of the source document.
The file armchair.svg defines a base URI the same as any SVG document or CSS stylesheet does, it's the absolute URL used to access it.
Webkit browsers get this wrong. There's this bug for the CSS stylesheets case of this issue.
Use an absolute url or put the gradient in the use file (but not in the symbol part).
It's really a Chrome/Safari bug.
I figured out, that the problem goes away if you remove all the hyphens from the linearGradient elements' IDs. Simple as that. SVG then works with groups too ().
I would be glad, if that helps somebody...
Firefox is behaving correctly according to the spec for the SVG <symbol> element. With SVG any element that are nested inside a <symbol> element or <defs> element are not directly rendered with CSS, even with a <use> element. Only their attributes are. You are using the style attribute, which is CSS inside the <symbol> element.
You must use the SVG fill attribute directly to have it work inside a <symbol> element. Even though you are using a SVG <use> element. It is referencing SVG graphical elements inside a SVG <symbol> element with the CSS style attribute, which will not be rendered according to the spec, since the CSS display property does not even apply to the SVG <symbol> element.
This is not a bug in Firefox, Firefox honors the spec whereas Chrome and Safari which use webkit, do not follow the spec and allow it for now. But they will soon follow the SVG spec.
See these references and the spec:
W3C <symbol> spec: http://www.w3.org/TR/SVG11/struct.html#SymbolElement
‘symbol’ elements are never rendered directly; their only usage is as something that can be referenced using the ‘use’ element. The ‘display’ property does not apply to the ‘symbol’ element; thus, ‘symbol’ elements are not directly rendered even if the ‘display’ property is set to a value other than none, and ‘symbol’ elements are available for referencing even when the ‘display’ property on the ‘symbol’ element or any of its ancestors is set to none.
MDN Firefox <symbol> element reference: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/symbol
The symbol element is used to define graphical template objects which can be instantiated by a element. The use of symbol elements for graphics that are used multiple times in the same document adds structure and semantics. Documents that are rich in structure may be rendered graphically, as speech, or as braille, and thus promote accessibility. note that a symbol element itself is not rendered. Only instances of a symbol element (i.e., a reference to a symbol by a element) are rendered.
The spec states that SVG elements inside the <symbol> element are not directly rendered. In that case you need to change the actually attributes of those elements inside a <symbol> element or <defs> elements. So just change the SVG fill attribute instead on those elements inside the <symbol> element.
Ok, this is a bug, but also an interpretation from the W3 Spec. In this section is specified two types of IRI local, and non-local.
local IRI references, where the IRI reference does not contain an or and thus only contains a fragment identifier
non-local IRI references, where the IRI reference does contain an <absoluteIRI> or <relativeIRI> and thus represents a reference to an element within the current document.
What I see, is that Chrome/Safari are more flexible when dealing with the specification, allowing the local IRI to reference an external document, if it not exist in the current document.
The point is, Chrome/Safari doesn't render the gradients of an external SVG file (loaded by reference, in a use tag) that are at the same external file, only if the gradients are loaded in the current main html/xml document.
So, I create two files for each SVG tool, one for Firefox, and one for other browsers. And now another problem as came up, for low values of transform: scale() in a g element, or small SVG elements, the gradient disappear (transparent), and this I believe is a bug.
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.
I have an SVG file that I am applying CSS to. Most rules seem to work, but when I apply rules about the rounding of corners (rx:5; ry:5) it has no effect. 'Inline' style rules work, but I'm having no luck with embedded and external style sheets:
<svg ...>
<defs>
<style type="text/css" >
<![CDATA[
rect{ rx:5; ry:5; }
]]>
</style>
</defs>
<rect
height="170" width="70" id="rect7"
x="0" y="0" />
</svg>
Any idea where I am going wrong?
rx and ry are regular attributes rather than presentation attributes. Only presentation attributes can be styled by CSS. The various regular/presentation attributes are listed here
See also Presentation Attribute and Property from the SVG 1.1 specification.
The upcoming SVG 2 specification proposes that most presentation attributes become CSS properties. So far Chrome and Firefox have implemented this part of the draft specification. I imagine other UAs will implement this in due course.
Scripting can't be simpler, why not to use it:
yourRect.setAttributeNS(null, "rx", "5");
yourRect.setAttributeNS(null, "ry", "5");
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,