browserstrangeness / browserstrangeness.github.io

18 stars 2 forks source link

calc() expressions in @media rule (FF59, CR66, SF12) #12

Open WebMechanic opened 4 years ago

WebMechanic commented 4 years ago

So I like using tests for actual standards feature support and then nest simple hacks with @support or @media to target specific engines or brands - or vice versa as it seems fit.

According to its CSS spec:

calc() can be used wherever <length>, <frequency>, <angle>, <time>, <percentage>, <number>, <integer> values are allowed.

@media (min-width:calc(1 * 1px)) {
}

MDN's @media page lists "calc() expressions" support for Blink starting with Chromium 66 (2018-04) and in Safari/12 (2018-09). https://caniuse.com/#search=calc()

Mozilla presumably implemented this when they switched to their shiny new "Quantum CSS" (Servo) in 2017 (FF57+ IIRC). However, Firefox 59 was the first public release to allow this:

It should work with other properties than "min-width" that take numerals as their value, and there's probably some @media combination with a Firefox-only or Chromium-only (-webkit-*) filter to include/exclude supporting browsers.

Usage example:

Leads to a more readable and certainly precise value. Using var() for the values will not work here, presumably as it may cause inheritance issues.

@media (min-width:30rem) and (max-width:calc(48rem - 1px)) {
  /* small screen*/
}
@media (min-width:48rem) and (max-width:calc(64rem - 1px)) {
  /* not so small screen*/
}
WebMechanic commented 4 years ago

As a side "discovery" nested calc() expressions in general are not supported prior Edge/16. On a property level this can be used to override inherited values kinda hack-free.

color: hsl(100, 10%, calc(100% / 2) ); /* Edge lt 17 */
color: hsl(100, 10%, calc(100% - calc( 0.47 * var(--lum))) ); /* Edge 17 up, others */

I don't know what to make of this though.

WebMechanic commented 4 years ago

and... a calc-related parser bug for Firefox 16 - 57. 16 = 1st version supporting standard calc().

Before Firefox 57 calc(123) is not parsed successfully.

Meaning: FF required spaces between operators, so calc(1 * 2 * 3) was fine. I actually ran into this the very first time I played with the calc() only to find "it didn't work" when it should've ... took me a few days to learn of that bug (and forgot about it since).

browserstrangeness commented 4 years ago

I played with calc a while back too with a thought like this -- give it a value (in the buggy format) that is very high in one x or y direction. If the value is parsed then the location would move it to where you want it. You could do it in reverse as well.

An idea might be to have a second opaque 'cover' div of absolute positioning be in a place on [or even off] the screen, and if the calc'd div did what was planned, it would appear behind or next to the cover div.

It doesn't lend itself to a general hack that way since a bug is not introduced in this scenario, but a visual cue for something like 'You are using an old version of Firefox.' would be possible in this fashion.

Colors are like that as well. With javascript it would be possible to detect the final position (or even the final color) after calculation.

browserstrangeness commented 4 years ago

Now back to how you started this -- as you were testing, you try things such as a supports or media query test with calc(123) or a nested calc, then older browsers would bug out and a hack would be possible to rule them out. Did you try that directly yet?

WebMechanic commented 4 years ago

'You are using an old version of Firefox.'

On occasion I did something to this effect for IE, in combination with CC back in the days. And I recall when Chrome (maybe even Safari) had it's own propretary ability to style scrollbars (pre Blink) like IE one had, there was an "enhanced design notice" for non Chromes.

But other than for a friendly "security notice" there's no site I ever made that relied on a specific browser (version); even when Netscape Navigator and DHTML was still a thing. Any flavour gets the content in an accessible manner. I never bought into pixel perfection or must-look-the-same and must-have-same-experience nonsense. I was always able to explain to my clients that people use different software and hardware and they get different (visual) results and experiences for doing do. And that's even more true today.

WebMechanic commented 4 years ago

Now back to how you started this -- as you were testing, you try things such as a supports or media query test with calc(123) or a nested calc, then older browsers would bug out and a hack would be possible to rule them out.

I'm not sure I get what you're asking :)

Did you try that directly yet?

There are usually 2-3 main normal breakpoints for the "rough" layouts that apply to all screens and browsers that match. The ones with calc() act as a filter to wrap new or experimental properties aka progressive enhancement. They're not necessary new breakpoints, just wrappers around other features or even to only use well supported standard syntax instead of a gazillion -prefix-'ed versions of properties.

I have a debug stylesheet that makes excessive use of this method and also declares a bunch of --vars along the way, but that's local only. I use it to find suitable breakpoints for new layouts.

WebMechanic commented 4 years ago

