CSS Pixels vs Physical Pixels

1. Overview

1.1 Physical Pixels ( Display Resolution )

Display Physical Resolution CSS Pixels (Approx) devicePixelRatio Scaling
MacBook Retina 2560 × 1600 1280 × 800/1440 × 900 2 Yes
External 2470W 1920 × 1080 1920 × 1080 1 No
bash 复制代码
% system_profiler SPDisplaysDataType

Graphics/Displays:

    Apple M1:

      Chipset Model: Apple M1
      Type: GPU
      Bus: Built-In
      Total Number of Cores: 7
      Vendor: Apple (0x106b)
      Metal Support: Metal 3
      Displays:
        Color LCD:
          Display Type: Built-In Retina LCD
          Resolution: 2560 x 1600 Retina
          Main Display: Yes
          Mirror: Off
          Online: Yes
          Automatically Adjust Brightness: Yes
          Connection Type: Internal
        2470W:
          Resolution: 1920 x 1080 (1080p FHD - Full High Definition)
          UI Looks like: 1920 x 1080 @ 60.00Hz
          Mirror: Off
          Online: Yes
          Rotation: Supported

1.2 CSS Pixels and devicePixelRatio ( What Browser See)

bash 复制代码
# Extended Display
window.innerHeight
992
window.innerWidth
1288
window.outerHeight
992
window.outerWidth
1920
window.devicePixelRatio
1

# Built-in Display (Macbook)
window.innerHeight
812
window.outerHeight
812
window.innerWidth
783
window.outerWidth
1440
window.devicePixelRatio
2


screen.width 
1920
screen.height
1080
screen.availHeight
1080
screen.availWidth
1920


screen.width 
1440
screen.height
900
screen.availHeight
900
screen.availWidth
1440

1.3 CMD+SHIFT+5 take screenshot

Dimensions: 1920 × 1080

Dimensions: 2880 × 1800

Why Use 1280 × 800 in Demos?

  • Baseline Reference: 1280 × 800 is the default scaled resolution macOS uses for Retina displays to balance clarity and layout space.

  • Design Compatibility: Many designers use it as a safe minimum for responsive layouts.

  • Predictable Ratio : It maps cleanly to 2560 × 1600 with a devicePixelRatio of 2.

But for real-world dev work , especially when you're optimizing layouts or srcset breakpoints, using your actual CSS pixel dimensions (1440 × 812) is far more accurate.

Correct Breakdown for 13" MacBook Pro Retina

Attribute Value
Physical Resolution 2560 × 1600 px
Default CSS Resolution 1280 × 800 CSS px (scaled)
devicePixelRatio 2
Effective Layout Space ~1440 × 812 CSS px (browser window)

So when you're seeing 1440 × 812 , it's likely because your display is set to a scaled resolution of 1440 × 900, which gives you more screen real estate while still rendering at Retina sharpness.

2. CSS Pixels vs Physical Pixels: visual comparison

bash 复制代码
╔══════════════════════════════════════════════════════════════════════════╗
║                            MacBook Retina Display                       ║
║                          Native: 2560 × 1600 px                         ║
║                          devicePixelRatio: 2                            ║
╚══════════════════════════════════════════════════════════════════════════╝

CSS Pixel Grid (Logical Layout Space):
┌────────────────────────────────────────────┐
│                                            │
│         1280 CSS px × 800 CSS px           │
│                                            │
└────────────────────────────────────────────┘

Each CSS pixel = 2 × 2 physical pixels
→ Rendered using 2560 × 1600 physical pixels
→ Crisp visuals, Retina sharpness

Example:
<img width="100"> → occupies 100 CSS px → rendered as 200 physical px wide


╔══════════════════════════════════════════════════════════════════════════╗
║                          External Display: 2470W                        ║
║                          Native: 1920 × 1080 px                         ║
║                          devicePixelRatio: 1                            ║
╚══════════════════════════════════════════════════════════════════════════╝

CSS Pixel Grid:
┌────────────────────────────────────────────┐
│                                            │
│         1920 CSS px × 1080 CSS px          │
│                                            │
└────────────────────────────────────────────┘

Each CSS pixel = 1 × 1 physical pixel
→ No scaling
→ Standard sharpness

Example:
<img width="100"> → occupies 100 CSS px → rendered as 100 physical px wide

📐 Retina Display Mapping: 13" vs 13.3"

MacBook Model Physical Resolution Common Scaled CSS Resolution devicePixelRatio Notes
13-inch Retina 2560 × 1600 1280 × 800 2 Classic Retina scaling
13.3-inch Retina 2880 × 1624 1440 × 812 (approx) 2 Newer MacBooks (e.g. M1/M2 Air)

🧠 How CSS Pixels Map Cleanly

  • 1280 × 800 CSS px → 2560 × 1600 physical px (2× scaling)

  • 1440 × 812 CSS px → 2880 × 1624 physical px (2× scaling)

