Text is not positioned the same in Safari as in other browsers
Asked Answered
T

5

11

The text is not positioned the same in Safari as it is in other browsers. Is there are reason for this? Is there a style to ensure it meets precise measurements.

* {
  margin: 0;
  padding: 0;
}
#btn_signup {
  opacity: 1;
  position: absolute;
  width: 185px;
  height: 50px;
  left: 10px;
  top: 10px;
  overflow: visible;
}
#Rectangle_1 {
  opacity: 1;
  fill: transparent;
  stroke: rgb(67, 66, 93);
  stroke-width: 1px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}
.Rectangle_1 {
  position: absolute;
  overflow: visible;
  width: 185px;
  height: 50px;
  left: 0px;
  top: 0px;
}
#Sign_up {
  opacity: 1;
  position: absolute;
  left: 58px;
  top: 16px;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  overflow: visible;
  width: 63px;
  white-space: nowrap;
  text-align: center;
}
#Path_1 {
  opacity: 1;
  fill: rgba(0,0,0,0);
  stroke: rgb(112, 112, 112);
  stroke-width: 0.5px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}
.Path_1 {
  overflow: visible;
  position: absolute;
  top: 16.5px;
  left: 1px;
  width: 183.5px;
  height: 0.5px;
  transform: matrix(1,0,0,1,0,0);
}
<div id="A5">
  <div id="btn_signup">
    <svg class="Rectangle_1">
      <rect id="Rectangle_1" rx="4" ry="4" x="0" y="0" width="185" height="50"></rect>
    </svg>
    <div id="Sign_up">
      <span style="font-family:Helvetica;font-style:normal;font-weight:normal;font-size:18px;color:rgba(77,79,92,1);display:inherit;">Sign up</span>
    </div>
    <svg viewBox="12.5 0 183.5 0.5" class="Path_1">
      <path id="Path_1" d="M 12.5 0 L 196 0"></path>
    </svg>
  </div>
</div>

In Firefox the text is flush to the line. In Safari there is a few pixels gap as shown below:

enter image description here

How do I remove that gap?

Added a bounty. Ask questions if you need more clarity.

Update:
Thanks for all the vertical centering suggetions. Half the cases I have would be fixed by vertical centering but the rest are top alignment cases.

It looks like what I want is to have my text content, the bounding box of the content at the caps height aligned to the top edge of the text line as seen in the picture.

Resources:
Looks like it is a common problem across platforms.
Cap height and x-height metrics are inaccurate
Vertical Metrics

