How to apply :hover on SVG inner <rect> element? - css

I have an SVG figure and want to apply :hover property on one <rect> element inside another . But it doesn't work:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" id="svg">
<style type="text/css"><![CDATA[
#rec1 {
fill:black; // outer element
}
#rec2 {
fill:white; // inner element
display:none; // and element not visible
}
#rec1:hover #rec2 {
display:block; // when hover outer lets inner become visible
// but it doesn't work
}
#rec1:hover {
fill:red; // strange but this hover works
}
]]></style>
<g id="g">
<rect id="rec1" x="0" y="0" width="200" height="50" />
<rect id="rec2" x="100" y="20" width="20" height="20" />
</g>
</svg>
What is the right way to apply hover in such a case?
UPD: one solution found.
First it's not one <rect> element in another. They are siblings. And as far as such kind of styles apply #rec1:hover #rec2 only possible with nested elements, it won't work here. So I applied style on the most outer <g> element:
#g:hover #rec2 {
display:block; // it works just fine
}
But are there any more ways to solve this problem?

Just use:
#rec1:hover + #rec2 {
display: block;
}
The + selector is used for selecting siblings (children of the same element -- g in this case), which is the case here.
Hope that helped!

Those rectangles are not nested as far as CSS is concerned.
This style #rec1:hover #rec2 would only apply if #rec2 was nested inside the #rec1 elements and the #rec1 element was being hovered.
Just apply the style as #rec2:hover

Related

Vue JS "scope" style does not work with SVG inner elements