This clean 2× mapping ensures:

  • Crisp rendering of text and UI

  • Predictable layout space for web design

  • Retina-quality visuals without distortion

🎯 Design Implications

  • When designing for Retina, always think in CSS pixels.

  • Use devicePixelRatio to serve high-res images via srcset or <picture>.

  • Test layouts at 1280px and 1440px breakpoints to ensure compatibility across both Retina classes.

3. How devicePixelRatio Is Calculated

1. Definition

devicePixelRatio is the ratio of physical pixels to CSS pixels on a display:

js

复制代码
devicePixelRatio = physical pixels / CSS pixels

For example:

  • A standard display with 96 DPI: devicePixelRatio = 1

  • A Retina display with 192 DPI: devicePixelRatio = 2

2. Browser Behavior

  • Browsers use the screen's actual pixel density and default DPI (usually 96) to compute this ratio.

  • They expose it via window.devicePixelRatio, which is a floating-point number (e.g., 1.25, 2, 3).

You can check it with:

js

复制代码
console.log(window.devicePixelRatio);

3. Factors That Influence It

  • Display DPI: Higher DPI means more physical pixels per inch.

  • Zoom Level : Page zooming affects devicePixelRatio because it changes the size of a CSS pixel.

  • Display Switching: Dragging a browser window to a monitor with different DPI can change the value dynamically.

  • Pinch Zooming : Does not affect devicePixelRatio, since it magnifies the page without altering CSS pixel size.

4. Why It Matters

  • It helps browsers render crisp images and text on high-DPI screens.

  • Developers use it to serve higher-resolution assets (e.g., @2x images).

  • It affects canvas rendering, media queries, and responsive design.

4. How Browsers Know the Screen's Pixel Density

1. Operating System APIs

Browsers query the OS for:

  • Physical resolution (e.g., 2880 × 1800)

  • Logical resolution (e.g., 1440 × 900)

  • Screen DPI settings (dots per inch)

These values are provided by the device's graphics subsystem and firmware.

2. Default CSS Pixel Grid

Browsers assume a baseline of 96 DPI for CSS pixels. This means:

  • 1 CSS pixel ≈ 1/96 inch

  • If the screen has 192 DPI, then devicePixelRatio = 2

3. devicePixelRatio Calculation

Browsers compute:

js

复制代码
devicePixelRatio = physical pixels / CSS pixels

This value is exposed via:

js

复制代码
window.devicePixelRatio

It dynamically updates if:

  • You zoom the page

  • You move the window to a different monitor

  • The OS changes display scaling

4. Limitations

  • Browsers don't directly expose physical screen size or DPI in inches.

  • There's no standard JavaScript API for exact physical dimensions (like window.screenDensity), though developers have long requested it.

  • Native apps have more access to this data than web apps.

5. What Is Display Scaling?

Display scaling is a feature in operating systems like Windows and macOS that:

  • Increases the size of interface elements without lowering screen resolution

  • Makes content readable on high-DPI displays (e.g. 4K or Retina)

  • Prevents UI elements from appearing tiny due to high pixel density

📐 Example: Windows Display Scaling

Let's say your laptop has a 3840 × 2160 (4K) screen:

  • Without scaling, everything would look extremely small.

  • Windows might apply 150% scaling, meaning:

    • Text and UI elements are drawn at 1.5× their normal size

    • But the screen still uses the full 4K resolution for sharpness

You can adjust this manually in Settings → Display → Scale and Layout.

🍎 macOS Scaling

macOS uses a similar system but emphasizes 2× Retina scaling:

  • A 5120 × 2880 display might render the UI as if it were 2560 × 1440

  • This keeps everything sharp and readable, while still using the full resolution for rendering

🔍 Why It Affects devicePixelRatio

When the OS applies scaling:

  • The browser updates devicePixelRatio to reflect the new ratio of physical pixels to CSS pixels

  • This ensures web content scales appropriately

6. Why Screenshot Size Exceeds Physical Resolution

🔍 macOS HiDPI Scaling (Retina Mode)

  • macOS renders the UI at a higher virtual resolution than the physical screen.

  • Then it downscales the rendered image to fit the actual display.

  • This improves text sharpness and UI clarity --- especially for Retina displays.

📊 Breakdown of What's Happening

Mode Virtual Resolution Physical Resolution devicePixelRatio Screenshot Size
Retina @ "Looks like 1440 × 900" 2880 × 1800 2560 × 1600 2 2880 × 1800
  • "Looks like 1440 × 900" is a scaled mode.

  • macOS renders everything at that size: 2880 × 1800.

  • Then it downsamples to 2560 × 1600 for display.

  • But screenshots capture the full virtual canvas, not the downsampled output.

🧪 How to Confirm This

You can run this in Safari's DevTools or Chrome:

js

复制代码
window.devicePixelRatio // Should return 2
screen.width            // Should return 1440
screen.height           // Should return 900

Yet your screenshots will be 2880 × 1800 --- confirming the 2× scaling.

7. Python Script: macOS Display Info via pyobjc

Here's the final version of your Retina-aware display diagnostic script. It compares:

  • Logical Resolution: CSS layout space (points)

  • Rendered Resolution: Framebuffer size used by macOS

  • Native Panel Resolution : From system_profiler (accurate for internal displays)

  • EDID Resolution : From ioreg (accurate for external monitors)

  • Physical Size: From Quartz

  • DPI: Based on native panel resolution

python 复制代码
import subprocess
import re
from AppKit import NSScreen
from Quartz import CGDisplayScreenSize, CGMainDisplayID

def get_edid_resolution():
    try:
        ioreg_output = subprocess.check_output(["ioreg", "-l"], text=True)
        edid_blocks = re.findall(r"<([0-9a-fA-F]{256})>", ioreg_output)

        for edid_hex in edid_blocks:
            edid_bytes = bytes.fromhex(edid_hex)
            h_active = edid_bytes[56] + ((edid_bytes[58] & 0xF0) << 4)
            v_active = edid_bytes[59] + ((edid_bytes[61] & 0xF0) << 4)
            if h_active > 0 and v_active > 0:
                return (h_active, v_active)
    except Exception as e:
        print(f"EDID parsing failed: {e}")
    return None

def get_native_resolution_from_system_profiler():
    try:
        output = subprocess.check_output(
            ["system_profiler", "SPDisplaysDataType"],
            text=True
        )
        match = re.search(r"Resolution:\s*(\d+)\s*x\s*(\d+)", output)
        if match:
            return int(match.group(1)), int(match.group(2))
    except Exception as e:
        print(f"system_profiler failed: {e}")
    return None

def get_display_info():
    screen = NSScreen.mainScreen()
    frame = screen.frame()
    scale = screen.backingScaleFactor()

    # Logical resolution
    logical_width = int(frame.size.width)
    logical_height = int(frame.size.height)

    # Rendered resolution
    pixel_frame = screen.convertRectToBacking_(frame)
    rendered_width = int(pixel_frame.size.width)
    rendered_height = int(pixel_frame.size.height)

    # Native panel resolution from system_profiler
    native_resolution = get_native_resolution_from_system_profiler()

    # EDID resolution
    edid_resolution = get_edid_resolution()

    # Physical size
    display_id = CGMainDisplayID()
    size_mm = CGDisplayScreenSize(display_id)
    width_mm = size_mm.width
    height_mm = size_mm.height

    # DPI based on native resolution
    panel_width, panel_height = native_resolution if native_resolution else (rendered_width, rendered_height)
    dpi_x = panel_width / (width_mm / 25.4)
    dpi_y = panel_height / (height_mm / 25.4)

    return {
        "Logical Resolution": f"{logical_width} × {logical_height}",
        "Rendered Resolution (Framebuffer)": f"{rendered_width} × {rendered_height}",
        "Native Panel Resolution (system_profiler)": f"{panel_width} × {panel_height}",
        "EDID Resolution (ioreg)": f"{edid_resolution[0]} × {edid_resolution[1]}" if edid_resolution else "Unavailable",
        "Backing Scale Factor": scale,
        "Physical Size (mm)": f"{width_mm:.2f} × {height_mm:.2f}",
        "DPI (Based on Native Panel)": f"{dpi_x:.2f} × {dpi_y:.2f}"
    }

if __name__ == "__main__":
    info = get_display_info()
    for key, value in info.items():
        print(f"{key}: {value}")
bash 复制代码
% python -m envato.utils.displays
Logical Resolution: 1440 × 900
Rendered Resolution (Framebuffer): 2880 × 1800
Native Panel Resolution (system_profiler): 2560 × 1600
EDID Resolution (ioreg): Unavailable
Backing Scale Factor: 2.0
Physical Size (mm): 286.87 × 179.29
DPI (Based on Native Panel): 226.67 × 226.67
相关推荐
ZzMemory8 分钟前
JavaScript 类数组:披着数组外衣的 “伪装者”?
前端·javascript·面试
梁萌19 分钟前
前端UI组件库
前端·ui
鲸渔23 分钟前
CSS高频属性速查指南
前端·css·css3
小高00723 分钟前
🌐AST(抽象语法树):前端开发的“代码编译器”
前端·javascript·面试
蓝易云24 分钟前
Git stash命令的详细使用说明及案例分析。
前端·git·后端
GIS瞧葩菜26 分钟前
Cesium 中拾取 3DTiles 交点坐标
前端·javascript·cesium
Allen Bright26 分钟前
【JS-7-ajax】AJAX技术:现代Web开发的异步通信核心
前端·javascript·ajax
轻语呢喃31 分钟前
Mock : 没有后端也能玩的虚拟数据
前端·javascript·react.js
Dnui_King35 分钟前
Oracle 在线重定义
java·服务器·前端
西柚啊1 小时前
AI + 可视化:Stagewise 如何让前端 UI 调试效率飞跃
前端·ai编程