IE11 defaults SVG to 100% width - how to refactor this code? - svg

I am not aware of alternatives to adding width: 1em to the SVG in order to fix the IE11 issue (please see comment in the code). Play with the code in the codepen. Appreciate any help! Thanks :)
https://codepen.io/ambrwlsn90/pen/zjZYpb
<div class="box">
<span class="handle--draggable">
<svg class="handle--icon" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 10 32">
<circle cx="2" cy="2" r="2" />
<circle cx="8" cy="2" r="2" />
<circle cx="2" cy="9" r="2" />
<circle cx="8" cy="9" r="2" />
<circle cx="2" cy="16" r="2" />
<circle cx="8" cy="16" r="2" />
<circle cx="2" cy="23" r="2" />
<circle cx="8" cy="23" r="2" />
<circle cx="2" cy="30" r="2" />
<circle cx="8" cy="30" r="2" />
</svg>
</span>
</div>
.box {
position: relative;
width: 400px;
height: 100px;
border: 3px solid black;
background-color: white;
top: 50px;
left: 100px;
padding: 15px;
line-height: 1.5em;
}
.handle--draggable {
position: absolute;
cursor: move;
left: -26px;
top: -3.5px;
}
/**
* 1. Magic number added to fix visual bug in IE: 11
*/
.handle--icon {
fill: black;
background-color: grey;
padding: 3.5px;
height: 37px;
width: 1em; /* 1. */
position: relative;
&:hover {
left: -5px;
border-right: 5px solid grey;
}
}

The SVG tag needs some basic attributes in order to be rendered as expected. If you read the W3C documentation according the outermost svg tag you will find the answer:
For embedded ‘svg’ elements, the width of the rectangular region into which the ‘svg’ element is placed.
A negative value is an error (see Error processing). A value of zero disables rendering of the element.
If the attribute is not specified, the effect is as if a value of '100%' were specified.
So you will need to specify the width and height attributes of the SVG tag, or it will be rendered at 100% width.
The opening svg tag should look like this:
<svg class="handle--icon" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 10 32" width=“10” height=“32”>
Then the SVG will look the same crossbrowser.
Having the width and height attributes defined on your svg element you can discard the ugly Internet Explorer 11 hack.

Related

Issue when zooming in on SVG donut chart in Safari

I'm attempting to create a donut chart using SVG, and am running into a problem when viewing it in Safari. Here's a fiddle that shows the issue; I'll describe it in detail below:
https://jsfiddle.net/nijhazer/phy2ossh/
This fiddle shows a graphic comprised of two circles overlaid atop one another. The problem becomes apparent when a Safari user increases zoom size in her browser:
Relevant HTML from the example:
<div class="donut-chart">
<svg width="200" height="200">
<circle class="backdrop" cx="100" cy="100" r="65" fill="#d5d8d5" stroke="none" stroke-width="0"></circle>
<circle class="progress" cx="100" cy="100" r="75" fill="none" stroke="lightgreen" stroke-width="20" style="stroke-dashoffset: 353.428875px;"></circle>
<circle class="outer-ring" cx="100" cy="100" r="85" fill="none" stroke="#d5d8d5" stroke-width="1"></circle>
</svg>
</div>
Relevant CSS from the example:
body {
background-color: white;
}
.donut-chart {
width: 200px;
height: 200px;
position: relative;
}
svg {
width: 200px;
height: 200px;
}
.progress {
stroke-dasharray: 471.24;
transform: rotate(-90deg);
transform-origin: 50% 50%;
background-color: transparent;
}
I don't know if this helps, but at I can read from the comments that people are blaming the CSS property transform-origin. In this example I moved the styling to attributes on the circle element. I also added pathLength to make it easier to control the progress bar.
body {
background-color: white;
}
.donut-chart {
width: 200px;
height: 200px;
position: relative;
}
svg {
width: 200px;
height: 200px;
}
<div class="donut-chart">
<svg viewBox="0 0 200 200" xmlns="http//www.w3.org/2000/svg">
<circle class="backdrop" cx="100" cy="100" r="65" fill="#d5d8d5"
stroke="none" stroke-width="0" />
<circle class="progress" cx="100" cy="100" r="75" fill="none"
stroke="lightgreen" stroke-width="20" stroke-dasharray="25 100"
transform="rotate(-90 100 100)" pathLength="100" />
<circle class="outer-ring" cx="100" cy="100" r="85" fill="none"
stroke="#d5d8d5" stroke-width="1" />
</svg>
</div>
I can confirm chrwahl's approach is working (at least in MacOS/IOS safari versions (15.4) I've tested - Unfortunately, the support of functions may vary from version to version)
Another workaround could be to add a translateX offset before rotating like so:
.donut-chart {
width: 200px;
height: 200px;
position: relative;
display: block;
}
svg {
width: 200px;
height: 200px;
}
.progress {
stroke-dasharray: 471.24;
background-color: transparent;
transform: translate(0px, 200px) rotate(-90deg);
}
<div class="donut-chart">
<svg width="200" height="200" viewBox="0 0 200 200">
<circle class="backdrop" cx="50%" cy="50%" r="65" fill="#d5d8d5" stroke="none" stroke-width="0"></circle>
<circle class="progress" cx="50%" cy="50%" r="75" fill="none" stroke="lightgreen" stroke-width="20" style="stroke-dashoffset: 353.428875px;"></circle>
<circle class="outer-ring" cx="100" cy="100" r="85" fill="none" stroke="#d5d8d5" stroke-width="1"></circle>
</svg>
</div>
This "hack" won't work combined with transform-origin: 50% 50%

