How to Achieve True 1px Borders on High‑DPI Mobile Screens
This article explains why 1 px borders appear thick on high‑resolution mobile devices, explores the role of devicePixelRatio, and presents five practical solutions—including iOS 0.5 px borders, border‑image, box‑shadow, pseudo‑elements, and viewport scaling—along with their pros, cons, and common pitfalls.
Introduction
Mobile web projects are increasing, and designers demand UI elements such as 1 px borders. On high‑resolution screens a CSS 1 px becomes thick, so a “fake” 1 px is often used.
This is a real 1 px border.
1. Cause
The issue is caused by the devicePixelRatio (DPR), which is the ratio of physical pixels to CSS pixels.
window.devicePixelRatio = physicalPixels / CSSPixelsMost screens have DPR = 2 (e.g., iPhone 8) or DPR = 3 (e.g., iPhone 8 Plus). To draw a physical 1 px on a DPR = 2 screen you need 0.5 CSS px, etc. iOS 8+ supports 0.5 px, while Android does not.
2. Solutions
1. WWDC iOS solution
WWDC recommended using a 0.5 px border, which renders as a physical pixel on iOS. border: 0.5px solid #E5E5E5; Chrome on iPhone 8 Plus does not render below 0.46 px.
Pros: simple, no side effects
Cons: works only on iOS 8+, not Android
2. Border image
Use a 1 px transparent border with a border‑image.
border: 1px solid transparent;
border-image: url('./../../image/96.jpg') 2 repeat;The image can be custom‑made.
Pros: simple, no side effects
Cons: need to recreate the image when the color changes; rounded corners become blurry
3. Box‑shadow
Use box-shadow to draw thin lines.
box-shadow: 0 -1px 1px -1px #e5e5e5, /* top */
1px 0 1px -1px #e5e5e5, /* right */
0 1px 1px -1px #e5e5e5, /* bottom */
-1px 0 1px -1px #e5e5e5; /* left */The first two values control which side, the next two control blur and spread.
Pros: easy, works with rounded corners
Cons: looks like a shadow rather than a border
4. Pseudo‑element
Most used method: create a ::after pseudo‑element and scale it.
.setOnePx{
position: relative;
&::after{
position: absolute;
content: '';
background-color: #e5e5e5;
display: block;
width: 100%;
height: 1px;
transform: scale(1, 0.5);
top: 0;
left: 0;
}
}Scaling the Y axis by 0.5 creates a true 1 px line.
For a border on all sides:
.setBorderAll{
position: relative;
&::after{
content: " ";
position: absolute;
top: 0;
left: 0;
width: 200%;
height: 200%;
transform: scale(0.5);
transform-origin: left top;
box-sizing: border-box;
border: 1px solid #E5E5E5;
border-radius: 4px;
}
}Pros: works on all devices, true 1 px, supports rounded corners
Cons: uses ::after, which may affect float clearing
5. Viewport scale + rem + JS
Adjust the viewport scale according to DPR and set the root font size.
<html>
<head>
<title>1px question</title>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no">
<style>
html{font-size:1px;}
*{padding:0;margin:0;}
.top_b{border-bottom:1px solid #E5E5E5;}
.a,.b{box-sizing:border-box;margin-top:1rem;padding:1rem;font-size:1.4rem;}
.a{width:100%;}
.b{background:#f5f5f5;width:100%;}
</style>
<script>
var viewport=document.querySelector('meta[name=viewport]');
if(window.devicePixelRatio==1){viewport.setAttribute('content','width=device-width,initial-scale=1,maximum-scale=1,minimum-scale=1,user-scalable=no');}
if(window.devicePixelRatio==2){viewport.setAttribute('content','width=device-width,initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,user-scalable=no');}
if(window.devicePixelRatio==3){viewport.setAttribute('content','width=device-width,initial-scale=0.3333333333,maximum-scale=0.3333333333,minimum-scale=0.3333333333,user-scalable=no');}
var docEl=document.documentElement;
var fontsize=32*(docEl.clientWidth/750)+'px';
docEl.style.fontSize=fontsize;
</script>
</head>
<body>
<div class="top_b a">Bottom border width is virtual 1px</div>
<div class="b">Top border width is virtual 1px</div>
</body>
</html>Pros: works on all devices, simple 1px usage
Cons: suitable for new projects; legacy projects may require extensive changes
3. Pitfalls
Various issues were encountered, especially when using pseudo‑elements inside pseudo‑classes and with elements that do not support pseudo‑elements (e.g., textarea).
For a vertical line set width:1px; height:100%; and scaleX(0.5); for a horizontal line set width:100%; height:1px; and scaleY(0.5).
4. Conclusion
For new projects the viewport‑scale method is recommended because of its compatibility and ease of use. For existing projects the pseudo‑element + transform method is the most practical. Border‑image and box‑shadow methods are less flexible and have poorer compatibility.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
MaoDou Frontend Team
Open-source, innovative, collaborative, win‑win – sharing frontend tech and shaping its future.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
