How to add a CSS transform or filter to an existing inline SVG transform or filter? - css

Without knowing what the inline SVG transform or filter is, how can I apply extra transforms or filters to the same element using CSS?
Whatever I try, my CSS transform or filter overwrites the inline SVG transform/filter, instead of adding to it.
For example I have in my SVG:
<circle r="50" transform="translate(100,100)">
And then I try to scale this circle using a CSS transform, without removing its initial translate transform:
circle {
transform: initial scale(0.5);
}
However, the original translate is replaced by the scale transform, they are not added.
The same problem applies to filters.

As #ccprog says. Wrap the element in a parent <g> element, and apply your transform or filter to that.
<g>
<circle r="50" transform="translate(100,100)">
</g>
g {
transform: scale(0.5);
}
However, be aware that may give a different result to what you are expecting.
The following two SVGs are not equivalent.
<g transform="scale(0.5)">
<circle r="50" transform="translate(100,100)">
</g>
<g>
<circle r="50" transform="translate(100,100) scale(0.5)">
</g>
You would need to modify the transform you apply to the group to get an equivalent result.
The equivalent of
<g>
<circle r="50" transform="translate(100,100) scale(0.5)">
</g>
is either
<g transform="scale(0.5) translate(100,100)">
<circle r="50" transform="translate(100,100)">
</g>
or
<g transform="translate(50,50) scale(0.5)">
<circle r="50" transform="translate(100,100)" fill="blue"/>
</g>

Related

Clear an already set "fill" property in CSS to get defaults back for an SVG

I'm using a platform that within it's own CSS it sets the fill property as so:
.tatsu-svg-icon-custom svg * {
fill: currentColor;
}
This ends up with an SVG I am adding being black in this instance - which is not helpful. This particular SVG is a multi-colored SVG and handles all the fill properties itself within the code of the SVG.
Obviously if I change this property to aother color, it colors the whole SVG that color - so that is not helpful either.
So my question is, how do I get the defaults back so it doesn't apply any color to it? Setting it to initial makes the SVG transparent.
The keyword that would help in this situation is revert-layer. Unfortunately, it is currently only implemented in Firefox (>= 97).
(This example will seem to work even for other browsers. But that is because for them, it is an invalid keyword.)
.tatsu-svg-icon-custom svg * {
fill: revert-layer;
}
<div class="tatsu-svg-icon-custom">
<svg width="100" viewBox="0 0 20 20">
<circle r="5" cx="5" cy="5" fill="yellow" />
<circle r="5" cx="5" cy="15" fill="blue" />
<circle r="5" cx="15" cy="5" fill="red" />
<circle r="5" cx="15" cy="15" fill="green" />
</svg>
</div>

SVG radius or position with CSS variables

