For a web application, I need to display small images as a circle and draw a circular gradient-filled border around them using only HTML and CSS. For unknown reasons, some systems reproducibly show misalignment between the image and the border so that they are not concentric. On affected systems, this behavior is visible in both Chrome, Firefox, and Edge, however, the direction of the misalignment is different. Other systems, however, are perfectly fine.
Enlarged screenshot of the misalignment in my web application:
My first thought was, that this might be a subpixel rendering issue but since I am using an even-numbered image size of 24x24px and an even-numbered border width of 2px this seems unlikely. I did some experiments by gradually increasing the image size by 1px and found that the direction and extent of misalignment are inconsistent and sometimes there seems to be an oval distortion. Below you find a reduced code snippet at screenshots from Chrome, Firefox, and Edge. I indicated the direction of misalignment in red. Increasing the border width yielded similar results, but the effect seems most pronounced with 2px.
.rounded-corners-gradient-borders {
box-sizing: border-box;
padding: 2px;
border-radius: 100%;
background: linear-gradient(45deg, #F48ACE 0%, #493A97 100%);
}
<img src="https://i.picsum.photos/id/368/24/24.jpg?hmac=qTESgqsVn81m_y-i5SDjG0xncWcyf-gYC40Jw9amc-k" />
Here is my code (tested on FF, Chrome, Edge): https://codepen.io/grilly17/pen/QWmegPj
function precompensateScaling(value, scale) {
return value / scale;
}
function precompensatePixelFractions(value, scale) {
return value - value * scale % 1 / scale;
}
// wait until page is fully loaded
window.onload = (event) => {
const original = document.getElementById('original');
const oHeight = parseFloat(window.getComputedStyle(original).getPropertyValue('height'));
const oPadding = parseFloat(window.getComputedStyle(original).getPropertyValue('padding'));
const scale = window.devicePixelRatio;
const unscaled = document.getElementById('unscaled');
//unscaled.style.transform = `scale(${1/scale})`; // alternative
unscaled.style.height = `${precompensateScaling(oHeight, scale)}px`;
unscaled.style.padding = `${precompensateScaling(oPadding, scale)}px`;
const adjusted = document.getElementById('adjusted');
adjusted.style.height = `${precompensatePixelFractions(oHeight, scale)}px`;
adjusted.style.padding = `${precompensatePixelFractions(oPadding, scale)}px`;
};
Thank you all for your support. I <3 the Stack Overflow community!