3

I have been working on a project for a while and I thought that using em was the de facto unit when it came to expressing length.

The way I usually proceed is to set the body and html elements to the desired px size and all the other elements to 1em. After that the rest is all in em. Until this morning this seemed like a good plan, until I found out that media queries don't seem to take in account the base body and html px value. They seem in fact to take the browser's default font-size which seems to be a very odd behavior. I also found this article which seems to confirm this.

I have done a small JsFiddle example which shows exactly this behavior:

https://jsfiddle.net/5dq3kq2t/2/

html, body {
    font-size: 20px;
}

* {
  font-size: 1em;
}

#divOf50Em {
  width: 50em;
  border: solid 1px red;
}

#cell2 {
  display: none;
}

@media screen and (min-width: 50em) { /* 1000px, in theory (50 * 20px) */
  #cell1 {
    display: none;
  }

  #cell2 {
    display: table-cell;
  }

}

The experiment is simple, the table by default show the value "1" until the 50em min-width media size has been met. the the value "2" should display. I also placed a div which size is 50em to show when the behavior should be triggered and also to confirm that the CSS works outside of media queries.

If you resize the output of this experiment, you would expect that the shift between the two values would happen at a 1000px value which is 50em * 20px, but it does not, it happens at 800px which is 50em * 16px (the default browsers font-size - if this is your default size of course).

I'm clearly unsure of why this happens but I have two questions:

  1. Is there a way around this (e.g. overwrite default browser font-size from a CSS perspective)
  2. If not, is there a way in JavaScript to access the browser's default font size? I was unable to find this information.

Otherwise it looks like having a script which would be aligned with media queries for some behaviors is impossible when using media width and ems since we cannot rely that the browser's font size will always be 16px.

Then if this is the case, my last question would be, is there a way to align media queries and JavaScript without going back to pixels? I prefer relative units over static pixels, they usually make styling much simpler.

7
  • Not certain what Question is? 1em is equivalent to 16px Commented Oct 8, 2016 at 15:13
  • @guest271314 not if you overwrite it, check my JsFiddle. You can overwrite the 16px from a CSS perspective but also browsers settings perspective. Commented Oct 8, 2016 at 15:15
  • What is the issue? Commented Oct 8, 2016 at 15:23
  • Stylesheets cascade. The latter setting overwrites former setting. That is, you reset, or overwrite previously set 20px at html, body { font-size: 20px; } to 1em at * { font-size: 1em; } Commented Oct 8, 2016 at 15:31
  • @guest271314 The issue is that given html and body are at 20px, everything else should also be at 20px (for 1em). In all stylesheets, the premise is that 1em, from the top level = 20px. Problem is, media queries still think that 1em = 16px. Also, the second problem is that this 16px value seems to come from the browser's stylesheet which means it can change. I'm looking for a way to make this value predictable. Commented Oct 8, 2016 at 15:36

2 Answers 2

1

After more research,

  1. It looks like there is no way around overwriting the 1em value = browser default font-size in pixel either in CSS or JavaScript.
  2. Media queries are referring to the browser, not HTML elements which seems to be the normal/expected behavior.
  3. It is possible to get the browser's default font-size in pixel by using JavaScript which I will not attempt considering it would only be a stop gap.
  4. A true solution would require all media queries to be overwritten by JavaScript which would become quite heavy and hard to scale.
  5. Using other units might solve some challenge but would cause cross-browsers compatibility problems to this day.
Sign up to request clarification or add additional context in comments.

5 Comments

"It is possible to get the browser's default font-size in pixel by using JavaScript which I will not attempt considering it would only be a stop gap." Why would this approach be stop gap?
@guest271314 because of point #4. Even if we have the JavaScript aligned with the media query, media queries will not be reliable because they are based on the browser settings. So technically it could solve one of the problem, but it would be a lot of work to solve only part of a problem which seems more global.
If requirement is to set a unit to 50x browser setting, or setting applied at css, a workaround is to multiply the retrieved value of a property by 50
@guest271314 I'm not sure I'm following you, but let's say we stick with EMs because they are the most supported "dynamic unit", the main problem is to keep the root element's pixel value with the one used in the media queries. Which means, if you change your root element's EM value on a mobile display for example, you need to rewrite all your media queries with the matching EM value. This is what I had in mind as far as "true solution" since this would be cross-browser friendly as well. But I'm not sure its worth all this effort and might not scale well.
You could alternatively use calc() to dynamically adjust property values.
0

A possible workaround is to set the media query at load event of window by getting the font-size of a child node of body using getComputedStyle

:root, body {
    --size: 20px;
    --opt: 50rem;
    --def: 1rem;
    font-size: var(--size);
}

* {
  font-size: var(--def);
}

#divOf50Em {
  width: var(--opt);
  border: solid 1px red;
}

#cell2 {
  display: none;
}

window.onload = function() {
  document.getElementById('documentWidth').innerHTML = 'Window width: ' 
    + document.documentElement.clientWidth + 'px';

  var n = 50;

  var fontSize = parseInt(getComputedStyle(document.getElementById("divOf50Em"))
                 .getPropertyValue("font-size"));

  var css = `@media screen and (min-width:${fontSize * n}px) { 
               /* 1000px, in theory (50 * 20px) */
               #cell1 {
                 display: none;
               }

               #cell2 {
                 display: table-cell;
               }
             }`;

  var style = document.createElement("style");
  style.textContent = css;
  document.head.appendChild(style);

  window.addEventListener('resize', function() {
    document.getElementById('documentWidth').innerHTML = 'Window width: ' +
      document.documentElement.clientWidth + 'px';
  });
}

plnkr http://plnkr.co/edit/lncvWwBWc18C76UYhPnf?p=preview

8 Comments

Very impressive. Do you know if there is a simple way to make this more browser compatible? I know I didn't mention it, so technically you solved the problem but this version could not really be used for a standard website.
@NicolasBouvrette You can substitute var() function for setting properties manually; also replace template literal with string concatenation using + operato; will fork plnkr to demonstrate example
@NicolasBouvrette See plnkr.co/edit/lncvWwBWc18C76UYhPnf?p=preview at version 2 . Version 3 uses inherit value at font-size property at * selector; calc(20 * 50) to set width at #divOf50Em element
Still pretty cool but I can't get it to work in IE yet. I also cannot use REM in my scenario because of browser support which is probably out of scope with this questions. I'm not sure this solution could be usable in my current use case without doing more heavy scripting which could impact rendering and browser performance. That's why I'm not sure it's actually a problem that is worth solving (if we consider the cross-browser challenges).
Have not tried ie in some time. Does ie not support calc()?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.