We have already
written about the methods (
Mobile First and
Response Web Design ) that we use when developing our service. In this article I want to share with you our experience. What seems simple in theory sometimes turns into a nightmare in practice. We will talk about how we manage to create a universal web service that can work on a large number of devices.
Browser Classes
There are thousands of devices on the market, several platforms and mobile browsers. Let's look at the global prevalence statistics of mobile browsers. The graph shows StatCounter data for the
past year . We were interested in the leaders - Opera, Mobile Safari, Android and Nokia (for all but Opera, the browser runs on the WebKit engine).

')
We borrowed the idea of
browser class support from Yahoo, which defines the following three classes:
- Class C browsers do not get JavaScript and CSS. By supporting this class, we guarantee the receipt of content by any user. In this class, we carried Internet Explorer version 7 and below.
- Class “A” includes the most common browsers that support many standards. In these browsers mandatory testing is carried out. We have included Internet Explorer 8 and 9 and the latest stable versions of Chrome, Safari, Firefox, Opera, Opera Mobile, and Opera Mini 4 and 6 in this class.
- Browsers that are absent in both classes fall into class “X”. These are, as a rule, old versions of browsers from class “A” and their latest unstable versions. Defining a browser in this class, we assume that there will be no serious errors in it. In these browsers we do not test.
Thus, by supporting the “C” class, we guarantee that the content will be received by the user, and he will be able to work with it. This means that without support for displaying images, JavaScript and CSS, the user will be able to navigate the content. This is the basis for class “A” and the entire application.
We used special conditional comments to disable CSS and JavaScript for IE browsers below version 8. Any "correct" browser will ignore what is written in the inside of these comments, and connect all the necessary files with styles and scripts.
<!DOCTYPE html> <html><head> <link type="text/css" ... /> <script ...></script> </head><body> ... </body></html>
Adaptive design
A few words about what the basic requirements were for web design. Firstly, this is “pixel-perfect” when viewing the mobile version of the site on the desktop (that is, it was necessary to adhere to the pixel-by-pixel correspondence of the layout and layout). Secondly, support for touch devices. And, thirdly, the minimum supported screen resolution was 240 pixels wide.
Why is it 240? The fact is that this is the screen width of the Nokia phone on the third-generation S60 platform, released in 2006, and (attention!) With a WebKit browser onboard, that is, with partial support for CSS3 and JavaScript 1.5. It is very difficult to find in the market a mobile phone with a smaller screen resolution, which would be used for Internet surfing.
The rubber layout on which responsive web design is built is not suitable for pixel perfect, among other things, we needed to limit the space of the tape with content. Therefore, specifying the minimum and maximum allowable widths, we got a block with content stretching to certain extreme values.

For small screens, we reduce the indentation values ​​of the main blocks using media queries. And in general, when we begin to develop a block, we determine how it will be displayed on devices with a low resolution of the screen, which elements of the block will be turned off or replaced, and for that using media queries.
It is important to remember that, although the phone is 7 years old with Opera Mini 4 and supports media queries, Internet Explorer 8 does not support them. Therefore, in our project we use media queries for degradation, that is, in the direction of reducing the screen size of devices.
In the tape, we use “floating” images that adjust their width to the width of the parent element, while maintaining the original proportions. Specifying
max-width equal to 100%, you prohibit images to be larger than the original size in the case when the container is wider than the image.

In the image below you can see that the image shrunk to the size of the ribbon, and using media queries, the indents inside the ribbon were reduced and the background, additional layers with shadows and page header elements turned out to be hidden.

Features of mobile browsers
Modern mobile browsers perfectly show pages that are not created for such low resolutions as mobile devices possess. As you must know, they succeed by using logical rather than physical resolution. For example, iPhone by default renders a page in a window 980 pixels wide and shows a smaller version, allowing the user to scale individual sections of the page. When developing a mobile service, this behavior is highly undesirable, so the browser needs to specify the logical window size equal to the device’s physical screen size (the
device-width value in the example below is responsible for this).
<meta name="viewport" content=" width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=0 " />
The
minimum-scale and
maximum-scale parameters define the valid page scaling values. If you set them equal to one, then you prevent the user from changing the scale of the page. For these purposes, there is another parameter -
user-scalable . If you have elements in your design with a property of position equal to fixed, then be sure to set this parameter to 0 in order to activate support for position: fixed in Android (setting the same
minimum-scale and
maximum-scale parameters will not achieve this).
Mobile browsers (especially touch devices) may not support pseudo-classes
:hover :focus and
:active . Therefore, if the design has functionality that you only need to show when you hover the mouse over the parent element, you should leave these elements visible by default. And after loading the page, determining the type of device, hide them using cascades in CSS. For example, as follows:
var touch = 'ontouchstart' in window; $('html').addClass( touch ? 'touch-yes' : 'touch-no' );
.touch-no .item .item__link { display: none; } .touch-no .item:hover .item__link { display: block; }
In addition to determining whether a device is touchy, we use a couple more tests, but to determine whether it is possible to show video content to a user right on the page with messages. To do this, we determine whether the browser supports HTML5 video and the presence of an installed Flash plug-in.
var video = !!document.createElement('video').canPlayType; var flash = (typeof navigator.plugins['Shockwave Flash'] == 'object'); if (!flash && typeof window.ActiveXObject != 'undefined') try { new ActiveXObject('ShockwaveFlash.ShockwaveFlash'); flash = true; } catch (e) {}
It should be noted that the
definition of browser capabilities becomes more preferable than ever to determine the name and version of the browser . After all, we are doing a service that can work on any kind of device.
In mobile browsers, the
scroll event may not come for a window. An example is Opera Mini. It is necessary to take this into account when creating interface elements that actively use
onscroll . For example, loading the next page of a message feed can be done by reaching the bottom of the window. Therefore, you must first display the elements that will ensure the page loads in the absence of JavaScript or the scroll event (in the example, the
feed__next-page element is responsible for this). And to hide such elements only after making sure that the scroll event comes.
<ul class="feed"> <li class="feed__item">...</li> <li class="feed__next-page">...</li> </ul>
.feed__next-page { display: block; }
$(document).one('scroll', function() { $('.feed__next-page').hide(); });
JavaScript and AJAX are awesome. But you need to understand that not everything that is convenient to use on the big screen will be convenient to use on a screen 240 pixels wide. We turn off many dynamic things for small screens, such as the ability to write a response in the tape without switching to a separate page (on the phone, this is not a pleasant action, because the interface “slows down” and twitches).

