Off-center rendering of inline SVG on iOS Safari
Asked Answered
Z

4

7

Edit: dev link removed, giving up on this question.

I've been trying to solve a seemingly tiny rendering issue for a week now, and have exhausted all my options, here's my cry for help. I've read all related questions on SO, yet still did not manage to solve this problem.

Goal
My goal is to render a simple symmetrically shaped SVG icon inline on a web page. The SVG is inside a wrapper which has a background color and some padding. This is what a correct rendering would look like:

enter image description here

Above is a magnified view in Chrome of what is a correct rendering. Note how the icon is positioned correctly, it is not off-center in any way.

Note that I am specifically using a small padding and big contrast in color to visualize the problem, as it is a subtle yet disturbing problem.

Problem
The above correct rendering is consistent across desktop browsers on Windows (Firefox, Chrome, Edge) as well as Chrome and Firefox on Android. It only renders incorrectly on iOS Safari. Here's an example of an incorrect rendering:

enter image description here

The above is a magnified screenshot from an iPhone 6S, iOS 11. These are 3 instances of the same icon on the same page. Note how each rendition is incorrect in being off-center. Note also how it's not even consistent in how it renders off-center, it seems almost random.

The icon itself
The icon itself comes from Symbolicons. I checked the vector itself to be sure that it has no spacing in it: enter image description here

It touches the borders, and is fitting geometrically in a viewbox of 24x24. I checked a few other icons in this icon pack, and I'm facing the same problem with other icons, yet only in iOS Safari. So I don't think there's a problem in the SVG itself.

Problem details
A bit more detail on iOS Safari behavior. I tested on iOS 10 and 11, iPhone 5, iPhone 6S, iPad Pro 10", iPad Pro 12". The incorrectness of the rendering seems to differ on the size of the device and its orientation. For example, the iPhone5 is the only one having a perfect rendering, yet when turning it into landscape mode and refreshing the page, it fails. On the iPads, it pretty much always fails yet it can differ from a subpixel of to way off, depending on the size of the screen and its orientation.

This behavior is perhaps a hint that some scaling effect is at work.

A solid baseline
The page I'm integrating these icons on is relatively complicated, with possibly many factors influencing rendering, so I started off with a minimum test case:

https://codepen.io/fchristant/pen/PQKWww

I tested this codepen across all iOS devices and they lead to a perfect rendering in any scenario, no off-center issues. Note how the codepen takes full control of rendering, using pixels instead of relative values:

.icon-wrap {
    display:block;
    background:green;
    margin:0;
    padding:0;
    width:16px;
    height:16px;
    border:1px solid red;
    box-sizing:content-box;
    padding:4px;
    margin-bottom:20px;
}

.myicon {
    display:block;
    width:100%;
    height:100%;
    padding:0;
    margin:0;
    border:1px solid purple;
    box-sizing:border-box;
}

Furthermore, both the wrapper and SVG are display:block, to avoid any spacing issues.

As said, this minimum test case works perfectly. Which means its possible to correctly render this scenario on mobile Safari.

Yet when integrating it back into my page, it fails. I've spent days chasing conditions in my page that may affect it, and am running out of options. Here are some of the things I experimented with:

  • Setting width / height on the SVG itself, despite also having it in CSS. Does not seem to matter.
  • Trying different values for preserveAspectRatio on the SVG: makes no difference
  • Setting the SVG version: no differences
  • Variations of position relative/absolute: no difference
  • Instead of width/height:100%, using hardcoded pixels: no difference
  • Placing it inside specific layout containers or outside (flexbox, grid): sometimes changes rendering, but still never consistently correct.
  • Playing around with meta viewport values: no difference
  • Trying different values for shape-rendering: no difference

I've considering that this may be a subpixel rounding issue, but logically that makes no sense. I'm using hardcoded dividable-by-2 pixel values everywhere. Plus, the original codepen works.

Bottom line: between the working codepen and my test page, there is some race condition that triggers the incorrect rendering, and I haven't found it despite days of searching.

Test page
Here's the test page: [removed]