Is it possible to use CSS Variables in SVG to manipulate values like radius or position in the "attribute styles" of an element?
For Example, in the below code, I have added a CSS color variable - --dark-text-clr and a radius variable --radius. When I use the color CSS variable in fill it renders fine - 1st circle, but using the radius variable doesn't render the element - 2nd circle.
:root{
--dark-text-clr: blue;
--radius: 12;
}
<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="100px" viewBox="0 0 300 100">
<circle cx="9" cy="9" fill="var(--dark-text-clr)" mask="url(#moon-mask)" r=9 ></circle>
<circle cx="36" cy="20" fill="var(--dark-text-clr)" mask="url(#moon-mask)" r="var(--radius)" ></circle>
</svg>
Yes, but CSS must have units for non-zero values.
:root{
--dark-text-clr: blue;
--radius: 12px;
}
<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="100px" viewBox="0 0 300 100">
<circle cx="9" cy="9" fill="var(--dark-text-clr)" mask="url(#moon-mask)" r=9 ></circle>
<circle cx="36" cy="20" fill="var(--dark-text-clr)" mask="url(#moon-mask)" r="var(--radius)" ></circle>
</svg>
According to the MDN Docs "Starting with SVG2, r is a Geometry Property meaning this attribute can also be used as a CSS property for circles."
There are three ways to set the radius value
as attribute
<circle ... r=10>
via class and stylesheet
circle {
r: 10px;
}
inline in 'style' attribute
<circle... style="r: 10px;" ></circle>
The last way has the greates presedence. Take a look at this example where all circle elements have r set as attribute, which is overridden by the stylesheet (2nd circle), which is overridden again by the inline style attribute (3rd circle)
(These three ways don't have to be used together, but are only combined to demontrate which one has a higher presedence and overwrites the already set values)
:root {
--dark-text-clr: purple;
--radius: 20px;
}
.circle {
r: 10px;
}
<svg xmlns="http://www.w3.org/2000/svg" width="300px" height="300px" viewBox="0 0 300 300">
<circle cx="10" cy="10" fill="var(--dark-text-clr)" mask="url(#moon-mask)" r=5></circle>
<circle cx="30" cy="30" fill="var(--dark-text-clr)" mask="url(#moon-mask)" r=5 class="circle"></circle>
<circle cx="60" cy="60" fill="var(--dark-text-clr)" mask="url(#moon-mask)" r=5 class="circle" style="r: var(--radius);" ></circle>
</svg>
Setting r with the variable on the attribute seems to be working in firefox, but not in chrome/edge
<circle ... r="var(--radius);" ></circle>
so better set it on the style attribute
<circle ... style="r: var(--radius);" ></circle>

How to apply CSS filter saturate to SVG elements? [duplicate]

This question already has an answer here:
Why don't CSS filters work on SVG elements in Chrome?
(1 answer)
Closed 4 years ago.
I have this svg composition:
<svg width="400" height="200">
<circle id="circle-1" cx="50" cy="100" r="40"></circle>
<circle id="circle-2" cx="150" cy="100" r="40"></circle>
</svg>
I would like to apply filter: saturate(0.2) to the second circle but the CSS filter property is not working.
In SVG elements we have to use SVG Color Matrix Transformations.
For the specific case we have to do:
<svg width="400" height="200">
<filter id="saturate" filterUnits="objectBoundingBox">
<feColorMatrix type="saturate" in="SourceGraphic" values="0.2"/>
</filter>
<circle id="circle-1" cx="50" cy="100" r="40" ></circle>
<circle id="circle-2" cx="250" cy="100" r="40" filter="url(#saturate)"></circle>
</svg>
Working example: https://jsfiddle.net/fguillen/dx947a36/19/

SVG apply same transform-origin to all children of a group/symbol

I can't get reusable items or all children of a group to conform to a specified transform-origin. The goal is to be able to reuse the same shape over and over with the same template of style. However, considering transform-origin the styling, CSS or otherwise, does not cascade. It will only apply at time of <use>.
For example:
svg {
width: 125px; height: 125px;
background: rgba(0, 0, 0, 0.5);
}
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
<defs>
<ellipse id="svg-ellipse-def" cx="500" cy="500" rx="140" ry="455" transform-origin="center" style="transform-origin: center"/>
</defs>
<symbol id="svg-ellipse" >
<ellipse cx="500" cy="500" rx="140" ry="455" transform-origin="center" style="transform-origin: center"/>
</symbol>
<g fill="none" stroke="red" stroke-width="50">
<use xlink:href="#svg-ellipse"/>
<use xlink:href="#svg-ellipse" transform="rotate(45)"/>
<use xlink:href="#svg-ellipse" transform="rotate(90)"/>
<use xlink:href="#svg-ellipse-def" transform="rotate(-45)"/>
</g>
</svg>
Essentially, the transform origin doesn't apply regardless if I use class= attribute or transform-origin= property or even inline style. I've also tried wrapping it in a <defs> container.
Desired outcome:
<use xlink:href="#svg-ellipse" transform="rotate(45)"/>
<use xlink:href="#svg-ellipse" transform="rotate(90)"/>
<use xlink:href="#svg-ellipse" transform="rotate(-45)"/>
But right now it looks like this:
<use xlink:href="#svg-ellipse" transform="rotate(45)" style="transform-origin:center"/>
<use xlink:href="#svg-ellipse" transform="rotate(90)" style="transform-origin:center"/>
<use xlink:href="#svg-ellipse" transform="rotate(-45)" style="transform-origin:center"/>
svg {
width: 125px; height: 125px;
background: rgba(0,0,0,0.5);
}
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000">
<symbol id="svg-ellipse" >
<ellipse cx="500" cy="500" rx="140" ry="455"/>
</symbol>
<g fill="none" stroke="red" stroke-width="50">
<use xlink:href="#svg-ellipse"/>
<use xlink:href="#svg-ellipse" transform="rotate(45)" transform-origin="center"/>
<use xlink:href="#svg-ellipse" transform="rotate(90)" transform-origin="center"/>
<use xlink:href="#svg-ellipse" transform="rotate(-45)" transform-origin="center"/>
</g>
</svg>
According to the documentation:
If the ‘use’ element references a ‘symbol’ element:
In the generated content, the ‘use’ will be replaced by ‘g’, where all attributes from the ‘use’ element except for ‘x’, ‘y’, ‘width’, ‘height’ and ‘xlink:href’ are transferred to the generated ‘g’ element. An additional transformation translate(x,y) is appended to the end (i.e., right-side) of the ‘transform’ attribute on the generated ‘g’, where x and y represent the values of the ‘x’ and ‘y’ attributes on the ‘use’ element. The referenced ‘symbol’ and its contents are deep-cloned into the generated tree, with the exception that the ‘symbol’ is replaced by an ‘svg’.
Looking at the dev console, this is confirmed, and the style is applied inline but not honored:
Even if the <use> element has an inline styling of transform-origin, because the symbol now is converted to it's own SVG as a child of the <use> element, and it has it's own inline styling, shouldn't that take higher priority over it's parent's inline?
You can target the <use> element itself, but none of its content. The content can, in principle, inherit CSS properties. But the barrier you'll always run into is: neither the CSS transform nor the transform-origin property are inheritable.
If you rotate the <use> element, you rotate it around the tranform origin of the <use> element, while the contents of the shadow DOM stay in place relative to its root.
If you set a transform-origin for the <symbol> or <ellipse>, it will only be applied if you transform that itself, and the transformation will be cloned into each of its reuses.
The best solution I see is giving all use elements that reference the same symbol the same transform-origin. For the attribute selector to work, you'll need to leave off the xlink namespace. That is deprecated anyway, but you'll have to consider browser compatibility.
But then, the same is true for transform-origin support in SVG. That, by the way is the reason for
setting transform-box: fill-box. After there were some differences in implementation between Firefox and Chrome, it is now accepted that this
property is needed for SVG elements to transform them in relation to their bounding box. I've changed your example a bit to demonstrate.
svg {
width: 125px; height: 125px;
background: rgba(0, 0, 0, 0.5);
}
use[href="#symbol1"] {
transform-origin: center;
transform-box: fill-box;
}
<svg version="1.0" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1500 1500">
<symbol id="symbol1" >
<ellipse cx="500" cy="500" rx="140" ry="455" />
</symbol>
<g fill="none" stroke="red" stroke-width="50">
<use href="#symbol1" transform="translate(200, 400)" />
<use href="#symbol1" transform="translate(200, 400) rotate(45)"/>
<use href="#symbol1" transform="translate(200, 400) rotate(90)"/>
<use href="#symbol1" transform="translate(200, 400) rotate(-45)"/>
</g>
</svg>

svg grouped elements jointly hover

Is it possible for hover on an svg element cause other elements with the same class to hover too without jQuery? Or do I have to next the two into an outer group?
I have inside an inline svg the following groups:
<g class="class1">
<path....>
<path....>
</g>
<g class="class1">
<path....>
<path....>
</g>
I then have in my CSS:
class1 {
...
}
class1:hover {
...
}
I guess you can't do it directly, but you can achieve it by adding an id to the parent element, no classes needed, like that:
#circles:hover circle{
fill: Wheat;
}
<svg id="circles" width="100" height="260" >
<circle cx="50" cy="50" r="40" stroke="Tomato" stroke-width="4" fill="Tomato" />
<circle cx="50" cy="140" r="40" stroke="Tomato" stroke-width="4" fill="Aquamarine"/>
</svg>

Resources