Safari trouble positioning SVG feSpotlight filter

I want to use a spotlight effect but it only seems to work in Chrome, looks "just ok" in Firefox, but will not position (x,y,z) in Safari. (Other browsers not tested)
I've tried different filter and primitive units, and while this makes a difference, Safari still cannot seem to position the lighting effect in any case.
In pursuit of understanding what is going on, I've tried lots of workarounds including different userSpaceOnUse/objectBoundingBox combos, and different svg structure but have never been able to find one that works on Safari.
Examples
Default filter/primitive units:
https://jsfiddle.net/localnerve/y470d52v/
objectBoundingBox units:
https://jsfiddle.net/localnerve/uyc7o52k/
A picture is also worth a 1000 words (Safari, Chrome, FF). The spotlight on Safari is rendered off-canvas to the right and bleeds in from the right.
To show the spotlight positions on Safari are "out of whack", here's me nudging them in web inspector so I can see the spotlight render at all:
Here is the code using objectBoundingBox filter and primitive units:
<style>
body {
margin: 0;
padding: 0;
font-family: sans-serif;
font-size: 16px;
color: #fff;
}
* {
transform-origin: 50% 50% 0;
}
.scene-container {
display: flex;
flex-flow: column nowrap;
justify-content: center;
align-items: center;
}
.scene-container.content {
position: relative;
width: 100%;
height: 150vh;
background-image: linear-gradient(hsla(240, 90%, 8%, 1) 0%, 99%, hsla(217,24%,71%,0) 100%);
box-shadow: 0px -10px 20px hsl(240, 90%, 8%);
}
.spot {
height: 100%;
width: 100%;
}
</style>
<body>
<div class="scene-container content">
<h2>Here's a spotlight.</h2>
<svg class="spot" viewBox="0 0 2000 3000" preserveAspectRatio="xMidYMid">
<defs>
<filter x="-0.2" y="-0.2" width="1.4" height="1.4" filterUnits="objectBoundingBox" primitiveUnits="objectBoundingBox" id="spotlight">
<feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur1"></feGaussianBlur>
<feSpecularLighting result="specOut" in="blur1" specularConstant="1.8" specularExponent="5" lighting-color="#ffffff">
<feSpotLight x="0.5" y="-0.4" z="0.03" pointsAtX="0.5" pointsAtY="0.8" pointsAtZ="0" limitingConeAngle="13.7"></feSpotLight>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"></feComposite>
</filter>
<clipPath id="spot-clip">
<rect x="-50" y="2840" width="2200" height="200"></rect>
</clipPath>
<filter id="spot-blur">
<feGaussianBlur in="SourceGraphic" stdDeviation="15">
</feGaussianBlur>
</filter>
</defs>
<g transform="translate(0, -175)" filter="url(#spotlight)">
<ellipse cx="50%" cy="95.333%" rx="27%" ry="130" fill="#fff" clip-path="url(#spot-clip)" filter="url(#spot-blur)"></ellipse>
<rect x="25%" y="43.667%" width="50%" height="50%" stroke="peru" stroke-width="3%" stroke-linejoin="round" fill="#000"></rect>
</g>
</svg>
</div>
</body>
Any insight you can give is greatly appreciated.
There are known bugs in webkit/Safari for light source positioning wrt transforms and oBB units. After the webkit/blink engine schism, no-one at Apple picked these bugs up from the Chrome team.
https://bugs.webkit.org/show_bug.cgi?id=88877
https://bugs.webkit.org/show_bug.cgi?id=113059
The workaround is not to use transforms and oBB units (do any dynamic positioning or sizing via JavaScript)