It's on my dev server. I'll try to keep it up whilst the question gets activity, if any. You can find the "globe" icon positioned on some of the thumbnail on the page. Unfortunately, you can only see the problem on a real iOS device, and only by zooming in as far as you can.

And yes, I know it seems absurd to go into extreme lengths to solve such a tiny issue, but this one really is throwing me off. It sticks out like a sore thumb once you see it. Plus, knowing that Safari can render this correctly, as demonstrated in the codepen, makes me hopeful that this can be solved.

I would be most grateful for a structural fix. I've had some tries with one-off fixes, for example by reducing or increasing 1px of size on the outer layout container. These kind of solutions solve it in one device/orientation, yet create new problems in the other scenarios. I'm hoping to consistently render such icons no matter in which context I put it. All browsers can do it, yet not mobile Safari. Well, Safari can (see codepen), yet there's something I'm doing that triggers it into behaving badly, and I have no idea what it is.

Edit: Suggestion was to replace the SVG with a CSS box, to see if this also suffers from the rendering glitch. Here a screenshot of that situation, it renders perfectly:

enter image description here

So it looks like it really is SVG-related.

Zenobia answered 19/2, 2018 at 20:14 Comment(14)
I'm seeing a CSS rule width/height=24.5px for .c_observation__icon svg on the test page, while the enclosing span is set to 24px. That is at least contrary to your claim "hardcoded dividable-by-2 pixel values everywhere".Cysticercoid
@Cysticercoid So sorry, that was a left-over from experimenting with subpixel rounding effects. Removed that width now so that it takes 100%. To be clear, the problem remains with or without this declaration.Zenobia
Is the problem still there if you replace the SVG with a solid-color CSS box, or an embedded image?Odessaodetta
@Odessaodetta Good thinking, that would prove whether it is an SVG problem or not. Just tested it, see edited answer. using a normal CSS box, rendering is perfect. So it looks like it really is an SVG issue. Reverted code back to the SVG version.Zenobia
@Odessaodetta To supplement with detailed test results. Current code (with SVG) renders incorrectly at iPhone 6s in both orientations. Incorrect on 10" iPad portrait, yet correct in landscape. Correct on iPad 12" in both orientations. All run the same iOS version. Absurd!?Zenobia
@Ferdy The dependence on device & orientation is probably about resolution. WebKit is adjusting the exact position of the SVG to line up with screen pixels, which is shifting it just enough that it isn't centered in the parent element's padding. But beyond that, I don't have any suggestions except to file a bug on WebKit (bugs.webkit.org).Odessaodetta
@AmeliaBR: Alright, thank for the effort, much appreciated!Zenobia
Looks like the 250 rep gonna be wasted if you can't provide an example.Tress
@Tress It's OK, rep is meaningless to me. Had to remove the link as its a dev server I can't keep up forever.Zenobia
In your codepen you don't declare version as a SVG attribute. Not sure if this could possibly cause render problems without testing the code but maybe you can find out yourself. Normally - version="1.1" - is used nowadays for SVG.Commissary
@RamonDreise I did try that, it's described in the question. A long question, so easy to miss :)Zenobia
Did you try linking your SVGs instead of inlining? I'm assuming its an SVG plus pixel density issue. SVGs don't actually have pixels, and iOS devices to some fancy pixel density rendering across devices (which usually works amazingly well considering the variety of devices). I believe safari renders inline and linked SVGs differently.Pelisse
Or, attempt to use a responsive unit instead of px. (%, em, vh)Pelisse
@Pelisse You could be right, also read that using the SVG as an image produces more reliable results. Unfortunately I can't go that route, my icon system is based on inline rendering, for several good reasons. Responsive units make the problem worse, that's why I went for precision.Zenobia
U
8

I was having the exact same issue until I added:

svg {
  -webkit-transform: translate(0px,0px); /* Safari and Chrome */
}

I hope this solves issues for anyone else!

Uptake answered 2/9, 2021 at 11:28 Comment(2)
@Zenobia This should be the accepted answer.Oneway
Thank you. This works perfectly. Why on earth is this necessary?Cithara
A
1

Well, try this...

I see that the wrapping span has width and height equal to the svg dimensions. However you have 1px padding. Unfortunately this is in the blind since we can't test it on our own. Try this:

.c_observation__indicator--country .c_observation__icon {
  background: #000;
  border-radius: 50%;
  width:25px;
  height:25px;
  padding:1px;
}
Anderlecht answered 22/2, 2018 at 14:1 Comment(3)
Thanks. Just tried it (reverted now) but unfortunately it makes no difference. The icon itself still moves around in every direction inside the padding box.Zenobia
@Ferdy There was no need to revert it, since it was the right thing to do. Have you tried applying position:relative to the parent span and position:absolute;top:1px;left:1px on the svg?Anderlecht
Whether its the right thing to do depends on box-sizing. Both types did not make a difference. I did tinker with absolute positioning as part of my investigation but it does not address the problem. As the screenshot shows, the misalignment differs per instance of the icon. A repositioning may fix the first one yet throw it off in the next one or even at a different resolution device.Zenobia
D
1

DISCLAIMER: I'm leaving an answer because I do not have the reputation to leave a comment, but I have no way of knowing if this could apply to the OP's particular problem, and besides this is not a solution but more of a direction to explore in trying to solve the problem.

I know this question is rather old now, but I encountered an SVG alignment problem in Safari for iOs and I managed to solve it with a little bit of JavaScript. Since the test page is gone, I can't comment on how to apply it in your particular case, but here is the idea:

By poking around, I noticed that Safari on iOs was returning wrong dimensions and positioning when calling getBoundingClientRect() on the element I wanted to center some other SVG element on. I put the call in a timeout with 250 ms delay, and it then returned the correct bounding rectangle, and I could then align my elements properly and consistently across browsers. The point is that there seem to be a race condition in Safari's SVG positioning code, so maybe this bug affects your icons as well.

If it is something that could work in the context of your project, I suggest trying to force Safari to re-layout the SVG a few hundred milliseconds after the DOM has loaded.

Dextrous answered 9/7, 2018 at 13:0 Comment(0)
G
0

This might be an outside the box type answer, but it might help.. though it is kinda hacky.. and I am only suggesting this answer because it seems you have it working on all other browsers except safari, and if you tinker around too much, you might create new problems with other browsers.

You can try to use javascript and a little bit of jquery to help with your problem. You can use them to detect what browser you're on, and from there you can make minor adjustments to the styling. You can also detect landscape mode as well. Here's a stackoverflow answer to help get started on how to detect browsers:

How to detect Safari, Chrome, IE, Firefox and Opera browser?

Once you figured out how to detect safari, you can use jquery to apply css styling to your div and images. So whatever you do to that div on safari, won't interfere with the work you've done with your other browsers.

<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script>
    $(document).ready(){

        var isSafari = /constructor/i.test(window.HTMLElement) || (function (p) { return p.toString() === "[object SafariRemoteNotification]"; })(!window['safari'] || (typeof safari !== 'undefined' && safari.pushNotification));

        if (isSafari) {
            if(window.innerHeight > window.innerWidth){
                //portrait mode
                $('.icon-wrap').css({
                    ... your css for safari code here ...
                });

                $('.myicon').css({
                    ... your css for safari code here ...
                });
            } 
            else {
                //landscape mode
                $('.icon-wrap').css({
                    ... your css for safari code here ...
                });

                $('.myicon').css({
                    ... your css for safari code here ...
                });
            }
        }
    }
</script>
Giamo answered 28/2, 2018 at 13:50 Comment(2)
Thanks for the effort. A very heavy solution, but my main objection is that it does not solve the problem. There is no CSS I know of that will specifically fix it for iOS Safari. It's not like I can always offset by 1px or something like that for it to work. Alignment and out of bounds rendering differs per instance of the icon.Zenobia
Well it is a lot of work, and you might want to do it for all those instances. Try to cover all the bases as much as you think you can. You can create specific style sheets for the problem devices and pull them up as needed. Does this happen with Google Chrome on a device the same size as an iPhone? If yes, you have to work around it. Your problem is actually one of the many reasons why people make app versions of what their websites would be.Giamo

© 2022 - 2024 — McMap. All rights reserved.