My goal is to use SVG in repeated background, but I need to be able to change its fill color in CSS for usage in the different parts of the website.
As far as my understanding of the SVG, it can't be laoded as an external file (background:url(image.svg)), because then I am not able to change the CSS property like fill proeprty of it. On the other hand, SVG can't be inlined in HTML (<svg>...</svg>) as then I am not able to use/reference it in background property.
Please correct me if I am wrong, or do you have any solution for this?
As far as my understanding of the SVG, it can't be laoded as an
external file (background:url(image.svg)), because then I am not able
to change the CSS property like fill proeprty of it.
Consider using CSS filters to change svg coloration instead of fill
In the example below the svg file is loaded as an external file:
background-image:url(https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/android.svg);
and its color will be changed on hover using a combination of css filters
You can get the desired combination of filters for a specific color using the utility
https://codepen.io/sosuke/pen/Pjoqqp
Hover to change color
.android {
display:inline-block;
width:96px;
height:105px;
background-image:url(https://dev.w3.org/SVG/tools/svgweb/samples/svg-files/android.svg);
}
.android.blue:hover {
filter: invert(8%) sepia(99%) saturate(7454%) hue-rotate(248deg) brightness(100%) contrast(143%);
}
.android.red:hover {
filter: invert(25%) sepia(100%) saturate(7420%) hue-rotate(6deg) brightness(98%) contrast(122%);
}
.android.gold:hover {
filter: invert(50%) sepia(31%) saturate(3357%) hue-rotate(113deg) brightness(99%) contrast(101%);
}
<div class="android blue">
</div>
<div class="android red">
</div>
<div class="android gold">
</div>
Ok so it's not super simple but, it can be done with some work to the SVG itself. if you look at the simple example you will see inside the tag under the you can essentially make a pattern of any SVG elements like a circle,rect, or a path.
So make the pattern as
<pattern id="dots" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse">
make sure the width and height are the same as your SVG or it will cut it off
then outside of the defs you can make a rect object with the fill of your pattern fill="url(#dots)" this must use the id of the pattern
Simple example
.pattern {
background-color: #2e4057;
height: 100vh;
}
<div class="pattern">
<svg width="100%" height="100%">
<defs>
<pattern id="dots" x="0" y="0" width="100" height="100" patternUnits="userSpaceOnUse">
<circle fill="#bee9e8" cx="50" cy="50" r="25">
</circle>
</pattern>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="url(#dots)"></rect>
</div>
More advanced SVG
.pattern {
background-color: #2e4057;
height: 100vh;
}
<div class="pattern">
<svg width="100%" height="100%">
<defs>
<pattern id="duck" x="0" y="0" width="209" height="209" patternUnits="userSpaceOnUse">
<g>
<path fill="red" d="M105.572,101.811c9.889-6.368,27.417-16.464,28.106-42.166c0.536-20.278-9.971-49.506-49.155-50.878
C53.041,7.659,39.9,28.251,36.071,46.739l-0.928-0.126c-1.932,0-3.438,1.28-5.34,2.889c-2.084,1.784-4.683,3.979-7.792,4.308
c-3.573,0.361-8.111-1.206-11.698-2.449c-4.193-1.431-6.624-2.047-8.265-0.759c-1.503,1.163-2.178,3.262-2.028,6.226
c0.331,6.326,4.971,18.917,16.016,25.778c7.67,4.765,16.248,5.482,20.681,5.482c0.006,0,0.006,0,0.006,0
c2.37,0,4.945-0.239,7.388-0.726c2.741,4.218,5.228,7.476,6.037,9.752c2.054,5.851-27.848,25.087-27.848,55.01
c0,29.916,22.013,48.475,56.727,48.475h55.004c30.593,0,70.814-29.908,75.291-92.48C180.781,132.191,167.028,98.15,105.572,101.811
z M18.941,77.945C8.775,71.617,4.992,58.922,5.294,55.525c0.897,0.24,2.194,0.689,3.228,1.042
c4.105,1.415,9.416,3.228,14.068,2.707c4.799-0.499,8.253-3.437,10.778-5.574c0.607-0.509,1.393-1.176,1.872-1.491
c0.87,0.315,0.962,0.693,1.176,3.14c0.196,2.26,0.473,5.37,2.362,9.006c1.437,2.761,3.581,5.705,5.646,8.542
c1.701,2.336,4.278,5.871,4.535,6.404c-0.445,1.184-4.907,3.282-12.229,3.282C30.177,82.591,23.69,80.904,18.941,77.945z
M56.86,49.368c0-4.938,4.001-8.943,8.931-8.943c4.941,0,8.942,4.005,8.942,8.943c0,4.931-4.001,8.942-8.942,8.942
C60.854,58.311,56.86,54.299,56.86,49.368z M149.159,155.398l-20.63,11.169l13.408,9.293c0,0-49.854,15.813-72.198-6.885
c-11.006-11.16-13.06-28.533,4.124-38.84c17.184-10.312,84.609,3.943,84.609,3.943L134.295,147.8L149.159,155.398z"/>
</g>
</pattern>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="url(#duck)"></rect>
</svg>
hope this helps
a more complete tutorial here
I have a SVG that needs to have a fixed height so its not super big when the width is 2000+ pixels (widescreens...)
The clipping mask should always be visible and the background image should be sliced and not be stretchend, i tried several things but nothing seems to work.
This is what i have now:
https://codepen.io/bucky208/pen/OEqbPp
div {
width: 100%;
}
<div>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 1024 1381.5" preserveAspectRatio="none" style="display: block; position: absolute; width: 100%;height: 400px;">
<g id="clipgroup">
<defs>
<polygon id="mask" points="0,572.1 0,1381.3 1024,1040.5 1024,337.6 0,0"/>
</defs>
<clipPath id="mask_1_">
<use xlink:href="#mask" style="overflow:visible;"/>
</clipPath>
<g style="clip-path:url(#mask_1_);">
<image style="overflow:visible;" width="331" height="444" id="theimage" xlink:href="https://image.ibb.co/ipbNkJ/56_E0406_E5_C8_EF897.jpg" transform="matrix(3.1119 0 0 3.1111 -3.0528 -2.604167e-04)"></image>
</g>
</g>
</svg>
</div>
Do i need another wrapper around everything? How do i restore the image ratio?
Kind regards and a big thank you for everyone trying to help.
In order to get image fills to fill their container but preserve the original aspect ratio, a filter combined with objectBoundingBox sizing and preserveAspectRatio is your friend. The following code does what I think you want:
svg {
background: red;
}
#svgcont {
position: absolute;
width: 100%;
}
<div id="svgcont">
<svg x="0" y="0" width="100%" height="800px">
<defs>
<filter id="image-fill-nostretch" primitiveUnits="objectBoundingBox">
<feImage x="0" y="0" width="1" height="1" id="theimage" xlink:href="https://image.ibb.co/ipbNkJ/56_E0406_E5_C8_EF897.jpg" preserveAspectRatio="xMidYMid slice"/>
<feComposite operator="in" x1="SourceGraphic"/>
</filter>
<clipPath id="mask_1_" clipPathUnits="objectBoundingBox">
<polygon id="mask" points="0,0.5 0,1 1,0.75 1,0.25 0,0"/>
</clipPath>
</defs>
<g clip-path="url(#mask_1_)">
<rect width="100%"height="100%" x="0%" y="0%" filter="url(#image-fill-nostretch)"/>
</g>
</svg>
</div>
I wanted to position text in the bottom left of an SVG element, and managed to achieve that using groups and transforms, but noticed that it won't update if i resize my window vertically, and also it won't apply on page load, but if i add the transform properties through developer console. they work perfectly, until i resize the window.
Is it some kind of a bug or i am doing something wrong? Is there another way to position text inside SVG element relative to bottom left and keep it responsive to window resize? I am using chrome 61 by the way.
here's a link to jsfiddle too:
https://jsfiddle.net/eow1c4o4/
*,*:before,*:after {
box-sizing: border-box;
}
body, html {
padding: 0;
margin: 0;
height: 300vh
}
tspan {
font-weight: 900;
font-size: 50px;
dominant-baseline: hanging;
}
<svg width="100%" height="100vh">
<defs>
<mask id="mask" maskUnits="userSpaceOnUse">
<rect width="100%" height="100%" fill="white"/>
<g style="transform: translate(0, 100%);">
<rect x="0" y="0" width="100%" height="100%" fill="white"></rect>
<g style="transform: translate(50px, -100%);">
<text x="0" y="0" text-anchor="start" font-family="Roboto">
<tspan x="0" dy="0">SOME</tspan>
<tspan x="0" dy="0.81em">ABSOLUTELY</tspan>
<tspan x="0" dy="0.81em">RANDOM TEXT</tspan>
</text>
</g>
</g>
</mask>
</defs>
<rect width="100%" height="100%" fill="#dedede" fill-rule="evenodd" mask="url(#mask)"></rect>
</svg>
Transforms in SVGs don't allow percentages. But geometric attributes do allow them. So the simplest way to do this is with a <use> element.
*,*:before,*:after {
box-sizing: border-box;
}
body, html {
padding: 0;
margin: 0;
height: 300vh
}
tspan {
font-weight: 900;
font-size: 50px;
}
<svg width="100%" height="100vh">
<defs>
<text id="mytext" x="0" y="-80" text-anchor="start" font-family="Roboto">
<tspan x="0" dy="0">SOME</tspan>
<tspan x="0" dy="0.81em">ABSOLUTELY</tspan>
<tspan x="0" dy="0.81em">RANDOM TEXT</tspan>
</text>
<mask id="mask" maskUnits="userSpaceOnUse">
<rect width="100%" height="100%" fill="white"/>
<use xlink:href="#mytext" x="5%" y="95%" fill="black"/>
</mask>
</defs>
<rect width="100%" height="100%" fill="#dedede" fill-rule="evenodd" mask="url(#mask)"></rect>
</svg>
The way we get the text to sit at the bottom, is to initially position it just off the top of the screen. Then we use a <use> element to reposition it at y="95%". That's just a little less than 100% so it ends up just above the bottom.
https://jsfiddle.net/eow1c4o4/1/
I want to implement a tooltip. The idea in its most simplicity is to show a rectangular, when hovered on a circle. How can I achieve that?
What I do not want is to show the rectangular when not hovered precisely on the circle.
Please check the example: https://codepen.io/EminDurak/pen/JXOVqv
SVG:
<svg width="800px" height="300px" viewBox="0 0 800 300" xmlns="http://www.w3.org/2000/svg">
<g x="282.7416666666667" y="252.6" width="3" height="3" class="tooltip-container">
<circle cx="200" cy="120" r="20" value="12" class="tooltip-circle"></circle>
<rect x="50" y="30" width="300" height="80" rx="4" ry="4" r="5" value="12" class="tooltip-box"></rect>
<text x="100" y="80" style="fill:black">Shouldn't appear when hovered here</text>
<text x="150" y="160" style="fill:black">Hover the circle</text>
</g>
</svg>
And the CSS (SCSS):
.tooltip-box {
fill: purple;
opacity: 0;
stroke-width: 1px;
}
.tooltip-container {
&:hover > * {
opacity: 1 !important;
}
}
So it turns out that the best solution to this problem is using display:none, instead of opacity:0. At the time of writing the question I had not checked which css selectors work for SVG; didn't think display would be one. But it is.
So this code below works:
<g x="282.7416666666667" y="252.6" width="3" height="3" class="tooltip-container">
<circle cx="200" cy="120" r="20" value="12" class="tooltip-circle"></circle>
<rect x="50" y="30" width="300" height="80" rx="4" ry="4" r="5" value="12" class="tooltip-box"></rect>
</g>
and css:
.tooltip-box {
fill: purple;
opacity: 0;
stroke-width: 1px;
}
.tooltip-container:hover > .tooltip-box {
opacity: 1;
}
Note: The reason I placed the <Text> element was just to indicate what I was trying to do. The main point is that when an svg element is set to display:none, the container <g> element's size is computed accordingly, so as if the svg element is not rendered at all. Then one can set the element to display: block on hovered to container <g> and consequentially be able to exercise, let's say, a tooltip function.
I'm trying to get svg image to scale nicely (http://coub.com/view/5tbis) with sibling div content so that they'd look like one thing. I managed to get it working in fireforx but not in chrome and ie, all latest versions. Here is my code:
<!DOCTYPE html>
<html>
<body>
<div style="display: flex; height: 10em;">
<div style="height: 100%">
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" style="width: 100%; height: 100%; " viewBox="0 0 400 400">
<rect x="0" y="0" width="400" height="200" fill="yellow" stroke="black" />
<rect x="0" y="200" width="400" height="200" fill="green" stroke="black" />
<circle cx="200" cy="200" r="100" stroke="black" stroke-width="3" fill="red" />
</svg>
</div>
<div style="display: flex; flex-direction: column; width: 100%">
<div style="flex: 1; background-color: green"> </div>
<div style="flex: 1; background-color: yellow"> </div>
</div>
</div>
</body>
</html>
In IE and Chrome there is a blank space around svg and in Chome, svg proportions does not always match divs' ones.
Any help is highly appreciated.
UPDATE: ok, preserveAspectRatio="none" fixes the problem for ie and chrome, but I still don't understand why do they behave in this way? The wrapper div do not have width, why svg then doesn't change it according its own width?
UPDATE: preserveAspectRatio="xMidYMid slice" makes it even better, but only for ff and ie