I have a button which contains an SVG icon. I am unable to change the color inner elements of the SVG when the stylesheet is "scope"
Component code:
<template>
<a class="my-btn">
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" d="..." fill="black" fill-opacity="0.54"/>
</svg>
<span>
My Button Label
</span>
</a>
</template>
<style src="#/assets/css/my-custome-button.css"scope></style>
CSS code (#/assets/css/my-custome-button.css)
a.my-btn:hover svg path {
fill: red !important;
fill-opacity: 1;
}
Issue
Because I am using the "scope" attribute in the tag, Vue automatically assigns an attribute to the matching elements and rewrites the CSS accordingly. It appears that Vue cannot access the SVG inner elements. The final CSS looks something like:
a.my-btn svg path[data-v-64222e86] {
fill: var(--danger) !important;
fill-opacity: .9;
}
but the output SVG does not include the "data-v-64222e86" attribute.
Removing the "scoped" property will address the issue, but the non-scoped CSS may affect other elements in the page (unintended result)
Any suggestion on how to have "scoped" CSS code that can control the style of SVG elements?
I solved this by using the :deep (shorthand for ::v-deep) selector:
a.my-btn:hover svg :deep(path) {
fill: red;
fill-opacity: 1;
}
More info here: https://github.com/vuejs/rfcs/blob/master/active-rfcs/0023-scoped-styles-changes.md

What is the compatibility of CSS3 pseudoclasses with SVG renderable elements?

I have been learning SVG and came across this informative article. The author states that
Most CSS selectors can be used to select SVG elements. In addition to the general type, class and ID selectors, SVGs can be styled using CSS2’s dynamic pseudo-classes (:hover, :active and :focus) and pseudo-classes (:first-child, :visited, :link and :lang. The remaining CSS2 pseudo-classes, including those having to do with generated content (such as ::before and ::after), are not part of the SVG language definition and, hence, have no effect on the style of SVGs.
This author has many articles on the web and appears very knowledgeable. However, the statement "the remaining CSS2 pseudo-classes....have no effect on the style of SVGs" makes one wonder about CSS3 pseudoclasses. Take this example I generated on Codepen (FF as browser).
<svg width="220" height="220" xmlns="http://www.w3.org/2000/svg">
<rect x="10" y="10" width="100" height="100" />
<rect x="110" y="110" width="100" height="100" />
</svg>
<style>
svg { border: 3px dashed #999 }
svg > rect:hover { fill: green }
rect:nth-child(even) { fill:red }
</style>
The CSS3 :nth-child pseudoclass works perfectly fine here (fills the 2nd rectangle red). Another example: substitute the :nth-child rule above with another CSS3 pseudoclass selector, a :not rule (all else remains the same):
rect:not([x="110"]) { fill:red } // fills the 1st rectangle red
I have found this reference but it does not help me.
What is the compatibility of CSS3 pseudoclasses with SVG elements?
Note: I am assuming these pseudoclass rules would only apply to SVG renderable elements.
The following are the full list of CSS3 Pseudo-class Selectors as defined in Selectors Level 3 Editor's Draft, where the ones with bolded links ones are compatible with SVG:
:target, selects elements that are targeted via fragment identifiers
:root, selects the root of the document, which is the html tag, with higher specificity
:nth-child(an+b), selects element that has an+b-1 siblings before it in the document tree
:nth-of-type(an+b), selects element that has an+b-1 siblings before it with the same element name in the document tree
:nth-last-of-type(an+b), selects element that has an+b-1 siblings after it with the same element name in the document tree
:last-child, selects any element that is the last child of its parent
:first-of-type, selects the first sibling of its type of the parent's children
:last-of-type, selects the last sibling of its type of the parent's children
:only-child, selects element that has a parent element that has no other children
:only-of-type, selects element that has a parent element that has no other children with the same element name
:empty, selects element that has no children at all
:not(X), selects element that is not selected by selector X, which is a simple selector
These were defined but without semantic meaning in the draft:
:enabled
:disabled
:checked
:indeterminate
The above are given meaning in the HTML5 Specification, along with a few other definitions for other pseudo-classes.
In summary, it seems like all CSS3 pseudo-classes work except for :root. You can read more at the MDN documentation for pseudo-classes. I could not find a credible resource describing the compatibility, so this was all determined through testing. There are no other pseudo-classes or pseudo-elements defined in CSS3.
Here's a pen that demonstrates the application of the full list of CSS3 Pseudo-class Selectors (outlined by #andrewli) on SVGs by applying fill or stroke attributes to SVG renderable elements.
I defined the different SVG elements in two different groups (two parent elements, shown in the pen as two columns) in order to fit in all the pseudo-classes that select different kinds of children:
<svg width="450" height="300">
<g transform="translate(5,5)">
<rect x="0" y="0" width="25" height="25" />
<rect x="0" y="40" width="25" height="25" />
<rect x="0" y="80" width="25" height="25" />
<circle cx="15" cy="132" r="13.5"/>
<circle cx="15" cy="170" r="13.5"/>
<polygon points="2,200 28,200 14,225"/>
<rect x="0" y="240" width="25" height="25" />
</g>
<g transform="translate(5,5)">
<rect x="220" y="0" width="25" height="25" />
</g>
<g transform="translate(5,5)" font-family="Verdana" font-size="16" fill="#444">
<text x="40" y="20" >:root</text>
<text x="40" y="60">:nth-child(2)</text>
<text x="40" y="100">:nth-of-type(3)</text>
<text x="40" y="140">:first-of-type</text>
<text x="40" y="180">:nth-last-of-type(1)</text>
<text x="40" y="220">:only-of-type, :last-of-type</text>
<text x="40" y="260">:nth-last-child(1), :last-child</text>
<text x="260" y="20">:only-child, :last-child</text>
</g>
</svg>
Interesting findings:
:root does appear to work. As expected, it gets applied to all elements, including svg elements, in the document.
Since :empty gets applied to all void svg elements, (<rect>, <circle>, <ellipse>, <polygon>, etc), it's a handy selector for targeting SVG shape elements.

SVG Fill not being applied in FireFox

I can't seem to figure out why Firefox is using the default svg fill color instead of the class's fill.
Here are the 3 fills when viewing the FF inspector:
SVG is being inserted via
<svg class="icon">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#menu-bag"></use>
</svg>
It should be showing the .skip-link .icon fill of white (#fff) but it's actually using the SVG fill of #002649; If i change .skip-link .icon to .skip-link svg then it works fine. Why can I not use a class and instead but explicitly state the element??
Am I missing something obvious about how Firefox fills an SVG? This CSS works fine in other browsers.
If the behavior was unique to Firefox prior to version 56, it was because #menu-bag refers to a <symbol> element.
The specs say that a re-used <symbol> should be implemented as if it were replaced by a nested <svg>. Firefox used to treat this literally in their shadow DOM. The shadow DOM isn't visible in your DOM inspector, but it is subject to CSS selectors.
Which means that this code:
<a href="#" class="skip-link">
<svg class="icon">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#menu-bag"></use>
</svg>
</a>
WAs implemented like this:
<a href="#" class="skip-link">
<svg class="icon">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#menu-bag">
<!--Start of shadow DOM boundary-->
<svg><!-- replacement for <symbol> -->
<!-- graphics content -->
</svg>
<!--End of shadow DOM boundary-->
</use>
</svg>
</a>
The svg.icon matches your .skip-link .icon rule (and as Kyle Mitt points out, that rule will always take precedence over your a:hover svg rule). This value is also inherited by the <use> element.
However, the shadow-DOM <svg> doesn't get the inherited value, because it is styled directly with the svg rule. When you change your selector to .skip-link svg, or when you trigger the a:hover svg rule, then the hidden inner element gets the style directly applied because that SVG is also a descendent of the link.
As Robert Longson noted in the comments, this is not how it is supposed to work. It's a side effect of the way that Firefox implemented <use> elements as complete cloned DOM trees, which just happened to be hidden from your DOM inspector.
Here's a "working" example of your original problem. Which is to say, on Chrome, Safari, Opera, Firefox 56+ or IE you will see a green circle that isn't altered when you hover it, but on Firefox prior to version 56 you will see a blue circle that turns red on hover/focus.
svg {
fill: navy;
}
a:hover svg, a:focus svg {
fill: red;
}
.skip-link .icon {
fill: green;
}
.icon {
height: 50;
width: 50;
}
<a href="#" class="skip-link">
<svg class="icon">
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#menu-bag" />
</svg>
</a>
<svg height="0" width="0">
<symbol id="menu-bag" viewBox="-10 -10 20 20">
<circle r="10" />
</symbol>
</svg>
So what to do you if you need to support old versions of Firefox? You have two options, one of which you've already figured out by trial and error:
Avoid setting default styles using the svg tag selector, and rely on normal style inheritance from the <use> element.
Use selectors that intentionally select the shadow-<svg> to cancel out the defaults, while also making sure that they have the intended effect on other browsers.
One option would be to use a rule like the following, which would maintain the specificity of your original rule for other browsers:
.skip-link .icon, .skip-link .icon use>svg {
fill: green;
}
The use>svg selector will never match anything except with the Firefox bug, so it is safe to use without side effects. (Originally, I'd just suggested adding svg to the end of the selector, but that could be problematic in certain situations.)
A more universal option based on the answer #AmeliaBR provided, is to simply do something along the lines of:
svg use svg {
fill: inherit;
}
which will make the shadow element inherit the fill color.
Robert is correct that <use> is not always applied consistently. Certainly when you use an SVG as an image, it doesn't know how to apply any of the CSS rules you've added to your page.
But there are a lot of other things here as well that could decide the element's style so an example might be helpful.
Here's a stack snippet to center our discussion.
svg {
fill: blue;
}
a:hover svg {
fill: red;
}
.skip-link .icon {
fill: purple;
}
.green {
fill: green;
}
<a href="#" class="skip-link">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="icon" >
<def>
<text id="text" >use xlink</text>
<text id="over" class="green">use xlink override</text>
</def>
<text x="5" y="15" >Plain</text>
<use x="5" y="30" xlink:href="#text" />
<use x="5" y="50" xlink:href="#over" />
<text x="5" y="65" class="green" >class="green"</text>
<text x="5" y="80" fill="orange" >fill="orange"</text>
</svg>
</a>
Specificity
The SVG element itself is being styled with several conflicting rules. What determines which rule wins has to do with [specificity and order]. In this case, the SVG element itself will end up purple. The hover anchor rule, for example, will never show up because it is less specific than .skip-link .icon
Inheritance
Some properties allow for inheritance from their parents, but only when not specified themselves. Any specifications will override the inherited value. If the question is, my <svg> element has a certain style, why isn't it being applied to all child elements equally, the answer is simple. It's perfectly fine for child elements to specify their own value and override the inherited one.
<text x="5" y="65" style="fill:green;" >class="green"</text>
<text x="5" y="80" fill="orange" >fill="orange"</text>
Use & Xlink
The tricky part becomes what happens when use is involved. In this case, it can be hard to trace the actual styles being applied. Use will create an inline representation of the element identified by the xlink attribute, but you cannot access this element directly. Therefore, selecting use in the developer tools will only reveal the styles applied to the parent of the element. The element itself may override the inherited properties and we'd have no way of observing it in the dev panel.
Here, for example, the style applied to use is inherited from the parent. In the developer tools, it appears that the winning rule is purple, but this is only because it hasn't taken into consideration the element being pulled in. This is a soft value that can be overridden if the element specifies any value.
But the full set of selectors for the inlined text would actually look like this:
Specific Situation
One thing I'd suggest in the future is providing runnable code that other people can use to easily reproduce the issue as it saves a lot of extra debugging time. However, here's what I suspect is happening with your exact situation:
svg {
fill: #002649;
}
a:hover svg {
fill: #8A8B8C;
}
.skip-link .icon {
fill: #FFF;
}
<a href="#" class="skip-link">
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
class="icon" >
<def>
<svg xmlns="http://www.w3.org/2000/svg" id="menu-bag">
<rect height="100" width="100" />
</svg>
</def>
<use xlink:href="#menu-bag" />
</svg>
</a>
Set your default svg fill color on the body or html tag and it will be inherited as a default, but you can easily override it using just a class.
body {
fill: black;
}
.green {
fill: green;
}
.red {
fill: red;
}
Now just use the color class anywhere to change the fill color. Add the color class to the svg, or to a span or other element wrapping the svg. Works in Firefox too.
<a href="#" class="skip-link green">
<svg>
<use xmlns:xlink="http://www.w3.org/1999/xlink" xlink:href="#menu-bag" />
</svg>
</a>
<svg height="0" width="0">
<symbol id="menu-bag" viewBox="-10 -10 20 20">
<circle r="10" />
</symbol>
</svg>
My case is not exactly the same, but I share it anyway.
I´m using svg as a background image, like the example below (googled for it, don´t remember where). And in Firefox had problems with the "fill" color.
As the fill value, I had to write it in RGB mode and worked properly (fill:rgb(237, 237, 237);).
If I wrote in in HEX (fill:#ededed;), it wouldn´t render.
If I wrote for example "fill: blue;" it would also show properly.
.a-class {
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 100 10' preserveAspectRatio='none' height='130' style='background:var(--main-lt-green); fill:rgb(237, 237, 237);'><polygon points='100 0 100 10 0 10'></polygon></svg>");
background-repeat: no-repeat;
background-size: 100% 100px;
background-position-y: top;
margin-top: -100px;
padding-top: 100px;
}
what fixed it for me was adding the following css globally:
svg, symbol, defs {
fill: inherit;
}
then you can set your svg's fill and it will apply.

In SVG is there a way to apply a different style sheet (CSS) to each <use> instance

I am trying to create an SVG document containing groups of multiple elements with a variety of styles. I want to reuse these groups, but change out the color scheme with each use.
It occurs to me that I could give each element in the reusable group a different #class and apply a different style sheet (CSS) to each element. Now I just need to figure out if this is possible with the current specifications.
Here is an SVG that illustrates reuse of an element with styling.
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="744.09448819"
height="1052.3622047"
id="svg2"
version="1.1"
inkscape:version="0.48.4 r9939"
sodipodi:docname="blocks-stackoverflow.svg">
<style type="text/css">
.one {
fill:#f00;
}
.two {
fill:#44f;
}
</style>
<defs
id="defs4">
<g id="bacon"> <rect
class="one"
id="rect3011"
width="31.428572"
height="51.42857"
x="108.57143"
y="209.50504" />
<rect
class="two"
id="rect3013"
width="80"
height="40"
x="120"
y="249.50504" />
</g>
</defs>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1">
<use xlink:href="#bacon" x="0" y="0"/>
<use xlink:href="#bacon" x="100" y="200"/>
</g>
</svg>
What the example does NOT accomplish is applying a different style sheet to the second . Is there a way to apply a different style sheet to each of a group? For example, how can I make the second pair of rectangles yellow and green instead of red and blue? Or maybe they're stroked instead of filled.
You can't set styles on elements referenced by a specific <use> element. You can style the original elements, but that affects all references to them.
However, you can change the default styles used when drawing the referenced content by setting styles directly on the <use> element itself. These styles will be inherited by any graphics content that doesn't have other styles set on it directly.
A demo I put together recently showing style possibilities on referenced icons.
To have two shapes within your referenced content have different fill colours that you can specify, you can have one of your rectangles use the default fill colour, and one of your rectangles use the currentColor keyword for fill. Then you need to specify both fill and color styles on each <use> element (or its ancestor), otherwise you'll get the system default fill and color, which are both black.
Example using your code.
<style>
.one {
fill:inherit;
}
.two {
fill:currentColor;
}
</style>
<use xlink:href="#bacon" x="0" y="0"
style="fill:red;color:blue" />
<use xlink:href="#bacon" x="100" y="200"
style="fill:green;color:yellow" />
Based on Apply style sheet to only a specific element trait the style sheet must include some #id syntax like this:
<style type="text/css">
.one {
fill:#f00;
}
.two {
fill:#44f;
}
#pencil .one {
fill:#0f0;
}
#pencil .two {
stroke:#ff0;
}
</style>
referencing the #id of the second
<use xlink:href="#bacon" x="100" y="200" id="pencil"/>
Although the second box will have both the fill from .two and the stroke from #pencil.two , so in my case I'd probably want to use the id qualifier on every clause of the style sheet to avoid them being combined.

CSS 3 hide element when :target is set to something else - show when it is blank?

Just trying to see if there is a pure CSS3 solution to this idea I came up with.
Basically, if the user navigates to http://example.com, I'd like to show an element on my page, say <section id="main" />. If the visit http://example.com/#pics, though, I'd like to show a photo gallery.
I know that this can be done with JQuery, but I'm trying to do as much as I can with CSS3, this is just an experimental site, so browser support isn't a priority, but I am interested in fallbacks.
This answer assumes you are selecting elements with the class "hideme". Adjust as needed for your case.
Any kind of "if not" can be done with the :not() psuedo-class. In this case:
.hideme:not(:target) {display: none;}
However, browser support for this is very low, so instead you should do something like this:
selector {do something}
selector:target {cancel the something}
Now, this is a little tricky with display because different elements have different defaults, but it visibility is an option for you:
.hideme {visibility: hidden;}
.hideme:target {visibility: visible;}
Alternatively, or additionally, you might be able to use position:absolute and position:static to remove the selected item from the flow of the document.
Have a look at the :target selector and the :not selector
you can do something along the lines of
div:not(:target){
display:none
}
#main:target {
display:block;
}
#pics:target {
display:block;
}
Ref:
http://www.w3.org/TR/selectors/ section 6.6.7 is the :not selector (no target from their file)
and
http://www.w3.org/TR/selectors/#target-pseudo
You can wrap your code in multiple containers, so you can use the target pseudo-class to select every element you need.
Example
<svg xmlns="http://www.w3.org/2000/svg" width="200" height="100" version="1.1">
<style>
/* hide elements by default */
.hide {
display: none;
}
/* show the elements by target */
#one:target .one,
#two:target .two {
display: block;
}
/* BONUS: using this technique, you can select other elements */
#one:target .default,
#two:target .default {
display: none;
}
/* some colors... */
.one {
fill: red;
}
.two {
fill: yellow;
}
</style>
<g id="one">
<g id="two">
<rect width="200" height="100" stroke="black" stroke-width="2" fill="green"/>
<circle class="hide one" cx="50" cy="50" r="20"></circle>
<circle class="hide two" cx="100" cy="50" r="20"></circle>
<circle class="default" cx="150" cy="50" r="20"></circle>
</g>
</g>
</svg>
Maybe it's not an elegant solution, but it works.

Resources