The color example was just that: an example. It occured to me in an inhouse experimental project so no big deal and I'd probably never do such a thing in the wild. I had to build a (static) theme file that depicted a colour palette. Single HSL values were stored as variables in several cascading selectors and style attributes to compute palette swatches (TD cells), and the Edge browser on the client's machine didn't do the math for some of them swatches. That's when I learned it has issues with nested calc() expressions.

browserstrangeness commented 4 years ago

You are right, but there are very rare cases of old browser versions... like a version of chrome (in the 30's) that did not do audio. They went back and fixed it shortly with the next version. It only matters when one is of course applying coding tasks -- usually, and even in this example, just making viewers get the latest version does of course suffice.

What I meant was, did you try an @supports () { } test or an @media () {} hack test for any of these options?

Those are actually valid hack foundations which would just need tweaking to rule out specific browsers after seeing which ones actually respond to the '(abc)' version etc...

WebMechanic commented 4 years ago

What I meant was, did you try an @supports () { } test or an @media () {} hack test for any of these options?

I have a Firefox/45 portable version that doesn't do calc() inside @media.

The calc(no-spaces) bug is something I ran into years ago. I still add them out of habit and because my IDE gives me squiggly lines if I don't :) The quote was from MDN's compatibility table. I tried to replicate this in FF/45 but there appears no issue with width:calc(100*4*1px). It results in a 400px wide element. But that equation includes and requires a unit. I don't know if this MDN comment was actually a special case, but back then calc() was only supported for a few properties like width. rgb( calc(20 + 20), 100, 10 ) didn't work by design because calc() was not supported for things like colour functions or gradient stops iirc. I guess that all happened with FF59.

The usage example is taken from my "breakpoints.css" which has a lot more granular steps in it. Firefox' Style Editor has a media query sidebar that highlights active media queries. If I do new layouts, this gives me an quick glance at what MQ (value range) would be a good candidate. In other browsers or with rulers disabled I can see the variable values assigned as a result of the active MQ in the rules editor. So it's basically "in daily use" but only locally to serve as a tool during early development. I realised that using rem or em might lead to rounding errors. The -1px gives a very precise breakpoint.

@media (min-width:20rem) and (max-width:calc(30rem - 1px)) { :root {--screen-rem:20rem} }
@media (min-width:30rem) and (max-width:calc(36rem - 1px)) { :root {--screen-rem:30rem} }
@media (min-width:36rem) and  ...
/* and a buttload more like this up to 120rem */

@media (min-width:320px) and (max-width:calc(360px - 1px)) { :root {--screen-px:320px} }
@media (min-width:360px) and (max-width:calc(400px - 1px)) { :root {--screen-px:360px} }
@media (min-width:400px) and ...

For the pixel blocks it's just lazy editing to copy the even value between two neighboring queries instead of manually subtracting 1 from max-width.

Ther's no @supports rule I used in combination with @media+calc() specifically. If I had to I'd do it as usual and wrap one into the other, depending on what property or feature has higher significance for the layout, ie. @supports(foo:bar) would wrap a set of media+calc().

browserstrangeness commented 4 years ago

You don't actually need all of them to make a hack though. Making a min-width (that calculates to 1 or less) or a max-width that ends up at least 100% after calculation would allow it to affect any page.

Any browser that doesn't do it would of course not inject any CSS code. Granted it can be harder (and sometimes not all that possible if the media query is too specific in its available options) than testing supports features, which of course were designed for such testing.

browserstrangeness commented 4 years ago

Once you have a general hack then you would be able to effect any measurements you like, or add browser specific code in combination.

WebMechanic commented 4 years ago

They're not used as hacks. The aim of all these media rules in "breakpoints.css" is not to tackle anything but to be highlighted in Firefox' style editor (which I often prefer over Chrome's) for whatever the current window range fits in and to set the variable that'll be visible when editing/tweaking individual rules in FF or CR.

They're a visual aid like the rulers you can enable in the devtool settings, but here I get a more useful readout in rem. I can then click on it, copy the line into the actual stylesheet and eventually turn it into the MQ I need; usually w/o the calc() part though.

It's funny how FF changes the rules from calc(64rem - 1px) into calc(-1px + 64rem):

image

browserstrangeness commented 4 years ago

Now if you check this page, there are a few very nice examples with stats on them: https://stackoverflow.com/a/61140118/2246213

browserstrangeness commented 4 years ago

Saw your post, but yes I see that. The way to use them as hacks is simply to find out which versions of browsers are affected and attempt to rule out older ones. That's what I meant by that. I do enjoy reading your postings by the way.

browserstrangeness commented 4 years ago

I made a ton of updates on the browserstrangeness page. Added a few things too.

WebMechanic commented 4 years ago

I do enjoy reading your postings by the way.

thanks, I hope it's not only for my funny English 😇

browserstrangeness commented 4 years ago

haha no, that's just a bonus ;) (just kidding, you write well.)