Viewbox placement in referencing SVG symbols and CSS dimensions - css

I edited my initial cry of despair into something more to the technical point, in order to turn it into a Q&A.
I'm using SVG symbols that I reference in the document with use elements. I'm styling these with CSS. I don't want to set both height and width in the CSS, I want to set only one of them with the other one scaling accordingly.
I do set a viewBox attribute on the symbol. But the graphic does not scale correctly.
<!DOCTYPE html>
<html>
<head>
<title>SVG Symbols</title>
<style>
body { margin: 20px; }
.svg-large { width: 500px; fill: yellow;}
</style>
</head>
<body>
<svg style="display:none;">
<symbol id="scary-smiley" viewBox="0 0 20 20">
<circle cx="10" cy="10" r="9.5" stroke-width="1"
stroke="black" />
<circle cx="6" cy="7" r="1.5" fill="black"/>
<circle cx="14" cy="7" r="1.5" fill="black"/>
<image xlink:href="https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Teeth_by_David_Shankbone.jpg/320px-Teeth_by_David_Shankbone.jpg"
width="10" height="5.2" x="5" y="11"/>
</symbol>
</svg>
<svg class="svg-large">
<use xlink:href="#scary-smiley"/>
</svg>
</body>
</html>

The code below has been tested in current Firefox, Chrome and a Webkit-based browser named Midori.
For some reason, defining the viewBox on the symbol element does not have the full desired effect in Firefox and Chrome. It does have some effect, though, as it makes the element scaleable. So, if you want set both width and height in CSS, you can do that.
If the viewBox element is specified only on the symbol and you set only one of width or height, then in Firefox and Chrome the other dimension is set according the default object size in HTML 5 whis is 300x150 px. So, in the example in the question, you get a 500x150 px element and the graphic is scaled to fit that rectangle.
If you want to define only one width or height with the other one scaling accordingly, then defining viewBox on the referencing SVG element works:
<!DOCTYPE html>
<html>
<head>
<title>SVG Symbols</title>
<style>
body { margin: 20px; }
.svg-large { width: 500px; fill: yellow;}
</style>
</head>
<body xmlns:xlink="http://www.w3.org/1999/xlink">
<svg style="display:none;">
<symbol id="scary-smiley">
<circle cx="10" cy="10" r="9.5" stroke-width="1"
stroke="black" />
<circle cx="6" cy="7" r="1.5" fill="black"/>
<circle cx="14" cy="7" r="1.5" fill="black"/>
<image xlink:href="https://upload.wikimedia.org/wikipedia/commons/thumb/1/14/Teeth_by_David_Shankbone.jpg/320px-Teeth_by_David_Shankbone.jpg"
width="10" height="5.2" x="5" y="11"/>
</symbol>
</svg>
<svg class="svg-large" viewBox="0 0 20 20">
<use xlink:href="#scary-smiley"/>
</svg>
</body>
</html>
Firefox' and Chrome's behaviour is standard compliant, according to the SVG 2 specification, according to which the <svg><use .../></svg> clause establishes a new SVG viewport.

Related

SVG filter not visible when using custom viewBox

