The fun thing about CSS Media Queries is that there isn’t a whole lot of solid documentation on them. Most engineers are concerned with simply detecting mobile or not. It takes a fair amount of digging, trial and error and wall-punching to wrap your head around queries for specific uses such as Android.
At HotelTonight, we have a native iOS and Android app. Our team is looking to deliver a great user experience by serving up an app with platform specific conventions, familiar UI patterns and gorgeous optimized details. Why should our handling of mobile visitors to our website be any different?
Our products are mobile-only so it makes sense that we get fair amount of people checking us out on mobile. Crazy, right?
Alright back to queries.
As a quick crash-course, CSS media is capable of detecting device-width/height, width/height, pixel density, and orientation. Sounds simple. Don’t worry the fun is just beginning.
At HotelTonight, we are using CSS media queries to serve a platform specific, device proportioned mobile front door to visitors of our website. There are a couple of things that we do that may not work in every case. For instance, the mobile front door is portrait only so we force the device into portrait by default. Although, CSS media is capable of targeting orientation. We also disable zooming and scaling because we have no scrolling, inputs or blocked readable content. This allows us to have more predictable behavior from a device.
The Two Faces of Media Queries
In terms of mobile queries there are actually two tools implied with it. CSS Media and Viewport meta. The viewport meta in the head allows us to give certain instructions to the device browser before it renders and the CSS Media allows us to hand out specific style instructions based on the device attributes. I will be discussing a combination of methods and various cocktails of those two that allow you to achieve different results.
Before we jump into the actual magic queries, there are a number of interesting power struggles going on that are useful to know about.
Device Width vs. Width
The width question within queries is quite tricky. It becomes of central importance to understand because 90% of the time, dimensions are the only way you might have to tell two devices apart. The idea is that device-width returns the actual width of the device. For instance, a 1024 x 768 device should return a device-width of 768. This is actually refering to physical device pixels not the resolution. So, an iPad 3 actually has a screen display of 1536px x 2048px, but it will return a device-width of 768px.
Fun Fact: Most devices return device-width based on orientation. Except iOS devices. They will return the portrait value in both cases.
The width property refers to width of the browser window (the viewport). It seems like a browser on mobile should always be the same width as the screen. However, there are a number of problems with this assumption.The first being that screen density plays a signifcant role, as does the type of browser(Firefox,Safari,Chrome) and my favorite: target density scaling.
Target Density Scaling
The Android ecosystem has a fair amount variety in screen sizes and densities. As a result, the browsers on Android attempt to scale screens up or down to fit nicely on various screen sizes. By default, Android centralizes its efforts around MDPI density. This is typically refered to as ‘overview’ mode. This generally makes sure that width responses are not reliable at all. At the least they are not what you would expect.
To elaborate, in overview mode an Android device will scale the viewport’s width to 800px, which for most mobile optimized layouts ( which are typically 320px ) will result in the layout being far too small for the screen. With some of Android’s fluid approaches you can define the target density or the target width but this is not done in CSS and can only explicitly target static sizes. This is a problem for a layout that must work on iOS , Android and beyond. Due to the target-density scaling, this also means that the viewport width is being scaled proportional to the device resolution and target-density which results in some really goofy reported widths ( such as 603px width on some Android tablets).
You can disable target density scaling via the Viewport meta tag but that makes it harder to detect against multiple densities and therefore, makes our road to multi-platform detection harder.
If you are aiming for less specific queries then this viewport tag will do the trick:
Since media queries render at a strange place within the stylesheet, they generally do not cascade very well. It usually a good idea to use the !important declaration to ensure that overlapping queries know exactly what to show the user. When a query renders, it is reacting to the device and viewport. Therefore, it is not aware of other queries so you have to ensure that style instructions are explicit. For instance, hiding an iOS device image in addition to showing the appropriate Android version.
The first thing that is required is to prime our viewport meta to stop the device browsers from ruining our day right off the bat. This is what we use for HotelTonight’s mobile front door.
user-scalable : This allow us to have a more consistent experience by disabling the user’s ability to pinch zoom and scale the viewport. Since most queries are dependent on the viewport’s reported width, it is ideal to stop it from changing erratically. Use with caution. For instance, if you have input fields, this messes with the UX of scrolling to the input when focused and it also makes tap zooming on blocks of text not work so well.
min/max-scale : This is another property that forces the viewport to not scale or zoom based on the device. To note, iOS is far better at respecting this than Android devices.
maximum-scale : This property is specifically good at stopping the page zoom and stops annoying behavior such as Safari’s infamous zoom on rotate bug. However, if you have a ton of text content to that needs to be accessible, stay away from this one.
width=device-width : This property tells the device to assume the viewport is the same size as the device screen. Its not always completely reliable( usually due to the browser). But, it is essential for targeting Android devices. This is the only way to force them to use the device-width which is more predictable than the viewport width.
Constructing the Query Set
In general, the most reliable differentiator for mobile devices is screen-pixel-ratio (pixel density).
With a query like this we can target a small sub-set of devices which includes iPhone4S, iPhone5, Nexus 4, Nexus 7, etc. Until recently this was a reliable way to target retina iPhones but as high density devices become more popular the query is getting less and less specific. Even so, it gives as fairly laser target for the amount of devices that could potentially see our site.
1 2 3 4 5
Next, we want to knock out some other general assumptions that help us narrow the field of devices that are detected. The easiest thing to do is to detect an average normal density smartphone which will give us a solid amount of coverage in handling minimal devices ( such as many of the Blackberrys and Palms). This also allows us to target the non-retina iPhone and many HTC devices.
1 2 3 4 5
At this point, we are still hitting a large swath of Android smartphones with our query. Part of what we want to do is present the user with 1) a device specific screen and 2) a properly proportioned screen. This means further segementing our approach within Android devices.
We can use a query set like this to target the different device densities. You will notice that MDPI does not specify a property for pixel-ratio. As previously mentioned, the default Android device is trying to scale us into MDPI anwyay, so we just need to make sure to react to a standard MDPI device size. Usually goes without saying but in most cases, it is also good to modify the device-width properties with min and max to make sure our range is more responsive. However, when you are trying to laser target a device ( such as a Nexus 7) sometimes a specific width query is needed.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
Done, right? Nope, the plot thickens with tablets (which are totally included in the mobile classifcation).
Tablets seem straight forward enough but they can also get confusing because they frequently have different resolutons vs. device sizes. Due to the resolution similarities to Android handsets its quite easy to overwrite handset queries with tablet ones.
We can start off easily and segment out iOS tablets. All current iPad versions (including Mini) report the same device properties so its actually fairly painless. Remember Unlike other devices, iOS doesn’t change its reporting based on orientation.
1 2 3 4 5 6
With Android tablets, its also required to look at the different browsers. In the case of the Nexus 7, Firefox and Chrome report their properties differently. Specifically, Chrome likes to report its viewport width differently than its device width, despite the viewport meta.
1 2 3 4 5 6 7 8 9 10 11
This query set works pretty well, but it has one problem. In terms of what the viewport reporting and what the CSS query will see: the iPhone 4S+ and Nexus 4 are the same device. Since they report the same dimensions and screen density. We handled it with a conditional in the mark-up. Basically, a method that checks the user agent. Further, this Android query uses a specific width property to force Nexus tablets out of the scope.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
Why all the CSS Queries?
The styles themselves have been striped from the above examples but a typical style for our CSS query looks like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
There are a lot of highly specific absolute values here. In most cases, this is not a great practice. However, we found that using a flowing layout with wildly different widths, heights and sizes was impractical and worth more headache then simply testing and handing the devices more targeted values. The relative zoom scale adjusts for small variations in devices and we found that density was generally a good guide for expected height within a mobile classification of device types.
We set out to deliver a great looking first web impression to a mobile user where we served them an appropriate call to action and platform. Hell it even works on Blackberry!