I have svg paths showing statistics about letters a-z.
I have corresponding divs with the letter itself.
Each div should colorize corresponding svg path when hovered.
Is it possible to use CSS only to define the relation ships?
I mean, not a way where I have 26 definitions in the css, describing relation of every div
logic like
div.a has been hovered, colorize path.a
div.b has been hovered, colorize path.b
...
what I have for now is this.
<body>
<svg height="210" width="400">
<path class="U" d="M 0 300 L 200 0 L 400 400" />
<path class="T" d="M 0 400 L 200 0 L 400 150" />
<!-- ... -->
</svg>
</body>
I am trying to find solution for the divs, hoping it wouldn't need JS
The simple answer to this is that is is only possible if the div contains the path. Something like this for instance:
/* HTML */
<div class="example-div">
<svg class="example-svg" height="210" width="400">
<path class="U" d="M 0 300 L 200 0 L 400 400" />
<path class="T" d="M 0 400 L 200 0 L 400 150" />
</svg>
</div>
/* CSS */
.example-div {
fill: white;
}
.example-div:hover {
fill: black;
}
.example-svg path {
fill: inherit;
}
If the div does not contain the path then as far as CSS is concerned it has no knowledge of it.
Related
I'm trying to to switch a Vue/Vuetify app from using the webfont for Material Design Icons to use SVG icons instead.
When using the webfont, an example of a heading that contains an icon is
<template>
<h1>
<v-icon>mdi-format-list-bulleted</v-icon>
My Heading
</h1>
</template>
The height of the icon automatically matches the heading because the <i> element that is generated for the icon has CSS properties
font-size: inherit;
color: inherit;
After switching to SVG icons, an example of a heading that contains an icon is
<template>
<h1>
<v-icon>{{ iconPath }}</v-icon>
My Heading
</h1>
</template>
<script>
import { mdiFormatListBulleted } from '#mdi/js'
export default {
data: () => ({
iconPath: mdiFormatListBulleted
})
}
</script>
This generates the following markup instead:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img"
aria-hidden="true" class="v-icon__svg">
<!-- path element omitted -->
</svg>
the CSS class has the following properties
.v-icon__svg {
height: 24px;
width: 24px;
fill: currentColor;
}
The fixed icon height of 24px means that the SVG icon doesn't match the heading's height.
Although the <v-icon> component provides a number of props that I can use to change the icon's size, e.g. size, large, small, these will fix the size to a specific value, rather than inheriting the size from the parent element.
Is there a way for an SVG to inherit it's size from it's parent element? More specifically, I want the SVG height and width to be the same as the parent element's height.
Demo
I've created a demo of what happens when you use SVG icons. It takes about 30 seconds for it to load, and you may need to disable 3rd party cookies for it to work.
If you set the height of the SVG to one of the relative units it will match the font-size of the context. You can combine this with vertical-align if necessary.
h1,h2 {
color: navy;
}
.height-ch {
height: 1ch;
fill: currentColor;
}
.height-em {
height: 1em;
fill: currentColor;
}
.height-rem {
height: 1rem;
fill: currentColor;
}
<h1>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img"
aria-hidden="true" class="height-em">
<path d="M 0 0 L 24 0 L 24 24 L 0 24 Z" />
</svg>
Heading em
</h1>
<h2>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img"
aria-hidden="true" class="height-em">
<path d="M 0 0 L 24 0 L 24 24 L 0 24 Z" />
</svg>
Heading em
</h2>
<h2>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img"
aria-hidden="true" class="height-ch">
<path d="M 0 0 L 24 0 L 24 24 L 0 24 Z" />
</svg>
Heading ch
</h2>
<h2>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" role="img"
aria-hidden="true" class="height-rem">
<path d="M 0 0 L 24 0 L 24 24 L 0 24 Z" />
</svg>
Heading rem
</h2>
I have many SVG's on my page, that I imported as (in React):
import { ReactComponent as Logo } from "./../../../images/example.svg";
And them I use them this way:
<div className='someClassName activeClassName?'>
<Logo />
</div>
But all of them are different in their structure.
What do I mean? Some of SVG's are looks like:
<svg>
<path/>
</svg>
some like:
<svg>
<g>
<g>
<path/>
</g>
</g>
</svg>
some looks like:
<svg>
<g>
<circle/>
<triangle/>
</g>
</svg>
And there are millions of types like this.
I have a 'activeClassName' which fill SVG in different color, when it's active, but to make it work with all my SVG's, I have to describe my classname styles kinda like this:
&--active {
svg {
fill: $primaryBlue !important;
path {
fill: $primaryBlue !important;
}
g {
fill: $primaryBlue !important;
g path {
fill: $primaryBlue !important;
}
}
}
}
This looks awful. How can I change, for example, the fill option for all of those SVG's? Please, help me... thanks
As #Robert Longson and #chrwahl pointed out:
removing fill attributes from your svg child elements is recommended.
Not sure, how you could "pre/postprocess" your imported svg component.
In plain js you could easily query your child elements and remove attributes like so:
let svgAsset = document.querySelector(".svgAsset");
// query child elements – maybe includeother svg primitives like circles/rects
let svgChildEls = svgAsset.querySelectorAll("g, path, circle, rect, polygon");
function removeFills(els = svgChildEls) {
els.forEach(function (el, i) {
el.removeAttribute("fill");
});
}
function addElClass(els = svgChildEls) {
els.forEach(function (el, i) {
let nodeName = el.nodeName.toLowerCase();
if(nodeName!='g'){
el.classList.add("svgChild");
}
});
}
function toggleActive(){
svgAsset.classList.toggle('svgActive');
svgAsset.classList.toggle('svgInactive');
}
svg{
display:inline-block;
width:200px;
}
/* inactive */
.svgInactive{
fill: #ccc;
}
.svgActive{
fill: orange;
}
.svgActive
.svgChild{
fill: blue;
}
<p>
<button onclick="toggleActive()">toggle active</button>
<button onclick="removeFills()">Remove fill attributes</button>
<button onclick="addElClass()">Add element Class</button>
</p>
<div class="svgWrp">
<svg class="svgAsset svgInactive" viewBox="0 0 100 100">
<path id="path0" fill="red" d="M0 0 l50 0 l0 50 l-50 0 z" />
<path id="path1" class="hasClass" fill="green" d="M33 33 l50 0 l0 50 l-50 0 z" />
<g fill="purple">
<circle id="" cx="66.666%" cy="33.333%" r="25%" fill="none" stroke="#000" stroke-width="2" />
</g>
</svg>
</div>
In the above example I've also included <g> elements and other shape primitives like polygons:
let svgChildEls = svgAsset.querySelectorAll("g, path, circle, rect, polygon");
You benefit from a lower css specificity – so you don't need nested selectors like
svg g path{ ... }
Manually checking and optimizing your svg source material is always the best approach, since you can't expect graphics from different sources to have a coherent structure.
E.g svgs generated by GUI applications tend to have slightly quirky markup including way to many <g> nesting, unsused or too many transforms (making it hard to get or manipulate x/y offsets) etc.
For some reason, :hover and links are not working in combination with clip-path in Firefox. No problem with Chrome. I need clip-path to work. I know that it works without the attribute. However, it's not an option for me to remove this attribute.
Any idea why?
Simplified example:
<svg xmlns="http://www.w3.org/2000/svg" height="210" width="400">
<style>
path {
fill: blue;
}
path:hover {
fill: red;
}
</style>
<a target="_parent" href="/test">
<path id="triangle" clip-path="url('#triangle-clip')" d="M150 0 L75 200 L225 200 Z">
<title>Triangle</title>
</path>
</a>
<clipPath id="triangle-clip">
<use href="#triangle" />
</clipPath>
</svg>
We need to break the recursion here that makes the whole thing invalid. I.e. in the version in the question the clip-path points to a <use> element that points to a <path> that has a clip-path that points to a <use> that points to a <path>...
Here's one way i.e. apply the clip-path only to the path when it's a descendant of the <a> element. That's ignored by the <use> element because the selector crosses the shadow DOM boundary.
Another way would be to replace the <use> element with a copy of the <path> its pointing to and remove the clip-path from that copy of the path, and so again fix the infinite recursion problem.
<svg xmlns="http://www.w3.org/2000/svg" height="210" width="400">
<style>
path {
fill: blue;
}
path:hover {
fill: red;
}
a > path {
clip-path: url('#triangle-clip');
}
</style>
<a target="_parent" href="/test">
<path id="triangle" d="M150 0 L75 200 L225 200 Z">
<title>Triangle</title>
</path>
</a>
<clipPath id="triangle-clip">
<use href="#triangle" />
</clipPath>
</svg>
In your case clip-path does nothing, so I just removed it. ANd it's important to add #namespace.
#namespace svg url(http://www.w3.org/2000/svg);
svg|a path {
fill: blue;
}
svg|a:hover path {
fill: red;
}
<svg xmlns="http://www.w3.org/2000/svg" height="210" width="400">
<a href="/test">
<path id="triangle" d="M150 0 L75 200 L225 200 Z">
<title>Triangle</title>
</path>
</a>
</svg>
I noticed that if you open developer tools in Firefox you will see that you have a shadow-root (closed) whereas in Chrome you do not. That could possibly be why your :hover Pseudo-class isn't executing.
https://developer.mozilla.org/en-US/docs/Web/API/ShadowRoot/mode
As #Robert Longson has pointed out your initial code has a recursion issue.
Essentially, you are clipping an svg path (triangle) by itself (... that still has to be defined by itself) – resulting in the exact same shape as the 'unclipped' version.
However, since you already used the <use> element, reordering your path and clipping path definitions - avoiding unnessesary recursions – might still simplify your app's code.
<svg xmlns="http://www.w3.org/2000/svg" height="210" width="400">
<clipPath id="triangle-clip">
<path id="triangle-path" d="M150 0 L75 200 L225 200 Z" />
</clipPath>
<style>
.link-content {
fill: blue;
clip-path: url('#triangle-clip');
}
a:hover .link-content{
fill: red;
}
</style>
<a class="link" target="_parent" href="/test">
<title>Triangle</title>
<use class="link-content" href="#triangle-path" />
</a>
</svg>
I'd like to have two paths in my svg with classes so i can toggle opacity when I hover the svg with my mouse.
The answere to this question is easy to find, but anyway: yes it is. See the Global Attributes in https://developer.mozilla.org/en-US/docs/Web/SVG/Element/path
.shape {
opacity: .5;
}
<svg width="300px" height="300px" viewBox="0 0 500 500" xmlns="http://www.w3.org/2000/svg">
<path class="shape" d="M 100 100 L 300 100 L 200 300 z" fill="orange" stroke="black" stroke-width="5" />
</svg>
Yes, you would use the stroke-opacity option.
I'm attempting create a SVG that expands with the amount of text I enter into it. I have three bars that I'm attempting to wrap with an outline, but the bars aren't melding into the outline, I'm getting a raised edge:
I have create a fiddle with my SVG code, please let me know how I can smooth those borders out. Also, if anyone has a better idea to how to create a SVG button that scales with text, please don't hesitate to let me know!
body { background-color: #13171a; }
g{
/*g element expands with text child element, so we'll apply the outline here */
outline: 1px solid #ffdb00;
}
g text { fill: #fff }
<svg
xmlns="http://www.w3.org/2000/svg"
version="1.1"
id="svg2"
viewBox="0 0 744 1052"
height="297mm"
width="210mm">
<g
id="layer1">
<path
id="path4150"
d="m 70,77 0,45"
style="fill:none;fill-rule:evenodd;stroke:#ffdb00;stroke-width:6;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1" />
<path
id="path4152"
d="m 85,77 0,45"
style="fill:none;fill-rule:evenodd;stroke:#ffdb00;stroke-width:6;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1;" />
<path
id="path4150-6"
d="m 58,77 0,45"
style="fill:none;fill-rule:evenodd;stroke:#ffdb00;stroke-width:6;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:0;stroke-dasharray:none;stroke-opacity:1" />
<text
id="text4169"
y="113.27509"
x="101.77287"
style="font-style:normal;font-weight:normal;font-size:40px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
xml:space="preserve"><tspan
y="113.27509"
x="101.77287"
id="tspan4171">TEST TEXT</tspan></text>
</g>
</svg>