Therewith answered 26/12, 2018 at 7:7 Comment(16)
Maybe a different line-height; try setting it explicitly.Fabri
Setting line height to 100% does work to fix it in Safari but then it causes the text to moved up in other browsers.Therewith
Odd. I can't think of anything else and I don't have Safari here to test with, sorry. Have an upvote instead.Fabri
Thanks. It also works if I set margin-top:-.2em. But again it affects the other browsers.Therewith
If I set vertical-align to different values it moves the text up or down by small amounts but none of the values make it flush.Therewith
@1.21gigawatts did you try using adding this CSS #Sign_up span{ height:300px;} ??. Please notify if it works I will add this as an answer.Keene
To align fonts you need to read its metrics, and here is one possible way: #38242598Interpenetrate
Some more reading: #42026739Interpenetrate
And btw, in my Firefox (and Chrome) there is also a gap, as shown in your Safari imageInterpenetrate
@1.21gigawatts i'm not sure what the problem is, but somone created a file to fix this problem, https://necolas.github.io/normalize.css/. you might try to contact the owner of this file and ask him personally why your problem does what it does and how his file fixes the problem.Thadeus
Sorry, I have not been able to get back to this sooner. Reading through the comments now.Therewith
@LGSon It looks like what I want is to have my text content, the bounding box of the content at the caps height aligned to the top edge of the text line.Therewith
With the resources you found you'll see this is not trivial cross browsers as web browsers doesn't have built-in functions/styles for this (and weren't meant to be pixel perfect either). ... First off, hope you noticed that I wrote I don't have that discrepancy in my Chrome/FF/Edge, it lines up as your Safari does in those, with a gap, still, using hardcoded values to move into place would be to brittle. ... Second, why not use SVG all the way, both for the border, line and text. Am pretty sure that will make it a lot easier to line things up.Interpenetrate
Well, web browsers weren't meant for more than sharing text documents but here we are. Anyway, I'm pro anti-hard coding non dynamic values. One solution would be to add the text of the current font to a canvas, scan the pixel color values and return the y position of the first non transparent pixel. That would allow me to get the caps height in any browser but requires JS. Alternatively, if web browsers were to add an style to optionally verticallyAlignTextToCapsHeight:true would that allow browser makers to enable that behavior without affecting current text rendering positioning?Therewith
Of course, with script, using either a canvas or using this suggestion(which I think is better), and you'll have options. Introducing a new style would be another, whether browser makers can enable that w/o side effect I really can't say (but can't see why not). ... And again, why not use SVG all the way?Interpenetrate
A note, add my alias to your comments so I get notified.Interpenetrate
S
5

See this question and answer: WebKit vs Mozilla vertical alignment of font glyphs in box

Basically, CSS allows you to play with the position of the text elements, but the actual letter positioning vs the element is font and browser dependent. You cannot directly modify this, which means that, in theory, you cannot really be certain of the actual position of the letters themselves.

CSS gives you indirect access, for example with vertical-align, you can align the text with the parent text. So depending on the value, the elements will position with regard to the actual letters. For example:

div {
  font-size: 20px;
  line-height: 1.2;
  border-top: solid lightgray 1px;
  font-family: arial;
}

div span {
  display: inline-block;
  background-color: lightblue;
  border-top: solid red 1px;
}

.topalign span {
  vertical-align: top;
}

.bottomalign span {
  vertical-align: bottom;
}
<div class="topalign">
  <span style="font-family: 'helvetica neue'; ">Helvetica N</span>
  <span style="font-family: 'arial'; font-size: 30px">Arial</span>
  <span style="font-family: 'Times new roman';  ">Times NR</span>
  <span style="font-family: 'Tahoma'; font-size: 15px;">Tahoma</span>
</div>
<div class="bottomalign">
  <span style="font-family: 'helvetica neue'; ">Helvetica N</span>
  <span style="font-family: 'arial'; font-size: 30px">Arial</span>
  <span style="font-family: 'Times new roman';  ">Times NR</span>
  <span style="font-family: 'Tahoma'; font-size: 15px;">Tahoma</span>
</div>

You can also play with line-height, which will change the text element's height without changing the font size, which means you can more or less control where you want the letter to be placed. But again, different font/browsers will render differently. You're still not positioning the letters precisely. For example:

div {
  font-size: 30px;
  line-height: 1.2;
  border-top: solid lightgray 1px;
  font-family: arial;
}

div span {
  display: inline-block;
  background-color: lightblue;
  border-top: solid red 1px;
}

.topalign span {
  vertical-align: top;
}
<div class="topalign">
  <span style="font-family: 'helvetica neue'; ">Helvetica N</span>
  <span style="font-family: 'arial'; line-height: 1.5;">Arial</span>
  <span style="font-family: 'Times new roman';  line-height: 0.5;">Times NR</span>
  <span style="font-family: 'Tahoma'; ">Tahoma</span>
</div>

So in theory, you cannot precisely define the the actual letters positions exactly where you want. It cannot be explicit, and so can lead to inconsistencies across browser.

That being said, the case you point out seems to be a Firefox bug with Helvetica font. The letter positioning of Helvetica isn't consistent with other fonts or other browsers. It is especially obvious when you compare to Helvetica Neue or Arial, which should be more or less the same. See:

div {
  font-size: 30px;
  line-height: 1.2;
  border-top: solid lightgray 1px;
  font-family: arial;
}

div span {
  display: inline-block;
  background-color: lightblue;
  border-top: solid red 1px;
}

.topalign span {
  vertical-align: top;
}
<div class="topalign">
  <span style="font-family: 'helvetica';  ">Helvetica </span>
  <span style="font-family: 'helvetica neue'; ">Helvetica N</span>
  <span style="font-family: 'arial'; ">Arial</span>
</div>

Even if you cannot explicitly state the letter positioning, you can expect a certain level of consistency nonetheless. Normally, there is a normal and coherent padding that is more or less equal at the top and bottom. It can vary from font to font, and from browser to broswer, but the way helvetica is rendered in Firefox sure seems like a bug (to me at least, maybe there's a reason, but I don't see why).

So if you can replace Helvetica with Helvetica Neue, you can play with line-height to position the letter vs the element and achieve what you want. For example, normally a line-height of about 0.75 will arrive flush with top and bottom of uppercase letters, which means that by positioning the element, you can position the letter. Like this for example:

* {
  margin: 0;
  padding: 0;
}
#btn_signup {
  opacity: 1;
  position: absolute;
  width: 185px;
  height: 50px;
  left: 10px;
  top: 10px;
  overflow: visible;
}
#Rectangle_1 {
  opacity: 1;
  fill: transparent;
  stroke: rgb(67, 66, 93);
  stroke-width: 1px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}
.Rectangle_1 {
  position: absolute;
  overflow: visible;
  width: 185px;
  height: 50px;
  left: 0px;
  top: 0px;
}
#Sign_up {
  opacity: 1;
  position: absolute;
  left: 58px;
  top: 16px;
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  overflow: visible;
  width: 63px;
  white-space: nowrap;
  text-align: center;
}
#Path_1 {
  opacity: 1;
  fill: rgba(0,0,0,0);
  stroke: rgb(112, 112, 112);
  stroke-width: 0.5px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}
.Path_1 {
  overflow: visible;
  position: absolute;
  top: 16.5px;
  left: 1px;
  width: 183.5px;
  height: 0.5px;
  transform: matrix(1,0,0,1,0,0);
}
<div id="A5">
  <div id="btn_signup">
    <svg class="Rectangle_1">
      <rect id="Rectangle_1" rx="4" ry="4" x="0" y="0" width="185" height="50"></rect>
    </svg>
    <div id="Sign_up">
      <span style="font-family:Helvetica neue;font-style:normal;font-weight:normal;font-size:18px;color:rgba(77,79,92,1);display:inherit;line-height: 0.75;">Sign up</span>
    </div>
    <svg viewBox="12.5 0 183.5 0.5" class="Path_1">
      <path id="Path_1" d="M 12.5 0 L 196 0"></path>
    </svg>
  </div>
</div>
Sheath answered 28/12, 2018 at 22:26 Comment(2)
So are you saying we need a new style that ensures that bounding box of the text, at the caps height, aligns to the edge of it's container element to ensure it looks consistent across browsers and fonts? What would you call this new style name?Therewith
Exactly, there doesn't seem to be any tool or standard to precisely position the letters. Part of the rendering relies on the font info, which is not exposed - at least that's my understanding. Maybe some day, something like a glyph-alignment rule could let us align the letters either to top, bottom or middle of the parent element. That would solve the problem you mention. But that would probably cause many other problems...Humus
F
2

Different browsers handle decimal positioning differently. Dealing with fractional pixels isn't in the CSS specifications, so even if everything's positioned correctly now, it could break again after a browser update. If you remove all the fractional pixel values and add line-height:1, it should work.

If that doesn't work, try this and let me know what you see (I don't use Safari or FF). Inspect the span in each browser and the tool should highlight a rectangle. See if the top of the rectangle doesn't touch the line or the top of the text doesn't touch the top of the rectangle. In Chrome, the top of the text doesn't touch the top of the rectangle, so the solution is line-height:1.

Fluoroscope answered 30/12, 2018 at 16:12 Comment(0)
N
1

Keep position: relative; for #btn_signup. Also center the Sign_up using the following CSS

#Sign_up {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
}

It will work same in all browsers.

* {
  margin: 0;
  padding: 0;
}

#btn_signup {
  opacity: 1;
  position: relative;
  width: 185px;
  height: 50px;
  left: 10px;
  top: 10px;
  overflow: visible;
}

#Rectangle_1 {
  opacity: 1;
  fill: transparent;
  stroke: rgb(67, 66, 93);
  stroke-width: 1px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}

.Rectangle_1 {
  position: absolute;
  overflow: visible;
  width: 185px;
  height: 50px;
  left: 0px;
  top: 0px;
}

#Sign_up {
  opacity: 1;
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  overflow: visible;
  width: 63px;
  white-space: nowrap;
  text-align: center;
}

#Path_1 {
  opacity: 1;
  fill: rgba(0, 0, 0, 0);
  stroke: rgb(112, 112, 112);
  stroke-width: 0.5px;
  stroke-linejoin: miter;
  stroke-linecap: butt;
  stroke-miterlimit: 4;
  shape-rendering: auto;
}

.Path_1 {
  overflow: visible;
  position: absolute;
  top: 16.5px;
  left: 1px;
  width: 183.5px;
  height: 0.5px;
  transform: matrix(1, 0, 0, 1, 0, 0);
}
<div id="A5">
  <div id="btn_signup">
    <svg class="Rectangle_1">
      <rect id="Rectangle_1" rx="4" ry="4" x="0" y="0" width="185" height="50"></rect>
    </svg>
    <div id="Sign_up">
      <span style="font-family:Helvetica;font-style:normal;font-weight:normal;font-size:18px;color:rgba(77,79,92,1);display:inherit;">Sign up</span>
    </div>
    <svg viewBox="12.5 0 183.5 0.5" class="Path_1">
      <path id="Path_1" d="M 12.5 0 L 196 0"></path>
    </svg>
  </div>
</div>
Nabala answered 3/1, 2019 at 5:54 Comment(1)
Thanks I use this when I need centering. If the browser is able to center perfectly across browsers than it is aware of the text content bounding box. If they could apply this to allow the text to align flush to it's vertical pixel position it would provide more consistency. If I or someone can draw a font to a canvas and then read the pixel position of the first non transparent vertical pixel someone could manually find out the caps height and then set use that offset to set the vertical position accurately in each browser.Therewith
K
1

Fonts render differently on different browsers. Here's a capture of the same line of text. It was captured on all major browsers and put one on top of each other to illustrate the size of the problem. (Taken from this article)

enter image description here

Different fonts will give different results

When I needed to have the text to be aligned vertically pixel perfect we had a team that made some research. We found that some fonts where better aligned vertically than others - so it's worth testing.

You can alway hack it with JS

If it's safari - change the line height (or margin or whatever)

if (navigator.userAgent.toLowerCase().indexOf('safari') > -1) {
  $('.your-element').css({lineHeight: "21px"});
}
Keating answered 3/1, 2019 at 20:27 Comment(1)
In another comment I suggest drawing the text to a canvas and then reading the position of the first non transparent pixel someone could manually find out the caps height and then set use that offset to set the vertical position accurately in each browser. This would require JavaScript though.Therewith
P
0

Please replace this code in CSS:

#Sign_up {
    opacity: 1;
    box-sizing: border-box;
    overflow: visible;
    white-space: nowrap;
    height: 50px;
    line-height: 50px;
    text-align: center;
    display: block;
}
Prologize answered 4/1, 2019 at 7:50 Comment(1)
Code w/o an explanation what it does is of very low quality.Interpenetrate

© 2022 - 2024 — McMap. All rights reserved.