css • Responsive media queries and pixels - martindubenet/wed-dev-design GitHub Wiki

Viewport units for relative dimensions

Because mobile phone browsers reserve some height for the GUI of their browser app, when setting the viewport height unit value to 100% { vh: 100%; } makes the element heigher then the actual viewport, displaying a scrollbar on the Y axes.

To fix the issue, use instead the Dynamic Viewport Height unit (dvh).

.example {
  height: 100vh; /* as backup for legacy browsers */
  height: 100dvh;
}

Media queries

CSS properties for responsive layout

From best to worst.

Rating Property Explanations Link
★★★ @media screen A CSS media type property very useful if your HTML is well formated since most CSS are done for screen disply only, when this boolean property is false it is the under lying basic HTML that will render the content. Making it as eazy to read printed documents as if it was a PDF.
★★★ @media print A CSS media type property that is useful to debug style issues on printer devices.
★★★ (min-width/max-width: *px) The basic media query to use, the saffer. It gets the job done.
★★☆ (min-resolution: *dpi) The official W3C approved property that rely on dpi units used to refer to PPI (Pixels Per Inch). ⚠ This property is NOT supported by Safari browsers!
★★☆ (orientation: portrait/landscape) A standard boolean property usefull for mobile devices (only), allowing to fix granular layout issues. The only options are either portrait or landscape.
★★☆ @media(pointer: fine/coarse) This media query allows you to detect whether the device has a precise pointing device (mouse device = fine) or not (touchscreen = coarse), which can influence the user interface design.
★☆☆ (-webkit-min-device-pixel-ratio: *) A non-standard boolean property for both Google Chrome and Apple Safari (mobile and regular) browsers. It rely on absolute values. ⚠ They only reason we still use this property is beacause Safari browsers does NOT support the min-resolution as approved by W3C. So you should always duplicate your values in both (1st) -webkit-min-device-pixel-ratio then cascade down to min-resolution. Test your device⌝
★☆☆ (aspect-ratio: *) Detect mobile device orientation, either horizontal or vertical. It rely on absolute values. ⚠ You should rely on orientation if you only need boolean result.
☆☆☆ (min-device-width: *px) 🚫 Obsolete! Developers should use the min-width media feature instead.

As of the this Screen Resolutions Market Share the common large computer screens size to target would be the 1920 x 1080 of 1080i HDTVs.

Implicitly the mobile first design approach implies the use of minimal values (min-width) ​​in our CSS.

/* PHONES */
@media screen and (min-width: 320px) and (pointer: coarse) { … }
@media screen and (min-width: 600px) and (pointer: coarse) and (orientation: landscape) { … }

/* TABLETS */
@media screen and (min-width: 800px) and (pointer: coarse) and (orientation: portrait) { … }
@media screen and (min-width: 1024px) and (pointer: coarse) and (orientation: landscape) { … }

/* LAPTOPS with touchscreen feature enable */
@media screen and (min-width: 1024px) and (pointer: coarse) { … }
/* LAPTOPS without touchscreen feature */
@media screen and (min-width: 1024px) and (pointer: fine) { … }

/* DESKTOPS */
@media screen and (min-width: 1200px) { … }

/* LARGE DESKTOPS */
@media screen and (min-width: 1800px) { … }

/* PRINTERS */
@media print { … }

Device's display specifications

Test your device's pixel density⌝