We adhere to the concept of
Progressive Enhancement , which is to use a simple semantic layout for the presentation of all content and functionality, and the latest innovations in CSS and JavaScript should be only a pleasant improvement in user interaction. This ensures that the project works on any device that supports HTML. Thus, there is nothing difficult in turning off some heavy things for devices with a low screen resolution (as a rule, they have less RAM and low performance). Here is a common example of a click event handler that sends some form.
var desktop = screen.width > 768 && !touch; $('.button').click(function (e) { if (desktop) { e.preventDefault();
I want to note that we do not disable all JavaScript - only functions that it would be more convenient to perform on separate pages specifically created for such cases.
Practice
Now let's try to create such an interesting button.

The button has gradient borders and a background, there is an icon and text, and there can be only an icon or text. This button plays the role of a collective image, by the example of which I will try to cover as many problems as possible.
Let's make the gradients with the help of CSS. To do this, we will place another
button__inner element in the button. Let's make it possible for ordinary links to look like our button. Hereinafter, CSS code will be cited, in which many properties, such as colors and gradients, are omitted, and Sass syntax (SCSS) will be used.
Sass is a CSS-based metalanguage in which there is support for variables, expressions, impurities, and much more.
<a href="#" class="button"> <span class="button__inner"></span> </a>
.button { padding: $border-width; height: $height - $border-width * 2; } .button__inner { line-height: $height - $border-width * 2; }
According to the requirements, the button should have a border of
$border-width pixels, and the height should be
$height pixels. Therefore, for the button class, we will make the pedding equal to
$border-width , and the height equal to the required height of
$height minus the peddings. For vertical alignment of elements inside the button__inner, such as icons and text, we will use the
line-height indication equal to the entire free space.
In our project, we do not use the
IMG element to create icons. And that's why. We first create a button in which the
IMG element will be used to display the icon. As the source of the image, we specify the path to the file with the transparent
background-image , and through the CSS
background-image properties and
background-position we specify the icon in the sprite. This is perhaps the most famous way to create sprite icons.
<a href="#" class="button"> <span class="button__inner"> <img src="empty.png" class="button__icon" alt="Icon" /> </span> </a>
.button__icon { width: ...; height: ...; background: ...; }
We had to abandon this option for two reasons:
- Alternative text may not be displayed at all by the browser (as WebKit does).
- There is no possibility to ensure the functioning of such an element in browsers with disabled CSS.
| IE6 | Chrome |
|---|
 |  |
See what a button looks like in Chrome and in IE6, which will not receive CSS. And no, there is no error - the button simply will not be visible. Therefore, instead of the
IMG elements, we use the usual inline elements (let it be
I ) and, for example, negative
text-indent , to hide the text inside them. Now in IE6, alternative text will be visible in place of the icon.
<a href="#" class="button"> <span class="button__inner"> <i class="button__icon">Icon</i> </span> </a>
.button__icon { text-indent: -9999px; }
| IE6 | Chrome |
|---|
 |  |
What if our class
button we apply not to the link (as we did before), but to the form element — the button?
<a href="#" class="button"> <span class="button__inner"> <i class="button__icon">Icon</i> <u class="button__text">Text</u> </span> </a> <button class="button"> <span class="button__inner"> <i class="button__icon">Icon</i> <u class="button__text">Text</u> </span> </button>
| <A> | <BUTTON> |
|---|
 |  |
The button is shorter than the link (to be exact, by the value equal to twice the size of the border of the
button class). The fact is that the box model does not apply to form elements, and the height for them must be set, including the size of the internal indents and borders. It doesn't matter - in CSS3 there is a
box-sizing property that allows you to change the default model.
conten-box value of
conten-box for it, we will force all elements that will specify the
button class to use the box model. In this example,
$include is the SASS syntax that connects styles from the so-called impurities, which allow you to group CSS properties. We use such constructions everywhere, when CSS properties require vendor prefixes to be specified.
@mixin box-sizing ($value) { -webkit-box-sizing: $value; -moz-box-sizing: $value; box-sizing: $value; } .button { @include box-sizing(content-box); height: $height - $border-width * 2; }
| <A> | <BUTTON> |
|---|
 |  |
Now let's try to open our example in Opera Mini.
| <A> | <BUTTON> |
|---|
 |  |
We see that the gradients are gone, but this should not have come as a surprise. The surprise is that the
button__inner element
button__inner become shorter and is aligned to the same differently in the link and the
BUTTON element. Why did it happen? The fact is that at the moment Opera Mini does not support the
line-height , which we used in
button__inner to align the internal elements vertically. Instead, now, for example, you need to reset the
line-height to one, specify the height of the element and its internal indents such that the value of the previous
line-height obtained in the sum of the values.
.button__inner { line-height: 1; padding: ( $height - $border-width * 2 - $font-size )/2 0; height: $font-size; }
| <A> | <BUTTON> |
|---|
 |  |
Further - more interesting. Opera Mini supports two rendering modes, and the user has the right to choose which of them work for the browser.
- In the desktop mode, the pages are displayed in almost the same way as in the large Opera, except that the browser will still try to fit the blocks with text across the width of the screen in order to eliminate the appearance of a horizontal scroll.

- The second mode is mobile, which can be switched from the browser settings. Being in it, Opera formats the page in such a way that it fits into one column, i.e. It became convenient for viewing on a device with a small screen without the appearance of a horizontal scroll.


The image shows that our two inline-block elements are lined up in one column, the sizes indicated in the styles have not been applied, only the color scheme remains. The fact is that Opera Mini in this mode defines itself as a handheld device. And to prevent the page from being pulled out, you need to specify the handheld media type in the style file - in this case, the design of this file will not be completely ignored, and the page will look as intended. And the indication of the type of media all does not help.
<link href="..." rel="stylesheet" type="text/css" media="all, handheld" />
A few more words about media queries and Opera Mini mobile mode. Let's now make a button with an icon and text hide the text on the screen with a resolution of less than or equal to 320 pixels in width:
@media screen and (max-width: 320px) { .button__text { display: none; } }

It seems to be simple and even works in the iPhone. But not in Opera Mini in mobile mode:

Do not forget that in this mode Opera Mini defines itself as a handheld device. And in media queries, you must either explicitly specify the type of handheld, or use the magic identifier all.
@media handheld, screen and (max-width: 320px) { .button__text { display: none; } } @media all and (max-width: 320px) { .button__text { display: none; } }

Now a few words about mobile Safari. Let's make a button from a link and delegate a
click event handler to the
body element.
<a class="button">...</a>
.button { }
$('body').delegate( '.button', 'click', function () {
This is sample code using jQuery, it works, the handler is called. But it is necessary to make a button on another element other than a link or form element, for example, on
DIV , as the code stops working - the handler is not called.
<div class="button">...</div>
.button { }
$('body').delegate( '.button', 'click', function () {
The fact is that the
click event, not executed on the link or form button, will not rise to the
body , and our handler will not be called. But if you specify the
cursor: pointer property for the
button class, then, oh, a miracle, everything starts working.
<div class="button">...</div>
.button { cursor: pointer; }
$('body').delegate( '.button', 'click', function () {
Conclusion
I want to note now that modern mobile browsers are almost as good as their big brothers. The same Opera Mini supports media queries, partly CSS3 and JavaScript (albeit with limitations). However, mobile browsers have a couple of big differences:
- Support
position: fixed . Despite the fact that somewhere the support appeared (iOS 5, Android 3, Opera Mobile), the implementation is lame, and it is often impossible to use. Blocks with position: fixed may freeze when scrolling, disappear and not appear until the next touch event. If the browser does not support position: fixed , then the element behaves as if the position: absolute property was set for it. - Overflow property support
overflow: scroll . An example of the lack of support is Opera Mini, where there can be only one scroll per document. If the browser does not support overflow: scroll , then the element behaves as if the overflow: hidden property was set to it. Support for this property is implemented in iOS 5, Android 3.2. - On the limitations and features of the Opera Mini (including JavaScript), you can read on the Opera website for developers .
And a few words about utilities that can be very useful when developing mobile services. To test work in a mobile Safari, you need an iOS simulator that comes with Xcode. To test the work in Opera Mobile, use the appropriate
emulator , in which you can use Opera Dragonfly for remote debugging. For Opera Mini there is an online
simulator . Also for debugging on mobile devices can be useful remote web inspector
Weinre (entered into Apache Cordova), which, by the way, is also used in the recently announced product of Adobe -
Shadow .
I tried to talk about how we create our service, which is convenient to use on a laptop, smartphone and tablet, game console, TV with Internet access, and even on the refrigerator.
Anton Eprev,
client developer Futubra