SVG radius or position with CSS variables - css

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>

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>

Draw a circle in svg with preserveAspectRatio="none"

I have an svg with viewBox="0 0 100 100" preserveAspectRatio="none" since I need to draw some elements with reference to percentages.
If I now draw a <circle cx="30" cy="50" r="0.5" stroke="white"/> though it becomes stretched/distorted as well.
Is there a way to tell the circle element to preserve its own aspect ratio?
If all you need is a circle you can use a very short, very wide line like in the following example.
This will work because
stroke-linecap="round" will give the illusion of a circle and because
strokes can take a vector-effect="non-scaling-stroke".
non-scaling-stroke
This value modifies the way an object is stroked. Normally stroking involves calculating stroke outline of the shape's path in current user coordinate system and filling that outline with the stroke paint (color or gradient). The resulting visual effect of this value is that the stroke width is not dependent on the transformations of the element (including non-uniform scaling and shear transformations) and zoom level.
div {
width: 300px;
height: 50px;
background-color: lime;
}
svg {
width:100%;
height:100%;
}
<div>
<svg viewBox="0 0 100 100" preserveAspectRatio="none">
<!--<circle cx="30" cy="50" r="20"></circle>-->
<line x1="30" y1="50" x2="30.1" y2="50" stroke-width="40" stroke="black" stroke-linecap="round" vector-effect="non-scaling-stroke" />
</svg>
</div>
If you need a white stroke too you can add a white line behind the black one with a bigger stroke width
div {
width: 300px;
height: 50px;
background-color: lime;
}
svg {
width:100%;
height:100%;
}
<div>
<svg viewBox="0 0 100 100" preserveAspectRatio="none">
<line x1="30" y1="50" x2="30.1" y2="50" stroke-width="45" stroke="white" stroke-linecap="round" vector-effect="non-scaling-stroke" />
<line x1="30" y1="50" x2="30.1" y2="50" stroke-width="40" stroke="black" stroke-linecap="round" vector-effect="non-scaling-stroke" />
</svg>
</div>

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>

Animated growing arrow link

Hi, I was wondering how one would go about animating an svg arrow like above (on hover).
I have tried playing around with CSS transforms, but they also scale the arrow-head which is no good. I assume the correct way to do this is using SVGs animations, but I don't know where to start.
For example I would the following arrow (line only) to grow and arrow head to move accordingly.
<svg width="600px" height="100px">
<defs>
<marker id="arrow" markerWidth="10" markerHeight="10" refX="0" refY="3" orient="auto" markerUnits="strokeWidth">
<path d="M0,0 L0,6 L9,3 z" fill="#f00" />
</marker>
</defs>
<line x1="50" y1="50" x2="100" y2="50" stroke="#000" stroke-width="5" marker-end="url(#arrow)" />
</svg>
Any help is very much appreciated!
You can create growing arrow by using "respoinsive" SVG like this.
svg{
width: 20px;
height: 20px;
transition:width 2s ease;
overflow: visible;
}
svg:hover{
width: 100px;
}
<svg>
<defs>
<marker id="m" markerWidth="4" markerHeight="8"
refX="0" refY="1" viewBox="0 0 1 2">
<polygon points="0,0 1,1 0,2" fill="black"/>
</marker>
</defs>
<line x1="0" y1="50%" x2="100%" y2="50%"
stroke-width="2" marker-end="url(#m)" stroke="black"/>
</svg>
There are some points to implement.
svg has no viewBox (so it is "responsive" SVG).
Line of arrow is defined by relative position of (root) svg size.
Arrow head is defined by marker element.
Growing animation is defined by CSS transition which animate width of svg. So, arrow grows with svg size.
In order to animate the individual SVG elements like HTML elements, you'll need to embed the SVG directly into the page like this:
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="612px" height="502.174px" viewBox="0 65.326 612 502.174" enable-background="new 0 65.326 612 502.174"
xml:space="preserve" class="logo">
<ellipse class="ground" cx="283.5" cy="487.5" rx="259" ry="80"/>
<path class="kiwi" d="M210.333,65.331C104.367,66.105-12.349,150.637,1.056,276.449c4.303,40.393,18.533,63.704,52.171,79.03
c36.307,16.544,57.022,54.556,50.406,112.954c-9.935,4.88-17.405,11.031-19.132,20.015c7.531-0.17,14.943-0.312,22.59,4.341
c20.333,12.375,31.296,27.363,42.979,51.72c1.714,3.572,8.192,2.849,8.312-3.078c0.17-8.467-1.856-17.454-5.226-26.933
c-2.955-8.313,3.059-7.985,6.917-6.106c6.399,3.115,16.334,9.43,30.39,13.098c5.392,1.407,5.995-3.877,5.224-6.991
c-1.864-7.522-11.009-10.862-24.519-19.229c-4.82-2.984-0.927-9.736,5.168-8.351l20.234,2.415c3.359,0.763,4.555-6.114,0.882-7.875
c-14.198-6.804-28.897-10.098-53.864-7.799c-11.617-29.265-29.811-61.617-15.674-81.681c12.639-17.938,31.216-20.74,39.147,43.489
c-5.002,3.107-11.215,5.031-11.332,13.024c7.201-2.845,11.207-1.399,14.791,0c17.912,6.998,35.462,21.826,52.982,37.309
c3.739,3.303,8.413-1.718,6.991-6.034c-2.138-6.494-8.053-10.659-14.791-20.016c-3.239-4.495,5.03-7.045,10.886-6.876
c13.849,0.396,22.886,8.268,35.177,11.218c4.483,1.076,9.741-1.964,6.917-6.917c-3.472-6.085-13.015-9.124-19.18-13.413
c-4.357-3.029-3.025-7.132,2.697-6.602c3.905,0.361,8.478,2.271,13.908,1.767c9.946-0.925,7.717-7.169-0.883-9.566
c-19.036-5.304-39.891-6.311-61.665-5.225c-43.837-8.358-31.554-84.887,0-90.363c29.571-5.132,62.966-13.339,99.928-32.156
c32.668-5.429,64.835-12.446,92.939-33.85c48.106-14.469,111.903,16.113,204.241,149.695c3.926,5.681,15.819,9.94,9.524-6.351
c-15.893-41.125-68.176-93.328-92.13-132.085c-24.581-39.774-14.34-61.243-39.957-91.247
c-21.326-24.978-47.502-25.803-77.339-17.365c-23.461,6.634-39.234-7.117-52.98-31.273C318.42,87.525,265.838,64.927,210.333,65.331
z M445.731,203.01c6.12,0,11.112,4.919,11.112,11.038c0,6.119-4.994,11.111-11.112,11.111s-11.038-4.994-11.038-11.111
C434.693,207.929,439.613,203.01,445.731,203.01z"/>
<filter id="pictureFilter" >
<feGaussianBlur stdDeviation="15" />
</filter>
</svg>
After doing this, you can use CSS animations on any of the individual SVG elements, just like HTML elements. For example, you could do the following:
svg ellipse { animate: grow 3s infinite; }
Without having the exact SVG code for your arrow, I can't give you more specific direction than that, but can point you in the direction of this article: https://css-tricks.com/using-svg/

Resources