Device Resolution (px) Browser viewport (px) PPI Resolution
🖥 UHD 8K 7680 × 4320
🖥 32in Pro XDR Display (Retina 6K) 6016 x 3384
🖥 32in iMac Pro 5K (UHD+) 5120 × 2880 3200 x 1800
🖥 27in UHD 4K LG monitor 3840 x 2160 1860 ~140dpi 1.5
💻 16in Macbook Pro 3072 x 1920 1536 226dpi 2.26
💻 13in Macbook Air 2560 x 1600 1280 113dpi 2
Tablet iPad Air 4 (2020) 1640 x 2360 820 x 1180 264dpi 2
Tablet iPad Mini 1536 x 2048 768 x 1024 324dpi 2
Tablet Pixel C (2015) 2560 x 1800 1280 x 900 308dpi 2
📱iPhone 12 Pro Max 1284 x 2778 428 x 926 458dpi 3
📱iPhone 12 1170 x 2532 390 x 844 460dpi 3
📱iPhone 12 Mini 1080 x 2340 360 x 780 476dpi 3
📱iPhone SE (2020) 750 x 1334 375 x 667 326dpi 2
📱Pixel 4 XL 1440 x 3040 412 x 869 537dpi 3.5
📱Pixel 5 1080 x 2340 393 x 851 432dpi 2.75
📱Pixel 2 - 412 x 732 - 2.625

Television specifications

TV standards Resolution (px) PPI
📺 UHD 4K 3840 x 2160 ~140dpi
📺 HDTV 1080i 1920 x 1080 72dpi
📺 HDTV 720p 1280 x 720 72dpi
📺 TV 480p 640 x 480 72dpi

⚠ Do not rely on resolution values alone!

For displaying contents within web browsers we rely ONLY on the viewport values. Not the screen resolution specifications of the device itself. Because the resolution does not directly reflect the physical size of the screen. A device may have a high resolution but a small screen, making content difficult to read based on resolution alone.

@media (-webkit-device-pixel-ratio)

The -webkit-device-pixel-ratio is a non-standard boolean CSS media feature which is an alternative to the standard resolution media feature.

@media (resolution)

The resolution CSS media feature can be used to test the pixel density of the output device. ⚠ This property is NOT supported by Safari browsers

@media only screen and (-webkit-min-device-pixel-ratio: 2),
       only screen and (min-resolution: 192dpi) { 
  /* 4K/UHD/Retina devices */
}

Breakpoint helper for developers and QA

 

Design for Device

Is mouse device available?

For a public (marketing type) website relying on viewport dimensions is a good enough approach. But for (Saas) web applications with dense dashboard templates where customers always request more details per pixels, a good practice would be to add the possibility to toggle between (Phone/Tablet) touch devices VS mouse devices. The first requiring a 48x48 pixels hitzone for fingers where a smaller 20x20 pixels is good enough for mouse device users.

To differenciate small laptops from large tablets (or laptops with touch screens) it makes sense to identify if the device has a mouse device enabled since this is the real features that differenciate the type of design to propose/support.

See Javascript example based on matchMedia() in this StackOverflow post.

The matchMedia() JavaScript method

The Window interface's matchMedia() method returns a new MediaQueryList object that can then be used to determine if the document matches the media query string, as well as to monitor the document to detect when it matches (or stops matching) that media query.

MDN

Gemini 2.5 Flash prompt:

« Generate the javascript code te get confirmation that the user of my website page as a mouse device available based on (matchMedia('(pointer:fine)').matches) »

Gemini 2.5 Flash code:
/**
 * Checks if the user's primary input device is a "fine" pointer,
 * which typically indicates the presence of a mouse or similar high-precision device.
 *
 * @returns {boolean} True if a fine pointer is detected, false otherwise.
 */
function hasMouseDevice() {
  // The 'pointer: fine' media query evaluates to true if the primary input mechanism
  // of the user is accurate enough to target a small area, like a mouse.
  // 'pointer: coarse' would typically indicate touchscreens.
  // 'pointer: none' would indicate no pointing device.
  return window.matchMedia('(pointer:fine)').matches;
}

// --- Examples of usage: ---

// 1. On page load:
if (hasMouseDevice()) {
  console.log("Mouse device detected: Yes, a fine pointer is available.");
  // You might add a class to the body for CSS adjustments
  document.body.classList.add('has-mouse');
} else {
  console.log("Mouse device detected: No, or a coarse/no pointer is available.");
  document.body.classList.add('no-mouse');
}

