CSS Variables default to SVG fill if invalid - css

I have an SVG with a number of rects that change fill colour as they move down the SVG (in a sort of gradient look). Users are able to choose a solid fill colour to 'theme' their experience. This colour replaces the 'gradient' color of the rects. I'm doing this using CSS variables.
However, I want to default back to the fill colour defined in the SVG if they don't choose a theme colour. In this case the CSS variable is set to '' making it invalid. For other elements I'm using a default that the element falls back to. I can't do this with the SVG rects as they're all different. I tried removing the default but I believe this sets the fill to it's initial CSS value, which is transparent.
If I have the following rect:
<rect id="rect" fill="#000000" x="0" y="0" width="200" height="50" rx="6"></rect> and the following CSS: rect { fill: var(--preview-primary); } I'd expect it to be black when --preview-primary is invalid, but it's transparent.
Is there a way I can do this? Thanks.

No, there is no way to fallback to the fill attribute.
Having a valid fill CSS rule will take precedence over the fill attribute.
For CSSOM, var(--anything) is always a valid rule. If ever the computation of the var() function is invalid, then it will fallback to the default value. And the default value for fill is black:
#f {
fill: var(--foobar);
}
<svg>
<rect id="f" x="0" y="0" height="80" width="80" fill="green"/>
</svg>
So to workaround this situation, you have a few choices available:
If you can't modify your SVG, you can toggle-on user-selected rules only when the value is not "".
sel.onchange = e => {
document.documentElement.style
.setProperty('--selected-color', sel.value);
// toggle a class so we know we have to handle it
document.documentElement.classList
.toggle('user-selected-color', sel.value !== "");
};
.user-selected-color rect {
fill: var(--selected-color);
}
select{vertical-align:top}
<svg height="180" width="180">
<rect x="0" y="0" height="80" width="80" fill="green"/>
<rect x="90" y="0" height="80" width="80" fill="blue"/>
<rect x="0" y="90" height="80" width="80" fill="red"/>
<rect x="90" y="90" height="80" width="80" fill="black"/>
</svg>
<select id="sel">
<option value="">none</option>
<option>orange</option>
<option>pink</option>
<option>violet</option>
<option>aqua</option>
</select>
If you can, you could set each element's CSS color and then fallback to the CSS value currentColor:
sel.onchange = e => {
document.documentElement.style
.setProperty('--selected-color', sel.value);
};
rect {
fill: var(--selected-color, currentColor);
}
select{vertical-align:top}
<svg height="180" width="180">
<rect x="0" y="0" height="80" width="80" fill="green" style="color:green"/>
<rect x="90" y="0" height="80" width="80" fill="blue" style="color:blue"/>
<rect x="0" y="90" height="80" width="80" fill="red" style="color:red"/>
<rect x="90" y="90" height="80" width="80" fill="black" style="color:black"/>
</svg>
<select id="sel">
<option value="">none</option>
<option>orange</option>
<option>pink</option>
<option>violet</option>
<option>aqua</option>
</select>

Remove the fill style from the SVG and place a default "fallback" color in the CSS for when the variable has not been defined.
rect {
fill: var(--preview-primary, 000);
}
rect {
fill: var(--preview-primary, black);
}
.red {
--preview-primary: red;
}
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect x="10" y="10" height="100" width="100"/>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect class="red" x="10" y="10" height="100" width="100"/>
</svg>

Related

SVG masking not working with transform applied