Why is setting an svg viewBox to say 0 0 1 1 causing my filter to disappear? I also tried playing around with x, y, height, and width attributes as well as changing primitiveUnits attribute, nothing did work. Any help would be appreciated.
svg {
border: 1px solid red;
height: 100px;
width: 100px;
}
svg circle {
fill: black;
}
<html>
<body>
<svg viewBox="0 0 1 1">
<defs>
<filter id="halo1">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" />
</filter>
</defs>
<circle cx="50%" cy="50%" r="25%" filter="url(#halo1)"></circle>
</svg>
<svg>
<defs>
<filter id="halo2">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" />
</filter>
</defs>
<circle cx="50%" cy="50%" r="25%" filter="url(#halo2)"></circle>
</svg>
</body>
</html>
Fiddle playground: https://jsfiddle.net/6x34urzg/14/
When the image is scaled from 1x1 (the viewBox="0 0 1 1") to 100x100 px the standard deviation (stdDeviation="3") is also scaled. The circle with the filter is still there but 100 time too big.
If you set the standard deviation to 1/100 (stdDeviation=".03") you will get the same result as the other SVG.
svg {
border: 1px solid red;
height: 100px;
width: 100px;
}
svg circle {
fill: black;
}
<html>
<body>
<svg viewBox="0 0 1 1">
<defs>
<filter id="halo1">
<feGaussianBlur in="SourceGraphic" stdDeviation=".03" />
</filter>
</defs>
<circle cx="50%" cy="50%" r="25%" filter="url(#halo1)"></circle>
</svg>
<svg>
<defs>
<filter id="halo2">
<feGaussianBlur in="SourceGraphic" stdDeviation="3" />
</filter>
</defs>
<circle cx="50%" cy="50%" r="25%" filter="url(#halo2)"></circle>
</svg>
</body>
</html>
Actually, in the CSS you know insert CSS to HTML like Internal CSS and Inline CSS. Internal CSS is you write css on in inside on tag and that tend general for use to give style on SVG. While, Inline CSS used to write css on unique element on tags like your program(coding). Although you have writenn css svg, your svg on "Hallo1" disappear because your program have write inline CSS viewBox="0 0 1 1"make your svg "Halo 1" disappear. Moreover, viewBox="0 0 1 1" make your circle dissapear because weight=1 and height=1 so that cirle which resulted so small and you cannot see that. To solve that problem you can change your viewBox become 'viewBox="0 0 100 100" '

IE 11 :after background size not working

I added a background-image inside the pseudo :after
::after {
content: '';
display: block;
position: absolute;
right: -2.5rem;
bottom: -1.5rem;
height: 9.5rem;
width: 9.5rem;
background-image: url('../img/icons/icon_logo.svg');
background-repeat: no-repeat;
// background-size: 100% auto;
background-size: cover;
}
But the image is way bigger than the actual size of the box.
Any idea how to solve this?
(Working fine in webkit browsers)
!!!Additional Information:
I tried other svg and it works great.
Works:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
<path fill="#FF6C00" d="M0 0h64v64H0z"/>
<path fill="#FFF" d="M33 0C22 0 13 9 ..."/>
</svg>
Does not work:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64">
<path fill="#FFF" d="M0 0h64v64H0z"/>
<defs>
<path id="a" d="M0 0h64v64H0z"/>
</defs>
<clipPath id="b">
<use xlink:href="#a" overflow="visible"/>
</clipPath>
<path fill="#277052" d="M43.7 51.8s-...."/>
<defs>
<path id="c" d="M0 0h64v64H0z"/>
</defs>
<clipPath id="d">
<use xlink:href="#c" overflow="visible"/>
</clipPath>
<path fill="#EE7203" d="M40.7 28.7c0 4.8-3..." clip-path="url(#d)"/>
<path fill="#1D1D1B" d="M43 10.9c.2.1.4 0..." clip-path="url(#d)"/>
</svg>
Read this link
Adobe Illustrator give me four options to declaring style sheet properties when saving graphics as an SVG file
Presentation Attributes
Style Attributes
Style Attributes (Entity Reference)
Style Elements
No problem using the first three ways to styling properties, but embedding style sheets into SVG content inside a element cause the problem!
I had similar issues rendering CSS in IE.
My solution:
Make sure you have a <!DOCTYPE> declaration before your html
Add the meta tag <meta http-equiv="X-UA-Compatible" content="IE=edge" > (this makes sure that your code always renders the cutting edge or most updated version of internet explorer; else, it can render from older versions and things get really messy.)
I hope that helps.
Try adding in display:block to the css.

CSS Clip-path positioning issues