SVG backgrounds are getting cut off in IE11 when zooming

We are trying to display SVG backgrounds in internet explorer. Our images are always getting cut off when a zoom other than 100% is used. This can be reproduced using the following code:
with this svg
<svg xmlns="http://www.w3.org/2000/svg" height="100" width="100" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="48" stroke="black" stroke-width="3" fill="red" />
</svg>
div {
width: 14px;
height: 14px;
background-size: 14px 14px;
background-repeat: no-repeat;
background-image: url("data:image/svg+xml;charset=utf-8,%3C%3Fxml version='1.0' encoding='UTF-8'%3F%3E%3Csvg xmlns='http://www.w3.org/2000/svg' height='100' width='100' viewBox='0 0 100 100'%3E%3Ccircle cx='50' cy='50' r='48' stroke='black' stroke-width='3' fill='red'%3E%3C/circle%3E%3C/svg%3E");
}
<div></div>
The result looks like this:
In all other browsers it renders fine. Has anyone else ever experienced this bug? Is there a workaround?
I have found one workaround which requires very little work:
Make the SVG image 2X size of the actual content (this would make the circle look like this:
<svg xmlns="http://www.w3.org/2000/svg" height="200" width="200" viewBox="0 0 200 200">
<circle cx="50" cy="50" r="48" stroke="black" stroke-width="3" fill="red" />
</svg>
Then use the :after pseudo element to create an inside element with 2x the desired size. So the html would be
<div class="circle"></div>
And the css would be
.circle {
width: 14px;
height: 14px;
position:relative;
}
.circle:after {
position: absolute;
top: 0;
left: 0;
content: ' ';
width: 28px;
height: 28px;
background-size: 28px 28px;
background-repeat: no-repeat;
background-image: url('circle.svg');
}
The extra space in the :after pseoudoelement gives IE spare canvas to draw on, but both the visible icon and the space occupied by the original container remain the same.

Why SVG's stroke width is different from border width?

Why <svg>'s 1px stroke-width is wider than <div>'s 1px border-width?
Is it possible to create an <svg> that looks exactly like the <div>below?
<svg>
<rect x="10" y="10" width="100" height="100" stroke-width="1" stroke="red" fill="white" />
</svg>
<div style="margin: 0 0 10px 10px; width: 100px; height: 100px; border: 1px solid red">
</div>
That's just antialiasing. You can turn it off if you want via the shape-rendering CSS property. Adjusting the co-ordinates by 0.5px may also work.
<svg>
<rect x="10" y="10" width="100" height="100" stroke-width="1" stroke="red" fill="white" shape-rendering="crispEdges" />
</svg>
<div style="margin: 0 0 10px 10px; width: 100px; height: 100px; border: 1px solid red">
</div>

SVG Responsive Text

I have an SVG within a web page, it consists of images + text
<object data="/infographic/timeline.svg" type="image/svg+xml">
<img src="/infographic/timeline.svg" alt="Timeline">
</object>
All the images are responsive, but the text isn't, so the text becomes really, REALLY small.
snippet of SVG (its massive)
<defs>
<style>
.cls-1 {
font-size: 60.014px;
}
.cls-1, .cls-10 {
opacity: 0.69;
}
.cls-1, .cls-10, .cls-4, .cls-5, .cls-7, .cls-8, .cls-9 {
fill: #ffffff;
}
.cls-1, .cls-10, .cls-3, .cls-4, .cls-5, .cls-6, .cls-7, .cls-9 {
text-anchor: middle;
}
.cls-1, .cls-3, .cls-6 {
font-family: "Roboto";
}
.cls-2 {
font-size: 32.014px;
}
.cls-3 {
font-size: 14.089px;
}
.cls-3, .cls-6 {
fill: #db7426;
}
.cls-4, .cls-6 {
font-size: 32px!important;
}
.cls-10, .cls-4, .cls-5, .cls-7, .cls-8, .cls-9 {
font-family: Roboto;
}
.cls-5 {
font-size: 24px;
}
.cls-5, .cls-8, .cls-9 {
font-weight: 400;
}
.cls-6 {
font-weight: 600;
}
.cls-10, .cls-7 {
font-size: 18.75px;
font-weight: 300;
}
.cls-7 {
opacity: 0.4;
}
.cls-8, .cls-9 {
font-size: 22px;
}
</style>
</defs>
<text id="Who_are_you_what_do_you_do_what_s_your_why_What_s_been_keepi" data-name="Who are you, what do you do, what’s your why? What’s been keepi" class="cls-8" x="397.706" y="535.325">Who are you, what do you do, what’s your why?<tspan x="397.706" dy="26.4">What’s been keeping you lying awake at night. </tspan></text>
Is there anyway I can get the text size to increase as the SVG/screen width gets smaller?
Any help would be greatly appreciated.
It's not possible with pure SVG (at least not yet). The only solution would be to either:
inline the SVG and manipulate the size of the text with javascript.
inline the SVG and control the size of the text with media queries (see below).
Add CSS to the SVG and use media queries there (see below).
use media queries to switch SVGs when the page gets small
Example of option 2: Using media queries with inlined SVGs
text {
font-size: 10px;
}
#media (max-width: 400px) {
text {
font-size: 20px;
}
}
<svg viewBox="0 0 100 100" width="100%" height="100%">
<circle cx="50" cy="50" r="50" fill="orange"/>
<text x="50" y="60" text-anchor="middle">Testing</text>
</svg>
Example of option 3: Using media queries in CSS in the SVGs
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100" width="100%" height="100%">
<style>
text {
font-size: 10px;
}
#media (max-width: 400px) {
text {
font-size: 20px;
}
}
</style>
<circle cx="50" cy="50" r="50" fill="orange"/>
<text x="50" y="60" text-anchor="middle">Testing</text>
</svg>
This is possible using the foreignObject svg element in a html context and some adjustment of the viewBow.
On this demos, the text stay selectable:
.demo {
overflow: auto;
resize: both;
border:1px black solid;
width: 230px;
height: 130px
}
.svgtext {
font-size: 28rem;
height:100%;
width:100%
}
<div class="demo">
<svg x="0" y="30" viewBox="0 0 100 100" width="100%" height="100%">
<foreignObject x="12" y="23" height="100%" width="100%">
<div class"svgtext">
Hello world!
</div>
</foreignObject>
</svg>
</div>
Use preserveAspectRatio to control the resizing behavior:
.demo {
overflow: auto;
resize: both;
border:1px black solid;
width: 230px;
height: 130px
}
.svgtext {
font-size: 28rem;
height:100%;
width:100%
}
<div class="demo">
<svg preserveAspectRatio="none" x="0" y="30" viewBox="0 0 100 100" width="100%" height="100%">
<foreignObject x="12" y="23" height="100%" width="100%">
<div class"svgtext">
Hello world!
</div>
</foreignObject>
</svg>
</div>

Resources