I'm trying to apply mask to transformed svg element (it's simplified, I'm trying to do it with path, but structure is same). If mask is applied to element outside of transformed group, it works as expected. If I try to do the same inside , element just disappears.
HTML:
<svg width="350pt" height="100pt" >
<defs>
<pattern id="circleFill" patternUnits="userSpaceOnUse" width="5" height="5" >
<circle cx="2" cy="2" r="2" fill="red"></circle>
</pattern>
<mask id="circleMask">
<rect x="0" y="0" width="100%" height="100%" fill="url(#circleFill)" />
</mask>
</defs>
<g transform="translate(0,150) scale(.1,-.1)">
<rect class="holder" x="300" y="300" height="1000" width="1000"/>
<rect class="main t" x="350" y="350" height="400" width="400" />
</g>
<rect x="300" y="50" height="50" width="50" class="main masked o" />
</svg>
CSS:
.holder {
fill: darkgray;
stroke: black;
stroke-width: 2px;
}
.o{ fill: red; }
.t{ fill: purple; }
.masked{ mask: url(#circleMask); }
If I add "masked" class to second rect (with classes "main t"), it just disappears.
The following structure works:
<g class="masked">
<g transform="...">
<rect ... />
</g>
</g>
I can't use it because I have more than 50 elements in image that should have the same transform and only some of them should be masked (and there are 5 different masks).
Here's the fiddle: Fiddle
What I'm doing wrong? Is it possible to mask element inside transformed group?

SVG change cursor from `cursor:wait` into e.g. `cursor:help` after hovering n seconds (loading time for tooltip) without scripting?

There has been a question about changing the cursor using before on this site, but it asked for a solution using Javascript, and it wasn't specific about when to trigger the action to change the cursor.
My question is about SVG and CSS/SMIL without the use of other scripting languages such as Javascript.
When hovering over a certain object, how to change the cursor from cursor:wait into e.g. cursor:help? The change should be triggered after an exact amount of seconds of being hovered over the object.
The use-case is clearly presented in a minimal snippet below.
MWE SNIPPET
#MOUSE_OVER_THESE{
cursor:wait;
}
<svg id="SVG"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="250"
height="175"
viewBox="0 0 250 175">
<text font-size="10" x="10" y="20">
<tspan
x="10" dy="0">Hover over the objects below. Can the cursor</tspan><tspan
x="10" dy="12">change from "cursor:wait" into e.g. "cursor:help"</tspan><tspan
x="10" dy="12.5">after about 1 second, (which will be right</tspan><tspan
x="10" dy="12.5">about when the tooltip appears on certain</tspan><tspan
x="10" dy="12.5">browsers) without using any scripting language?</tspan></text>
<g id="MOUSE_OVER_THESE">
<rect x="50" y="100" width="60" height="50" fill="red">
<title>This is a tooltip.</title>
</rect>
<rect x="150" y="100" width="60" height="50" fill="blue">
<title>This is another tooltip.</title>
</rect>
</g>
</svg>
I can not afford to use scripting languages such as Javascript, so I am wondering if there is a more native SVG with CSS/SMIL approach.
Here is a trick using transition and a hidden element.
UPDATE
The mouse need to be moved slightly in order to see the cursor change
.hide {
transition:1s visibility 1s;
cursor:wait;
}
.hide:hover {
visibility:hidden;
}
#MOUSE_OVER_THESE {
cursor:help;
}
#MOUSE_OVER_THESE:hover + .hide {
display:none;
}
<svg id="SVG"
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="250"
height="175"
viewBox="0 0 250 175">
<text font-size="10" x="10" y="20">
<tspan
x="10" dy="0">Hover over the objects below. Can the cursor</tspan><tspan
x="10" dy="12">change from "cursor:wait" into e.g. "cursor:help"</tspan><tspan
x="10" dy="12.5">after about 1 second, (which will be right</tspan><tspan
x="10" dy="12.5">about when the tooltip appears on certain</tspan><tspan
x="10" dy="12.5">browsers) without using any scripting language?</tspan></text>
<g id="MOUSE_OVER_THESE">
<rect x="50" y="100" width="60" height="50" fill="red">
<title>This is a tooltip.</title>
</rect>
<rect x="150" y="100" width="60" height="50" fill="blue">
<title>This is another tooltip.</title>
</rect>
</g>
<rect class="hide" x="50" y="100" width="160" height="50" fill="transparent"></rect>
</svg>

Center an element of a SVG mask

I'm a newbie in SVG so it's probably an easy question. I'm trying to make an SVG Mask with a simple triangle shape inside a rectangle. What I want to achieve is to get the rectangle responsive with his width but the triangle should
- get a fixed size
- be always at the center of the viewport
You'll understand better with my snippet:
.header-arrow {
height: 70px;
}
svg {
height: inherit;
}
#arrow-down-alpha {
transform: translateX(calc(50vw - 130px/2));
}
<div class="header-arrow">
<svg width="100%">
<defs>
<mask id="myMask" x="0" y="0" width="100%" height="100%">
<rect fill="white" x="0" y="0" width="100%" height="100%" />
<polygon id="arrow-down-alpha" fill="black" x="00" y="0" width="165px" height="100%" points="55.91 37.8 111.81 0 0 0 55.91 37.8" />
</mask>
</defs>
<rect id="base-mask" mask="url(#myMask)" x="0" y="0" width="100%" height="100%" />
</svg>
</div>
It's workning right now in chrome, but the translateX (or translate) is not working in firefox and edge. I've tried to use the transform SVG attribute but it seems that I can't use percentages values.
I'm not realy familiar with the viewbox but I'm not sure it will help in this case.
Thanks anyway for any kind of help !
Here's one way to achieve what you want without relying on new units or calc(). It should be cross-browser compatible also.
How it works:
We wrap the triangle in a nested SVG. We use an SVG because it has an x attribute which can take percentages.
We position this nested SVG at x="50%". It is now centred in the mask (roughly, see next step).
We move the triangle shape so it is centred at x=0. That's so that it is not offset from the centre of the mask.
We set overflow="visible" on the nested SVG so the part of the triangle that is now off the left of the SVG (ie. x < 0) are not clipped.
.header-arrow {
height: 70px;
}
svg {
height: inherit;
}
<div class="header-arrow">
<svg width="100%">
<defs>
<mask id="myMask" x="0" y="0" width="100%" height="100%">
<rect fill="white" x="0" y="0" width="100%" height="100%" />
<svg x="50%" overflow="visible">
<polygon fill="black" points="0 38 56 0 -56 0" />
</svg>
</mask>
</defs>
<rect id="base-mask" mask="url(#myMask)" x="0" y="0" width="100%" height="100%" />
</svg>
</div>

Confusion about the order of CSS transform

All:
When I try CSS transform, something about the order of scale and translate confused me:
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="scale(2) translate(10, 10)"/>
</svg>
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="translate(10, 10) scale(2)"/>
</svg>
These two give different effects. Could anyone give me some explanation about how the CSS transform be processed and rendered?
Thanks
From W3C
x = ""
The x-axis coordinate of the side of the rectangle which has the smaller x-axis coordinate value in the current user coordinate system.
and Mozilla Developer Network:
This attribute indicates an x-axis coordinate in the user coordinate
system. The exact effect of this coordinate depend on each element.
The reason they don't look the same is because scaling the element also scaled the user coordinate system.
I've added two more SVG elements so we can see what it looks like with just the first transform applied to it.
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="scale(2)"/>
</svg>
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="scale(2) translate(10, 10)"/>
</svg>
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="translate(10, 10)"/>
</svg>
<svg>
<rect x="10" y="10" width="20" height="30" style="stroke: #3333cc; fill:none;"/>
<rect x="0" y="0" width="20" height="30" style="stroke: #000000; fill:none;" transform="translate(10, 10) scale(2)"/>
</svg>
In the first one the scale is applied first. Now both the rect and the user coordinate system is 2x as big as the purple rect. So when it is moved to 10,10 that is not the same location as it is for the purple rect.
In the second, the rect is moved first. It has the same coordinate system as the purple rect so it ends up in the exact same place. It is then scaled so it's upper left hand corner stays in the same place.
Every transform function like scale, translate represents a matrix. And matrix concatenation/multiplication is not commutative. So M1 x M2 must not be equal to M2 x M1.
E.g. a scaling matrix (column major order), scale(2, 2):
m11=2.000 m21=0.000 m31=0.000
m12=0.000 m22=2.000 m32=0.000
m13=0.000 m23=0.000 m33=1.000
E.g. a translation matrix, translate(10, 10):
m11=1.000 m21=0.000 m31=10.000
m12=0.000 m22=1.000 m32=10.000
m13=0.000 m23=0.000 m33=1.000
Scale * Translate:
m11=2.000 m21=0.000 m31=20.000
m12=0.000 m22=2.000 m32=20.000
m13=0.000 m23=0.000 m33=1.000
Translate * Scale:
m11=2.000 m21=0.000 m31=10.000
m12=0.000 m22=2.000 m32=10.000
m13=0.000 m23=0.000 m33=1.000

Change in CSS the background-color for the first rect in my #ID

I generate a SVG in a GWT application and I want to modify the first rect background-color inside my SVG in CSS.
Here the code:
<svg id="chart8" width="720" height="350" style="overflow: hidden;">
<defs id="defs">...</defs>
<rect x="0" y="0" width="720" height="350" stroke="none" stroke-width="0" fill="#ffffff">
<g>...</g>
<g><rect .../></g>
<g>...</g>
</svg>
So i want to modify in CSS the fill value only for the first rect element for #chart8.
I tested this but it's not working:
#chart8 rect:first-of-type {
fill: #F1F1F1;*/
}
Have you an idea?
Thanks!

Resources