I have created a fairly simple shape using an SVG element which is then put into my CSS using clip-path. It should make the corners rounded for me but for some reason only 1 of the corners does the effect perfectly.
This is the shape:
<svg height="500" width="500">
<path fill="#555555" d="M50,0 L450,0 Q500,0 500,50 L500,400 Q500,450 450,450 L200,450 L175,500 L150,450 L50,450 Q0,450 0,400 L0,50 Q0,0 50,0z" />
</svg>
This is what happens when i use it as a clip-path
body {
background: #555;
}
img {
clip-path: url(#svgPath);
-webkit-clip-path: url(#svgPath);
}
<svg height="0" width="0">
<defs>
<clipPath id="svgPath">
<path fill="#FFFFFF" d="M50,0 L450,0 Q500,0 500,50 L500,400 Q500,450 450,450 L200,450 L175,500 L150,450 L50,450 Q0,450 0,400 L0,50 Q0,0 50,0z" />
</clipPath>
</defs>
</svg>
<img src="https://dummyimage.com/500" />
It seems to work perfectly within FireFox but shows the corners aren't cut correctly in Chrome apart from the bottom right corner.
The default units for the clip-path is userSpaceOnUse and this seems to calculate the coordinates of the path with reference to the root element. This is the reason why the clip-path seems like it is producing an incorrect output. Nullifying the margin and padding on the root element or absolutely positioning the element (like in the below snippet) should solve the issue.
body {
background: #555;
}
img {
position: absolute;
top: 0px;
left: 0px;
clip-path: url(#svgPath);
-webkit-clip-path: url(#svgPath);
}
<svg height="0" width="0">
<defs>
<clipPath id="svgPath">
<path fill="#FFFFFF" d="M50,0 L450,0 Q500,0 500,50 L500,400 Q500,450 450,450 L200,450 L175,500 L150,450 L50,450 Q0,450 0,400 L0,50 Q0,0 50,0z" />
</clipPath>
</defs>
</svg>
<img src="http://lorempixel.com/500/500/" />
However, in a real life scenario the actual element that has to be clipped could be present anywhere within the body and hence I think it is a much better approach to use the objectBoundingBox as the units like in the below snippet:
body {
background: #555;
}
img {
clip-path: url(#svgPath);
-webkit-clip-path: url(#svgPath);
}
<svg height="0" width="0">
<defs>
<clipPath id="svgPath" clipPathUnits="objectBoundingBox">
<path fill="#FFFFFF" d="M0.1,0 L0.9,0 Q1,0 1,0.1 L1,0.8 Q1,0.9 0.9,0.9 L0.4,0.9 L0.35,1 L0.3,0.9 L0.1,0.9 Q0,0.9 0,0.8 L0,0.1 Q0,0 0.1,0z" />
</clipPath>
</defs>
</svg>
<img src="https://dummyimage.com/500" />
As mentioned in the question itself, this behavior is visible only in Chrome and not Firefox for reasons unknown to me. Firefox produces an output similar to the expected one even when (a) extra padding + margin is added to the body and (b) when the image itself is wrapped inside another container which also has padding + margin.
The only case where Firefox's output matches with Chrome is when a padding is added directly to the img tag itself. I believe this happens because padding is part of the element and thus affects the coordinates.

Can't control SVG icon fill color with CSS styles

I have read all post in here about styling my svg fill color with CSS but without luck.
What I want is to able to make an icon with a link. My external svg file is grey, but I would like to make it red with css and change color to yellow when hovering.
I think I am targeting the SVG wrong. Please help. My test is here:
testpage
<%#LANGUAGE="VBSCRIPT" CODEPAGE="1252"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Test</title>
</head>
<style type="text/css">
<!--
.svgicon {
fill: red;
}
.svgicon:hover {
fill: yellow;
}
-->
</style>
<body>
<table width="100%" border="0" class="tabelform">
<tr>
<td width="100%"><object type="image/svg+xml" data="S/Images/new.svg" height="18" width="18"></object>test icon</td>
</tr>
</table>
</body>
</html>
Answer a little overdue, but worth having for reference for others.
Basically, the only type of SVG usage which can be used in conjunction with CSS is the inline usage.
This means you would literally put your SVG markup directly into the HTML source as follows:
<div class="my-svg">
<svg xmlns="http://www.w3.org/2000/svg" id="SVG-dropdown-icon" viewBox="0 0 15 11">
<title>
Expand
</title>
<path d="M1.758 1L7.5 6.582 13.242 1 15 2.709 7.5 10 0 2.709z"/>
</svg>
</div>
NOTE: This SVG has been optimised using SVGO and then manually edited to include and ID
You can now control the SVG using CSS like so:
.my-svg {
fill: pink;
}
.my-svg:hover {
fill: red;
}
currentColor
You can also use the currentColor keyword in the SVG to apply a colour to certain elements of it, for example:
<div class="my-svg">
<svg id="SVG-active-icon" viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg">
<title>
Current Event
</title>
<g fill="none" fill-rule="evenodd">
<circle class="activeEventPulse" stroke="currentColor" fill="#EBEBED" cx="12.5" cy="12.5" r="11.5"/>
<ellipse fill="currentColor" cx="12.5" cy="12.5" rx="4.5" ry="4.5"/>
</g>
</svg>
</div>
.my-svg {
color: red;
}
JS Fiddle
This can be handy if you need to use the same SVG across different websites / themes, such as dark and light, for easily switching SVG colours with CSS.
Caching / performance consideration: SVG cloning
You should also keep in mind, it's not a good idea to use inline SVG for repetitive images, such as icons, because they can not be cached (the SVG code will be repeated throughout your HTML, increasing the ultimate file size).
Instead, one approach I like to use is to create an SVG index at the top of my page, which contains all the SVGs I want to use on the page, for example:
<svg xmlns="http://www.w3.org/2000/svg" class="svg-index">
<svg xmlns="http://www.w3.org/2000/svg" id="SVG-dropdown-icon" viewBox="0 0 15 11">
<title>
Expand
</title>
<path d="M1.758 1L7.5 6.582 13.242 1 15 2.709 7.5 10 0 2.709z"/>
</svg>
<svg id="SVG-active-icon" viewBox="0 0 25 25" xmlns="http://www.w3.org/2000/svg">
<title>
Current Event
</title>
<g fill="none" fill-rule="evenodd">
<circle class="activeEventPulse" stroke="currentColor" fill="#EBEBED" cx="12.5" cy="12.5" r="11.5"/>
<ellipse fill="currentColor" cx="12.5" cy="12.5" rx="4.5" ry="4.5"/>
</g>
</svg>
</svg>
Make sure you set the SVG index to display: none so it doesn't show up on the page.
You can now reuse these SVGs repetitively throughout the page using the xlink:href attribute as follows:
<svg class="dropDown">
<use xlink:href="#SVG-dropdown-icon" />
</svg>
<svg class="active">
<use xlink:href="#SVG-active-icon" />
</svg>
<svg class="active">
<use xlink:href="#SVG-active-icon" />
</svg>
<svg class="dropDown">
<use xlink:href="#SVG-dropdown-icon" />
</svg>
JS Fiddle
This is called cloning, and allows you to take advantage of cacheable SVGs which can be controlled with CSS!
Hope this helps!
try to use inline svg instead of external svg source then you can control

CSS padding property for svg elements

I can't figure out how the CSS padding property is interpreted for svg elements. The following snippet (jsFiddle):
<!DOCTYPE html>
<meta charset="utf-8">
<title>noob d3</title>
<style>
svg{background-color:beige;
padding:0px 0px 50px 50px;}
rect{fill:red;
stroke:none;
shape-rendering:crispEdges;}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
d3.select("body")
.append("svg")
.attr("width", 155)
.attr("height", 105)
.append("g")
.append("rect")
.attr("class", "frame")
.attr("x", 50)
.attr("y", 50)
.attr("width", 50)
.attr("height", 50);
</script>
</body>
... displays significantly differently in Firefox and Chrome. What's worse, neither display really makes sense to me: the size of the displayed svg element (the "beige" rectangle) looks to be significantly bigger than what I expected.
So my question is two-fold: 1) How is the padding property of an svg element supposed to affect where things get drawn within it? 2) Is there a polyfill that will ensure that both Chrome and Firefox both handle padding in the same way?
AFAIK, the SVG standard doesn't specify anything like padding, which is why it's handled inconsistently. Just set the SVG to the size you want (with padding) and maybe add a rect to make it appear like you want it to appear.
From my experience (granted, still very little as I am still learning SVG), I have strayed away from using padding wherever that I could do so. It was suggested to me when I was first learning SVG that I use margin in place of padding, if possible.
This is also because you can use display: block; and margin: 0 auto; to make the left and right sides of an SVG to fit directly into the middle of the screen.
There is no padding or margin, but you can set x and y attributes such that the elements inside or outside get a padding and margin. For example, if an element starts at (0,0), starting at (10, 10) will automatically give a margin of 10.
You can apply padding to parent svg elements
The padding as described by the OP actually works – albeit, not as desired.
Outermost <svg> will be rendered with padding (won't work for nested svgs).
But: child elements (e.g the <rect>) won't be re-aligned according to – unlike HTML DOM elements.
svg {
background-color: beige;
max-height:20em;
}
.pdd{
padding: 0px 0px 50px 50px;
}
rect {
fill: red;
stroke: none;
shape-rendering: crispEdges;
}
.borderBox{
box-sizing: border-box;
}
.overflow{
overflow:visible
}
<p>Rendered size: 205 x 155 – padding added to initial dimensions </p>
<svg class="pdd" width="155" height="105">
<g>
<rect class="frame" x="50" y="50" width="50" height="50" />
</g>
</svg>
<p>Rendered size: 155 x 105; cropped</p>
<svg class="pdd borderBox" width="155" height="105">
<g>
<rect class="frame" x="50" y="50" width="50" height="50" />
</g>
</svg>
<p>Rendered size: 155 x 105; cropped; overflow visible</p>
<svg class="pdd borderBox overflow" width="155" height="105">
<g>
<rect class="frame" x="50" y="50" width="50" height="50" />
</g>
</svg>
Usecase: padding for fluid svg layouts
So, padding doesn't work well for fixed widths/heights.
However, it can be handy for flexible/fluid layouts – provided you're using relative (percentage) units for svg child elements.
*{
box-sizing:border-box;
}
svg{
border:1px solid #ccc;
}
svg {
background-color: lightblue;
padding:0 10px;
overflow:visible;
}
.svg2 {
padding:10px;
}
.svg3 {
padding:0px;
}
.resize{
resize:both;
overflow:auto;
padding:1em;
border:1px solid #ccc;
}
<p>resize me :</p>
<div class="resize">
<svg id="svg" width="100%" height="40" xmlns="http://www.w3.org/2000/svg">
<circle cx="0" cy="10" r="5" />
<circle cx="0" cy="30" r="5" />
<circle cx="50%" cy="10" r="5" />
<circle cx="50%" cy="30" r="5" />
<circle cx="100%" cy="10" r="5" />
<circle cx="100%" cy="30" r="5" />
</svg>
</div>
<div class="resize">
<svg class="svg2" width="100%" height="100%" xmlns="http://www.w3.org/2000/svg">
<!-- align path center to x/y =0 by adding viewBox offset width/2 height/2 -->
<symbol class="icon icon-home" id="iconHome" viewBox="20 20 40 40" overflow="visible">
<path d="M36.4 22.2l-5.2 0l0 13l-3.4 0l0-16.7l-7.7-8.7l-7.7 8.7l0 16.7l-3.4 0l0-13l-5.2 0l16.4-17.4z"></path>
</symbol>
<use x="0" y="0%" href="#iconHome" width="20" height="20" />
<use x="0" y="100%" href="#iconHome" width="20" height="20" />
<use x="50%" y="0%" href="#iconHome" width="20" height="20" />
<use x="50%" y="100%" href="#iconHome" width="20" height="20" />
<use x="100%" y="0%" href="#iconHome" width="20" height="20" />
<use x="100%" y="100%" href="#iconHome" width="20" height="20" />
</svg>
</div>
Based on what I was able to try on firefox and chromium: the specified width and height for an svg include the padding.
In other terms, if you want an image of 20*20px with a padding of 10px on each side, you should set the width to 20+10*2 = 40px (same thing with the height) and the padding to 10px
Note : 20+10*2 : 20 is the width you want, 10 is your padding and you double it because you want it on both sides.
The best solution is open Inkscape (or other SVG editor) and change dimension

Resources