// 2. You can also use it to conditionally enable/disable features:
function enableMouseSpecificFeatures() {
  if (hasMouseDevice()) {
    console.log("Enabling features for mouse users (e.g., hover effects, complex drag-and-drop).");
    // Example: Add hover effects (if not handled purely by CSS)
    const hoverElements = document.querySelectorAll('.hover-effect');
    hoverElements.forEach(el => {
      el.addEventListener('mouseenter', () => {
        el.style.backgroundColor = 'lightblue';
      });
      el.addEventListener('mouseleave', () => {
        el.style.backgroundColor = '';
      });
    });
  } else {
    console.log("Disabling/adjusting features for touch/keyboard users.");
    // Example: Provide alternative interaction for touch
    const clickableElements = document.querySelectorAll('.clickable');
    clickableElements.forEach(el => {
      el.addEventListener('click', () => {
        alert("This element was clicked!");
      });
    });
  }
}

// Call this function when the DOM is ready
document.addEventListener('DOMContentLoaded', enableMouseSpecificFeatures);

// 3. Listening for potential changes (less common for 'pointer:fine', but possible if input devices change)
// While 'pointer:fine' is generally static for a session, you can technically listen for changes.
// This is more relevant for dynamic device changes (e.g., connecting a mouse to a tablet).
const mediaQuery = window.matchMedia('(pointer:fine)');

function handlePointerChange(e) {
  if (e.matches) {
    console.log("Pointer changed to fine (mouse detected).");
    // Re-enable mouse-specific features or adjust UI
  } else {
    console.log("Pointer changed to coarse or none (mouse no longer primary).");
    // Disable mouse-specific features or adjust UI
  }
}

// Add the listener
mediaQuery.addEventListener('change', handlePointerChange);

// Initial check for the listener
handlePointerChange(mediaQuery);

Device orientation?

Modern SaaS applications are based on modular components that can be trought within different slots of different templates. Not all components requires a to be adapted accross all breakpoints. Of course using CSS container queries is THE best practice but in real life we don't always have the time required to (re)design on component from scratch based on @container instead of @media queries based on viewport dimensions like we do since years.

So knowing the orientation of a device to style it is a good solution for fixing CSS issues. And, since it is not often that a computer screen is vertically installed, this is also a hint that the user is on a mobile device.

Gemini 2.5 Flash prompt:

« Generate the javascript code of a function that set deviceOrientation as either Landscape, Portrait or Square values based on the comparaison of window.innerWidth and window.innerHeight »

Gemini 2.5 Flash code:
/**
 * Determines the device orientation (Landscape, Portrait, or Square) based on window dimensions.
 *
 * @returns {string} The orientation: 'Landscape', 'Portrait', or 'Square'.
*/
function getDeviceOrientation() {
  const width = window.innerWidth;
  const height = window.innerHeight;

  if (width > height) {
    return 'Landscape';
  } else if (height > width) {
    return 'Portrait';
  } else {
    // width === height
    return 'Square';
  }
}

// --- Examples of usage: ---

// Get the current orientation
let currentOrientation = getDeviceOrientation();
console.log("Current Device Orientation:", currentOrientation);

// You can also listen for changes in orientation (e.g., when the user rotates their device or resizes the browser window)
window.addEventListener('resize', () => {
  const newOrientation = getDeviceOrientation();
  if (newOrientation !== currentOrientation) {
    console.log("Device Orientation Changed to:", newOrientation);
    currentOrientation = newOrientation; // Update the current orientation
  }
});

// Another example: Use it to apply specific CSS classes or logic
function applyOrientationSpecificStyles() {
  const orientation = getDeviceOrientation();
  const body = document.body;

  // Remove any previously set orientation classes
  body.classList.remove('orientation-landscape', 'orientation-portrait', 'orientation-square');

  // Add the new orientation class
  if (orientation === 'Landscape') {
    body.classList.add('orientation-landscape');
  } else if (orientation === 'Portrait') {
    body.classList.add('orientation-portrait');
  } else {
    body.classList.add('orientation-square');
  }

  console.log("Applied style for orientation:", orientation);
}

// Call it initially and on resize
applyOrientationSpecificStyles();
window.addEventListener('resize', applyOrientationSpecificStyles);

References: