Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Incremental CSS Media Queries

Consider a stylesheet that resizes the page as the height diminishes using media queries and transform:

@media (max-height: 620px) {
    body {
    transform: scale(0.95);
    }
}
@media (max-height: 590px) {
    body {
    transform: scale(0.90);
    }
}
@media (max-height: 560px) {
    body {
    transform: scale(0.85);
    }
}
// and so on ...

The page "zooms out" as the window height diminishes, allowing the full content to be displayed on smaller screens. If I want to support screens smaller than 560px height, I need to add more media queries.

Notice that for each 30px lost in height, we call scale with 0.05 less in the input.

Question: Is it there a way to define incremental media queries using only css?

Follow Up: If a pure css solution is not available, what would be the simplest way of accomplishing such effect in vanilla JS?

Edit: Thank you all for posting different solutions to this problem. I appreciate your help. Your answers helped me learn how to improve my code.

like image 424
Guilherme Salomé Avatar asked Aug 02 '19 20:08

Guilherme Salomé


People also ask

How do you add multiple media queries in CSS?

You may use as many media queries as you would like in a CSS file. Note that you may use the and operator to require multiple queries to be true, but you have to use the comma (,) as the or operator to separate groups of multiple queries. The not keyword can be used to alter the logic as well.

Do media queries go in CSS?

Using media queries. Media queries are commonly associated with CSS, but they can be used in HTML and JavaScript as well. There are a few ways we can use media queries directly in HTML.

What is the use of @media in CSS?

The @media CSS at-rule can be used to apply part of a style sheet based on the result of one or more media queries. With it, you specify a media query and a block of CSS to apply to the document if and only if the media query matches the device on which the content is being used.

What is @media only screen?

only screen: The only keyword is used to prevent older browsers that do not support media queries with media features from applying the specified styles. Syntax: @media only screen and (max-width: width) Example 2.


2 Answers

This is not possible using CSS alone. You can do this using JS by adding a window.onresize function to watch for resizing and scaling the body of the document. This solution also scales dynamically so you do not need to worry about breakpoints or @media queries.

function updateScale() {
  let viewScale = window.innerHeight / document.documentElement.scrollHeight;
  document.body.style = 'transform:scale(' + viewScale + ')';
  document.documentElement.scrollHeight = window.innerHeight;
}

window.onresize = function() {
  updateScale();
}
updateScale();
body {
  transform-origin: 0 0;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

#block {
  background-color: black;
  width: 300px;
  height: 4000px;
}
<div id='block'></div>
like image 105
Nathan Fries Avatar answered Oct 01 '22 00:10

Nathan Fries


@NathanFries is correct, this isn't something that is possible with native CSS.

CSS includes viewport units for percentage-based values for your width and height, but you can't pass that onto the scale() function.

You'd then need to tie this to some resize event listener.

Here is a quick example that might accomplish what you're looking to do:

// @see https://github.com/WICG/EventListenerOptions/blob/9dcca95/explainer.md#feature-detection
// @example `elem.addEventListener('touchstart', fn, supportsPassive ? { passive: true } : false);`
var checkSupportPassiveEvents = function() {
    var supportsPassive = false;
    try {
        var opts = Object.defineProperty({}, 'passive', {
            get: function() {
                supportsPassive = true;
            },
        });
        window.addEventListener('testPassive', null, opts);
        window.removeEventListener('testPassive', null, opts);
    } catch (e) {}

    return supportsPassive;
};

var supportsPassive = checkSupportPassiveEvents();

var mapRange = function(fn, inStart, inEnd, outStart, outEnd) {
    if (outStart === void 0) {
        outStart = inStart;
        outEnd = inEnd;
        inStart = 0;
        inEnd = 1;
    }

    var inRange = inEnd - inStart,
        outRange = outEnd - outStart;

    return function(val) {
        var original = fn((val - inStart) / inRange);
        return outStart + outRange * original;
    };
};

var linear = function(x) {
    return x;
};

var minHeight = 320;
var maxHeight = 620;
var minScale = 0.45;
var maxScale = 1;
var screenHeightToScaleFactorInner = mapRange(linear, minHeight, maxHeight, minScale, maxScale);

var screenHeightToScaleFactor = function(height) {
    if (height <= minHeight) {
        return minScale;
    } else if (height > maxHeight) {
        return maxScale;
    } else {
        return screenHeightToScaleFactorInner(height);
    }
};

window.addEventListener(
    'resize',
    function(e) {
        var height = this.innerHeight;

        this.document.body.style.transform = 'scale(' + screenHeightToScaleFactor(height) + ')';
    },
    supportsPassive ? { passive: true } : false
);
like image 40
romellem Avatar answered Oct 01 '22 00:10

romellem