use var to set offset position in SVG gradient - css

I am using a gradient to fill an svg path.
like this:
<svg viewBox="0 0 40 40">
<defs>
<linearGradient id="progress-gradient" x1="0%" x2="0%" y1="0%" y2="100%">
<stop offset="var(--offset-empty)" stopColor="var(--color-empty)" />
<stop offset="var(--offset-full)" stopColor="var(--color-full)" />
</linearGradient>
</defs>
<path d="..removed" fill="url(#progress-gradient)" />
</svg>
and I am styling it with css:
#progress-gradient {
--color-empty: #ffffff;
--color-full: #ff0000;
--offset-empty: 50%;
--offset-full: 100%;
}
I am trying to set the offsets dynamically to create a kind of progress within the SVG icon.
But this isn't working. Colors are working, but not offset.
Is there any other way I can dynamically set the offset positions with CSS?
many thanks

Related

How to set the size of the handle in a QSlider?

I'm trying to add some style to a QSlider I want to use a custom image as the handle that the user drags back and forth. While I've figured out how to use style sheets to have a custom icon drawn where the handle should be, the image is being drawn much too small and I cannot figure out how to make it larger.
Setting width and height seem to do nothing. I've tried using image, border-image and background-image, but none give me the ability to set the size of the handle image. Does anyone know how to do this?
This is the style sheet that I've been adding to my QSlider in QtDesigner:
QSlider::handle:vertical {
image: url(:/data/icons/mixer-slider-handle.svg);
width:64px;
height:64px;
}
This is the SVG:
<svg
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="30"
height="45"
viewBox="0 0 7.9374997 11.90625"
version="1.1"
id="svg8"
>
<defs
id="defs2">
<linearGradient
id="linearGradient844"
inkscape:collect="always">
<stop
id="stop840"
offset="0"
style="stop-color:#cecece;stop-opacity:1;" />
<stop
id="stop842"
offset="1"
style="stop-color:#ffffff;stop-opacity:1" />
</linearGradient>
<linearGradient
inkscape:collect="always"
id="linearGradient824">
<stop
style="stop-color:#cecece;stop-opacity:1;"
offset="0"
id="stop820" />
<stop
style="stop-color:#ffffff;stop-opacity:1"
offset="1"
id="stop822" />
</linearGradient>
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient824"
id="linearGradient826"
x1="-3.9103179"
y1="297.24557"
x2="-3.8304768"
y2="285.38882"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.8683302,0,0,0.96503255,7.3827223,9.9179025)" />
<linearGradient
inkscape:collect="always"
xlink:href="#linearGradient844"
id="linearGradient838"
gradientUnits="userSpaceOnUse"
gradientTransform="matrix(0.86322913,0,0,0.81935486,7.5301966,52.317886)"
x1="-3.8119318"
y1="285.99686"
x2="-3.7885454"
y2="296.82458" />
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(0,-285.09375)">
<rect
style="fill:url(#linearGradient826);fill-opacity:1;stroke:#0e0e0e;stroke-width:0.1373108;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect818"
width="5.157938"
height="11.397007"
x="1.453124"
y="285.36124"
ry="2.866178" />
<rect
style="opacity:1;fill:url(#linearGradient838);fill-opacity:1;stroke:none;stroke-width:0.12615089;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
id="rect828"
width="4.6027613"
height="8.9100981"
x="1.7161824"
y="286.60291"
ry="2.184411" />
<path
style="fill:none;stroke:#000000;stroke-width:0.24780074px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
d="m 1.8804469,291.0695 4.3266789,0.047"
id="path846"
inkscape:connector-curvature="0" />
</g>
</svg>
Looks like the answer is to set the margin attribute for both the groove and the handle to reflect the size of the handle you are using.
This worked for me:
.QSlider::groove:vertical {
border: 1px solid #111;
background-color: #333;
width: 6px;
margin: 24px 12px;
}
.QSlider::handle:vertical {
image: url(:/data/icons/mixer-slider-handle.svg);
margin: -24px -12px;
height: -30px;
}

Is it possible to animate a linear gradient gradientTransform property of an svg image

I have a simple outer circle svg created with the following code
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns="http://www.w3.org/2000/svg"
id="Layer_1"
data-name="Layer 1"
viewBox="0 0 260 260">
<style>
.class1 {
fill-opacity:1;
fill:url(#grayGradient);
}
</style>
<defs>
<linearGradient id="grayGradient"
gradientUnits="userSpaceOnUse"
x1="0" y1="130" x2="260" y2="130">
<stop offset="0" style="stop-color:#919191;stop-opacity:1;" />
<stop offset="1" style="stop-color:#919191;stop-opacity:0;" />
</linearGradient>
</defs>
<title>outer circle</title>
<path
d="M130,0A130,130,0,1,0,260,130,130,130,0,0,0,130,0Zm.36,235a105,105,0,1,1,105-105A105,105,0,0,1,130.36,235Z"
class="class1" />
</svg>
I'm trying to make this look like an indeterminate progress circle, so I want to rotate the gradient around the path of the circle. I know that if I add a gradientTransform=rotate() attribute to the linearGradient, it will move the position of the gradient in the circle.
Is it possible to animate the gradientTransform so that it will appear that the circle is spinning?
I'm very new to svg. Perhaps there is a better way to achieve the effect I'm looking for? I do need to use an svg image that has the same shape.
Thanks for any and all suggestions...
Just add inside <linearGradient> ... </linearGradient> this string:
<animateTransform
attributeName="gradientTransform"
type="rotate"
from="0 130 130"
to="360 130 130"
dur="1s"
repeatCount="indefinite"
/>
But it won't work in IE and Safari.

How to use less #variable for SVG inline style "stop-color"

I have two variables for colors in color.less
#color-example-1: red;
#color-example-2: yellow;
and an svg in example.html that looks something like this:
<svg viewBox="0 0 48 48">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="0" y1="24" x2="48" y2="24">
<stop offset="0" style="stop-color:#FFF33B"/>
<stop offset="1" style="stop-color:#E93E3A"/>
</linearGradient>
<circle class="st0" cx="24" cy="24" r="24"/>
</svg>
Is it possible to either replace the stop-color value with a #variable or (even better) define the whole linearGradient in the css file?
A desired result would be something like this:
css
.example-gradient {
background: linear-gradient(135deg, #color-example-1 0%,#color-example-2 100%);
}
html
<svg viewBox="0 0 48 48">
<circle class="example-gradient" cx="24" cy="24" r="24"/>
</svg>
You can't define the whole gradient in CSS. CSS gradients don't currently work on SVG elements. They might one day in the future. If they did, you would use something like the following:
circle {
fill: linear-gradient(to right, yellow, orange)
}
However all is not lost. You definitely can restyle the <stop> elements in the SVG gradient definition.
Note that the style="stop-color: ..." in the SVG gradient will override any CSS you define. So the first thing you need to do is remove it, or change it to a presentation attribute (stop-color="#abcdef").
.stop1 {
stop-color: blue;
}
.stop2 {
stop-color: yellow;
}
<svg viewBox="0 0 48 48">
<style type="text/css">
.st0{fill:url(#SVGID_1_);}
</style>
<linearGradient id="SVGID_1_" gradientUnits="userSpaceOnUse" x1="0" y1="24" x2="48" y2="24">
<stop offset="0" class="stop1" stop-color="#FFF33B"/>
<stop offset="1" class="stop2" stop-color="#E93E3A"/>
</linearGradient>
<circle class="st0" cx="24" cy="24" r="24"/>
</svg>
Note that I obviously haven't used LESS here, but it should work fine as long as your SVG is inlined in your HTML.

SVG gradient using CSS

I'm trying to get a gradient applied to an SVG rect element.
Currently, I'm using the fill attribute. In my CSS file:
rect {
cursor: pointer;
shape-rendering: crispEdges;
fill: #a71a2e;
}
And the rect element has the correct fill color when viewed in the browser.
However, I'd like to know if I can apply a linear gradient to this element?
Just use in the CSS whatever you would use in a fill attribute.
Of course, this requires that you have defined the linear gradient somewhere in your SVG.
Here is a complete example:
rect {
cursor: pointer;
shape-rendering: crispEdges;
fill: url(#MyGradient);
}
<svg width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
<style type="text/css">
rect{fill:url(#MyGradient)}
</style>
<defs>
<linearGradient id="MyGradient">
<stop offset="5%" stop-color="#F60" />
<stop offset="95%" stop-color="#FF6" />
</linearGradient>
</defs>
<rect width="100" height="50"/>
</svg>
2019 Answer
With brand new css properties you can have even more flexibility with variables aka custom properties
.shape {
width:500px;
height:200px;
}
.shape .gradient-bg {
fill: url(#header-shape-gradient) #fff;
}
#header-shape-gradient {
--color-stop: #f12c06;
--color-bot: #faed34;
}
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="none" class="shape">
<defs>
<linearGradient id="header-shape-gradient" x2="0.35" y2="1">
<stop offset="0%" stop-color="var(--color-stop)" />
<stop offset="30%" stop-color="var(--color-stop)" />
<stop offset="100%" stop-color="var(--color-bot)" />
</linearGradient>
</defs>
<g>
<polygon class="gradient-bg" points="0,0 100,0 0,66" />
</g>
</svg>
Just set a named variable for each stop in gradient and then customize as you like in css. You can even change their values dynamically with javascript, like:
document.querySelector('#header-shape-gradient').style.setProperty('--color-stop', "#f5f7f9");
Building on top of what Finesse wrote, here is a simpler way to target the svg and change it's gradient.
This is what you need to do:
Assign classes to each color stop defined in the gradient element.
Target the css and change the stop-color for each of those stops using plain classes.
Win!
Some benefits of using classes instead of :nth-child is that it'll not be affected if you reorder your stops. Also, it makes the intent of each class clear - you'll be left wondering whether you needed a blue color on the first child or the second one.
I've tested it on all Chrome, Firefox and IE11:
.main-stop {
stop-color: red;
}
.alt-stop {
stop-color: green;
}
<svg class="green" width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
<linearGradient id="gradient">
<stop class="main-stop" offset="0%" />
<stop class="alt-stop" offset="100%" />
</linearGradient>
<rect width="100" height="50" fill="url(#gradient)" />
</svg>
See an editable example here:
https://jsbin.com/gabuvisuhe/edit?html,css,output
Here is a solution where you can add a gradient and change its colours using only CSS:
// JS is not required for the solution. It's used only for the interactive demo.
const svg = document.querySelector('svg');
document.querySelector('#greenButton').addEventListener('click', () => svg.setAttribute('class', 'green'));
document.querySelector('#redButton').addEventListener('click', () => svg.setAttribute('class', 'red'));
svg.green stop:nth-child(1) {
stop-color: #60c50b;
}
svg.green stop:nth-child(2) {
stop-color: #139a26;
}
svg.red stop:nth-child(1) {
stop-color: #c84f31;
}
svg.red stop:nth-child(2) {
stop-color: #dA3448;
}
<svg class="green" width="100" height="50" version="1.1" xmlns="http://www.w3.org/2000/svg">
<linearGradient id="gradient">
<stop offset="0%" />
<stop offset="100%" />
</linearGradient>
<rect width="100" height="50" fill="url(#gradient)" />
</svg>
<br/>
<button id="greenButton">Green</button>
<button id="redButton">Red</button>
Thank you everyone,
for all your precise replys.
Using the svg in a shadow dom, I add the 3 linear gradients I need within the svg, inside a .
I place the css fill rule on the web component and the inheritance od fill does the job.
<svg viewbox="0 0 512 512" xmlns="http://www.w3.org/2000/svg">
<path
d="m258 0c-45 0-83 38-83 83 0 45 37 83 83 83 45 0 83-39 83-84 0-45-38-82-83-82zm-85 204c-13 0-24 10-24 23v48c0 13 11 23 24 23h23v119h-23c-13 0-24 11-24 24l-0 47c0 13 11 24 24 24h168c13 0 24-11 24-24l0-47c0-13-11-24-24-24h-21v-190c0-13-11-23-24-23h-123z"></path>
</svg>
<svg height="0" width="0">
<defs>
<linearGradient id="lgrad-p" gradientTransform="rotate(75)"><stop offset="45%" stop-color="#4169e1"></stop><stop offset="99%" stop-color="#c44764"></stop></linearGradient>
<linearGradient id="lgrad-s" gradientTransform="rotate(75)"><stop offset="45%" stop-color="#ef3c3a"></stop><stop offset="99%" stop-color="#6d5eb7"></stop></linearGradient>
<linearGradient id="lgrad-g" gradientTransform="rotate(75)"><stop offset="45%" stop-color="#585f74"></stop><stop offset="99%" stop-color="#b6bbc8"></stop></linearGradient>
</defs>
</svg>
<div></div>
<style>
:first-child {
height:150px;
width:150px;
fill:url(#lgrad-p) blue;
}
div{
position:relative;
width:150px;
height:150px;
fill:url(#lgrad-s) red;
}
</style>
<script>
const shadow = document.querySelector('div').attachShadow({mode: 'open'});
shadow.innerHTML="<svg viewbox=\"0 0 512 512\">\
<path d=\"m258 0c-45 0-83 38-83 83 0 45 37 83 83 83 45 0 83-39 83-84 0-45-38-82-83-82zm-85 204c-13 0-24 10-24 23v48c0 13 11 23 24 23h23v119h-23c-13 0-24 11-24 24l-0 47c0 13 11 24 24 24h168c13 0 24-11 24-24l0-47c0-13-11-24-24-24h-21v-190c0-13-11-23-24-23h-123z\"></path>\
</svg>\
<svg height=\"0\">\
<defs>\
<linearGradient id=\"lgrad-s\" gradientTransform=\"rotate(75)\"><stop offset=\"45%\" stop-color=\"#ef3c3a\"></stop><stop offset=\"99%\" stop-color=\"#6d5eb7\"></stop></linearGradient>\
<linearGradient id=\"lgrad-g\" gradientTransform=\"rotate(75)\"><stop offset=\"45%\" stop-color=\"#585f74\"></stop><stop offset=\"99%\" stop-color=\"#b6bbc8\"></stop></linearGradient>\
</defs>\
</svg>\
";
</script>
The first one is normal SVG,
the second one is inside a shadow dom.
Here is how to set a linearGradient on a target element:
<style type="text/css">
path{fill:url('#MyGradient')}
</style>
<defs>
<linearGradient id="MyGradient">
<stop offset="0%" stop-color="#e4e4e3" ></stop>
<stop offset="80%" stop-color="#fff" ></stop>
</linearGradient>
</defs>

compass/css fade border at edges?

I've seen this technique used quite a lot. Like say a separator border (like bottom border for stackoverflow header) which fades at both ends. How do I achieve this with compass? I've searched their documentation and google and can't find any examples of how to do this.
I've never used Compass CSS, but how about mixing CSS and SVG?
Your SVG file:
<svg width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<defs>
<linearGradient id="div" x1="0%" y1="0%" x2="0%" y2="100%">
<stop offset="0%" style="stop-color:rgb(255, 255, 225);stop-opacity:0"/>
<stop offset="50%" style="stop-color:rgb(153,153,153);stop-opacity:1"/>
<stop offset="100%" style="stop-color:rgb(255, 255, 225);stop-opacity:0"/>
</linearGradient>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="url(#div)" />
</svg>
The CSS:
div.separator
{
width: 80%;
height: 16px;
background-image: url(gradient_file.svg);
}

Resources