When using rem units in Google Chrome (or firefox), the result is unexpected. I'v set up a page with the root font-size set to 10px to make it easier the translate a pixel based design to html/css.
This is my css. My expectation would be a page with one 500px wide box and if the screen is wider than 500px the background should turn red.
html {
font-size: 10px;
}
#media screen and (min-width: 50rem){
body{
background-color: red;
}
}
.test{
width: 50rem;
background: black;
height:100px;
}
But, despite both values being defined as 50rem, the results is a 500px wide box with the page turning red at 800px.
https://jsfiddle.net/tstruyf/puqpwpfj/4/
What am I doing wrong or why is this?
It's meant to work like this, it's just a little confusing if you don't know what's going on.
If you're not using rem in media query declarations then they are based off the root html base font size. If you don't declare this, then in most modern web browsers it's 16px.
As you have declared it as 10px a rem will be 10px throughout your code. Unlike em units, where it is based on the closest parent declaration size.
The confusion comes in that media queries declarations do not base themselves on the declared font-size that you apply to html and instead always use the default size - which as I said is 16px in pretty much all browsers.
That's why 50rem is coming out as 800px - 16px * 50.
Note, this is only for the declaration of the media query breakpoint, if you assign something to be 1rem tall inside the media query, then it will base itself on the base html size.
html {
font-size: 10px;
}
#media screen and (min-width: 50rem){ // 800px (uses base font-size)
div.somediv {
width: 50rem; // 500px (uses the declared html font-size)
}
}
You're not doing anything wrong, this is how rem in media queries are meant to work. As per the spec:
Relative units in media queries are based on the initial value, which
means that units are never based on results of declarations. For
example, in HTML, the em unit is relative to the initial value of
font-size, defined by the user agent or the user’s preferences, not
any styling on the page.
So any styling you apply to the base font size doesn't have an effect on the media query calculation.
Here's the relevant part of the spec.
If you really want to use rem and have them use a font-size basis of 10px, you could always write a SASS mixin to do the conversion for you. Other than that, it might be less confusing to stick with px for media queries.
The size of 1rem in media queries is not affected by changes to the base font size. But you could calculate the correct breakpoint yourselves. You can calculate the breakpoint in pixels based on your new base font size and the desired size in rems.
pixels = desired-rem-value * current-base-font-size
For example for a base font size of 18px and a breakpoint at 20 "rem":
20 * 18px = 360px
There are a few options how to calculate the pixel value:
1. calculate by hand:
html {
font-size: 18px;
}
/* For a breakpoint at 20 times your base font size*/
#media(min-width: 360px) {
...
}
2. Use calc():
#media(min-width: calc(20 * 18px)) {
...
}
3. Use a CSS prepocessor function:
(For example in scss or something similar.)
$base-font-size: 18px;
html {
font-size: $base-font-size;
}
#function media-rem($rem) {
#return $rem * $base-font-size; // $rem must be unitless
}
#media(min-width: media-rem(20)) {
...
}
Based on the rem CSS Specification:
When used outside the context of an element (such as in media
queries), these units refer to the computed font metrics corresponding
to the initial values of the font property.
So, in this case, which media query, rem refers to the initial values of the font property (Default browser's font-size = 16px).
Related
Let's say that I have default font size set to 16px (this should be redundant since it's default in most browsers):
For brevity, my site contains some box with size 10em x 10em.
body, html
{
font-size: 16px;
}
.box {
border: 1px green solid;
background-color:green;
width:10em;
height:10em;
}
My site is already responsive with min-width media queries for specific breakpoints targetted at em values (36, 62 and 85em).
The only thing my site does is change width and color of the box.
#media (min-width:36em) {
.box {
width: 36em;
border:1px red solid;
background-color:red;
}
}
#media (min-width:62em) {
.box {
width: 62em;
border:1px blue solid;
background-color:blue;
}
}
#media (min-width:85em) {
.box {
width: 85em;
border:1px orange solid;
background-color:orange;
}
}
Now let's say that I have some device with resolution 1440x900 where browsers decide that the device's pixel density is 2dppx by default. As result, everything looks much bigger than it should be. And I don't like it.
But the solution should be easy, right? Just add a media query at start for 2dppx pixel density and since it's twice as much pixels, I just reduce the font size by half...that should work, right?
And since I use "em" on everything then by changing the font-size by half I automatically reduce size of everything "em"-sized by half...right?
#media ( min-resolution: 2dppx) {
body, html {
font-size: 50%;
}
}
relevant jsFiddle
Well I found it does not work as I thought it would...I tried to google answers but did not find anything relevant, I suspect I misunderstand how font-sizes, ems and pixel density works...
Reducing font-size by 50% reduces the font-size by much more than by half. The original size is 16px but font-size: 50%; results in a font much smaller than font-size:8px ... Tested in Firefox and Chrome. Does anyone know why is that?
When I set font-size:8px; in the media query for 2dppx then media queries for min-width:36em, 62em and 85em do not trigger as I would have expected, at least when I change layout.css.devPixelsPerPx in Firefox. The min-width in media query behaves like the font-size is still at 16px eventhough it's changed with the dppx media query. Tested in Firefox and Chrome. Does anyone know why is that?
UPDATE
Looks like media queries are loaded in 1dppx context, it does not matter if you use "em", it seems they are cached on load and values such as min-width are fixed to the first value and eventhough you change the base font-size in different media query, other queries don't refresh.
The only solution I can think of right now is to multiply all media queries. One media query for px value in 1 dppx context and another media query for px value in 2 dppx context (which is 50% of the first value).
Thanks to Mr Lister in comments, I found out that this:
#media ( min-resolution: 2dppx) {
body, html {
font-size: 50%;
}
}
...is not what I intended to do. The 50% rule works for <html> tag AND for <body> tag. The resulting font size is not 8px in body, but 4px, because 50% in html is 8px and another 50% in nested body tag results in 4px.
What I intended to do was this:
#media ( min-resolution: 2dppx) {
html {
font-size: 50%;
}
}
Font-size issue is solved.
For the media query: I found out that sizes in #media queries ignore pixel density but sizes in the CSS rule body do not. Since I use "em" sizes and SASS, I can simply replace all #media queries with this mixin (example with 2dppx):
#mixin media-min-width($min-width) {
#media(min-width:$min-width), (min-width: $min-width / 2) and (min-resolution: 2dppx) {
#content;
}
}
When I tried to use em units with media queries I noticed that the unit is based on browser's default font size, not on the html root.
It working odd when I'm gonna to set 20em for element width when breakpoint is min-width: 20em. Both units in this case ar not equal because the element's 20em is based on the html font-size and media query is based on the default browser font size.
Is there a way to achieve the same size for both using em unit without defining additional, separate variable only for breakpoints (#bp-size: 16.250em)?
html {
font-size: 0.813em; // 13px (assume default browser font-size: 16px)
}
.box {
width: 1em; // 13px x 13px
height: 1em;
background-color: #000;
// Problem
#size: 20em;
#media screen and (min-width: #size) { // 320px (20 x 16px) why not 260px?
width: #size; // 260px (20 x 13px)
background-color: #f00;
}
}
<div class="box"></div>
I asked this question not long after yours. I finally found the definitive answer from the html-gods buried deep in the Media Queries page.
Relative units in media queries are based on the initial value, which means that units are never based on results of declarations. For example, in HTML, the ‘em’ unit is relative to the initial value of ‘font-size’.
It doesn't matter what you set in your html, media queries will always be set on the browsers initial font-size attribute.
This is very possible -- What you're looking for is rem rather than em:
#size: 20rem;
rem is relative to the root element, whereas em is relative to the current element. See W3 and TutsPlus for further information in this regard.
Hope this helps! :)
I've created a responsive site and want to use em's to control the font size.
I've set the body font base size to 64.5% as recommended to create a base of 10px.
However, though the sizing seems OK, it does not change with the size of the browser. What am I missing?
http://jsfiddle.net/XaUz9/
See if this helps you: http://jsfiddle.net/panchroma/qK8j2/
In your example, you have explicity set the font sizes and there's nothing instructing the fonts to scale as the viewpont changes.
Setting
body{ font-size:62.5%; }
simply sets the font size (relative to the browsers default setting), it won't result in any scaling.
A couple of ways you achieve what you want is to either set sizes for h1, h2, p etc as the viepoint changes using #media queries , eg
#media (max-width: 480px) {
h1 {font-size: 2em }
h2 {font-size: 1em}
p{font-size:1em}
}
or you could set a default body font size at different viewpoints and the font sizes will scale relative to the setings you have at the head of your CSS file, eg
#media (min-width:481px) and (max-width: 767px) {
body{
font-size:90%;
}
}
The cascade is what makes CSS special and powerful. But in the case of media queries, overlap can seem problematic.
Consider the following CSS (continuing rules for CSS media query overlap):
/* Standard - for all screens below 20em */
body { color: black; font-size: 1em; }
/* Query A - slightly wider, mobile viewport */
#media (min-width: 20em) and (max-width: 45em) {
body { color: red; } /* supposed to be unique for this width */
}
/* Query B - everything else */
#media (min-width: 45em) {
body { font-size: larger; } /* because viewport is bigger */
}
So when the screen is exactly 45em wide, the overlap at 45em will be treated according to the standard CSS cascade:
All max-width: 45em definitions will be applied first,
and all min-width: 45em will be applied thereafter.
Consider these two conditions:
All text would normally be black, but Query A is unique and has color: red.
Since Query B is for larger viewports, it's text has the CSS font-size: larger.
Therefore, at a width of exactly 45em, we'd get big and red text. What would be the best solution to avoid this?
I see two possibilities:
Re-declare the text to have color: black in Query B, but then you're managing two declarations if you choose to change the color in the future. (Of course, not such a problem with this single line of code, but imagine there's a lot of other declarations and selectors.)
Avoid overlap by using pixel values like max-width: 799px and min-width: 800px, but then you're using pixels — I guess they could be 49.9375em and 50em, respectively. Though what if the default is no longer 16em and something gets rounded? And we're still not certain what happens at that gap. (A black hole that breaks the space-time continuum?)
Both have their drawbacks... any other ideas?
The only reliable way to create two mutually exclusive #media blocks for any given media query is to use not to negate it in one of the blocks. Unfortunately, this means repeating your media query once for each #media block. So, instead of this for example:
#media (max-width: 49.9375em) {
body {
color: red;
}
}
#media (min-width: 50em) {
body {
font-size: larger;
}
}
You would have this:
/*
* Note: Media Queries 4 still requires 'not' to be followed by a
* media type (e.g. 'all' or 'screen') for reasons I cannot comprehend.
*/
#media not all and (min-width: 50em) {
body {
color: red;
}
}
#media (min-width: 50em) {
body {
font-size: larger;
}
}
Interactive jsFiddle demo
This is very effective at closing the gap with range media features like width and height since it essentially turns this into an either-or scenario. But, like your first two options, it isn't perfect: as mentioned, you have to repeat the same media query twice, and add not to one of them. There is no if/else construct for #media as described in Conditional Rules 3.
Although I mention this in my answer to your previous question:
From my experiments it would seem Safari on iOS rounds all fractional pixel values to ensure that either one of max-width: 799px and min-width: 800px will match, even if the viewport is really 799.5px (which apparently matches the former).
It should be noted, still, that I've noticed some quirks when it comes to rounding. That said, I haven't been able to find a fractional value that would evade both media queries and end up not receiving styles from either set of rules (which, by the way, is the worst that can happen, so don't worry about potentially creating a space-time rift). That must mean browsers — at least, Safari as I've tested — do a reasonable job of ensuring they satisfy media queries even if you have values that differ (by exactly 1 CSS pixel).
When it comes to units with larger gaps that can be observed on desktop browsers, though, like ems, there is a much larger margin of error. For example, one comment suggests using 49.99999em instead of something more arbitrary than 49.9375em, but apparently there is a difference, at least with a default font size of 16px.
I simplified your code, changed the media queries to use decimal values, and put the code in jsFiddle:
#media (max-width: 49.9375em) {
body {
color: red;
}
}
#media (min-width: 50em) {
body {
font-size: larger;
}
}
If you resize the Result pane to exactly 800 pixels (the text will update to guide you along), you actually end up with different results depending on whether #media (max-width: 49.9375em) is used, or #media (max-width: 49.99999em) is used (I was surprised by this too)...
Either way, you're right: option 2 has its drawbacks too. I'm not particularly fond of it, to be honest, because I wouldn't want to crack my head over device and user agent quirks which are out of my control. If you're like me, I suppose it would be better to go through the inconvenience of redeclaring your rules at the cost (?) of being more vigilant around your code, as that's at least still within your control as an author.
For me, the best way is to keep a gap of 0.01em:
#media (min-width: 20em) and (max-width: 44.99em) {
body { color: red; } /* supposed to be unique for this width */
}
#media (min-width: 45em) {
body { font-size: larger; } /* because viewport is bigger */
}
I recommend you to read this article for the details and the comparison of the different solutions to prevent media query overlapping.
Cheers,
Thomas.
Im reading about optimal font sizing and layout sizing...and Im looking into em instead of px.
From what I understand, if I make the css like this;
html {
font-size: 16px;
}
body {
font-size: 1em;
}
It will force the browser to make 16px = 1em, and that will enable me to do width and height properties by calculating desired pixels/16, right?
Almost all browsers gave their default font size as 16px.
So if you simply set font-size:100% on your body tag then work from that you'll be golden.
Here's a good site that I use for calculating font-sizes from your base size:
http://riddle.pl/emcalc/
In the settings tabs simply change it to 16px and that's you set.
You are correct, and so is #Billy Moat.
You're not really gaining anything by explicitly declaring the 16px value on the HTML element - browsers tend to do that anyway.
Another trick that you can use is the "62.5%" trick.
If you declare:
body { font-size: 62.5% }
You can make further declarations in em's that map neatly to pixel measurements. e.g.:
h1 { font-size: 3em; /* equals 30px */ }
h2 { font-size: 2.4em; /* equals 24px */ }
.nav { width: 50.5em /* equals 505px */ }
That's because 10/16 = .625. So with this trick, you can rebase your measurements and not have to do the math later of dealing with a 16px base.
The only trick of this method is that once you declare a font size for an element, all children elements have to have their em values based on that parent's value (this is true of all relative units of measurement).