Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sub-Pixels calculated and rendered differently among browsers

Tags:

The purpose:

I am working on a code similar to this to create a component where an input field has an embedded button:

http://codepen.io/anon/pen/pgwbWG?editors=110

As you can see, the button is positioned absolutely with top and bottom set to 0, to achieve a 100% height element.

Also to note is that the border of the text-input must stay visible and also wrap the button. To achieve this I added a margin: 1px to the button so that there is (should be) space to display the surrounding text-input red border (usually when the input field content is invalid).

The problem:

is that on Firefox it is (mostly) rendered correctly, while on Chrome (and apparently on the newest Safari) it will have a 1px gap at the bottom of the button.

CSS seems ok but it appears to be a calculation/rounding problem in the rendering, where the bottom or the top margin of the button are not really 1px (can see it inspecting the element). And also the padding of the input seems to influence in that.

At different zoom-rates it will add or remove 1px of margin to the top or the bottom of the button, resulting in a 1px-gap or in a covered-border.

As I set the button margin to 0px then the bottom margin is fixed but I loose the 1px margin on the top, finishing to cover the red border of the text-input.

The examples:

Probably I am not clear or too verbose in explaining it, so here are some screenshots of the bug, from different zooms on Chrome (note the CSS is always the same):

enter image description here enter image description here enter image description here enter image description here

The solution:

I was not able to find a cross-browser solution. How to deal with it and get a consistent component? (no Javascript please)

like image 932
Kamafeather Avatar asked Jan 08 '16 11:01

Kamafeather


People also ask

How many subpixels are in a pixel?

For color displays, each pixel is made up of three subpixels for red, green and blue. Each subpixel can be set to 256 different shades of its color, so it is therefore possible for a single LCD pixel to display 256 ∗ 256 ∗ 256 = 16.8 million different colors.

How do subpixels work?

Subpixel rendering works by increasing the luminance reconstruction points of a color subpixelated screen, such as a liquid crystal display (LCD) or organic light-emitting diode (OLED) display. This thumbnail image is downsized and does not show the technique.


1 Answers

As you already know, the problem arises from a different approach to subpixel calculus between browsers

In Chrome, for instance, borders can have a fractional size, but margins are handled different (as integers).

I don't have documentation about it from the Chrome team, but it's what can be seen in dev tools:

dev tools capture

AFAIK, there is not a way to change that.

Instead, you can transfer the use of the margin in the button to a border.

Since you need to get space for the 1px border of the input, do the same in the button, set a 1px border (instead of a margin), and set it transparent.

The remaining trick is to set the background-clip property to padding box, so that this transparency is not affected by the background

There is another bug in Chrome, the padding expressed in em is not reliable at this level of precision when the browser is zoomed. I changed this in the snippet.

Since we are using the border button to get the dimension ok, we can style the border using instead a inset shadow.

* {
  margin: 0; padding: 0; box-sizing: border-box;
}
button, input, wrapper {
  display: inline-block; border-radius: 3px;
}

.wrapper {
  position: relative;
  
  width: 60%;
  margin: 1em;
  background-color: #ccc;
}

input {
  border: 1px solid red;
  width: 100%;
  
  background-color: limegreen;
  line-height: 3em;
/*  padding: 0.75em; */
  padding: 10px;
}

button {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  border: 1px solid transparent;
  width: 7em;
  margin: 0px;
  background-clip: padding-box;
  box-shadow:  inset 0px 0px 0px 2px  black;
}
<div class="wrapper">
  <input type="text">
  <button>Test</button>
</div>

Another example, where the button has a border. But we need a wrapper around it to get the dimensions ok.

* {
  margin: 0; padding: 0; box-sizing: border-box;
}
button, input, wrapper {
  display: inline-block; border-radius: 3px;
}

.wrapper {
  position: relative;
  
  width: 60%;
  margin: 1em;
  background-color: #ccc;
}

input {
  border: 1px solid red;
  width: 100%;
  
  background-color: limegreen;
  line-height: 3em;
/*  padding: 0.75em; */
  padding: 10px;
}

.buttonwrap {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  border: 1px solid transparent;
  width: 7em;
  margin: 0px;
  background-clip: padding-box;
}
button {
  position: absolute;
  right: 0px;
  top: 0;
  bottom: 0;
  width: 100%;
  border: 2px solid blue;
  margin: 0px;
}
<div class="wrapper">
  <input type="text">
  <div class="buttonwrap">
      <button>Test</button>
  </div>
</div>
like image 78
vals Avatar answered Sep 25 '22 12:09

vals