Creating applications that run on a variety of form factors has become a necessity in an environment where applications live in the cloud and people bring their own devices to work, commonly referred as BYOD (Bring Your Own Device). The range of devices was extended from desktops to smartphones and tablets as well. They have screen sizes from 3.5" over 10" to 24" and utilize different input methods, touch, pen, the traditional mouse or a combination of all of them. People expect that applications run smoothly on their own devices, and are less willing to make any compromises when it comes to usability than ever before. This blog post describes the approach we took to support this variety of devices for the new LightSwitch HTML Client.
Let's start with the one daunting question that people ask first: Which devices and browsers does LightSwitch support?
On the desktop LightSwitch supports IE10, IE9, latest Chrome, Firefox and Opera.
Devices supported by LightSwitch are Windows RT, Windows Phone 8, iPhone and iPad (iOS 5/6) and Android 4 (Android 2 is functional with some limitations).
The challenges of the web
We faced a lot of different challenges when trying to deal with this variety of devices, challenges very common to web developers, but new to people coming from a native or managed world. The most obvious one is different screen sizes on different devices. But this is only a small part of it, you also have to take into account different input methods and the performance of the individual device. Last but not least, even though the underlying platform is HTML(5) and JavaScript in all cases, it is still a fairly new standard and the supported set of features still differs hugely between devices and browsers.
Input: Is the mouse and the keyboard dead? No, but they have been supplemented by newer input methods. The devices and desktop world might have started off from two different places, but they are slowly merging together, under one umbrella of modern applications. Gestures like swipe or tap are primarily used on phones and tablets. Still, the on-screen keyboard exists and you have to take it into account, it covers a large part of the screen when opened. On the larger end, monitors in the range of 20 - 27" have support for touch now. This requires a different set of controls which are large enough for fingers but work well with the mouse as well, e.g. you might want to use toggle switches instead of checkboxes. We tried to combine the best of both worlds.
Performance: A common logic seems to be that smaller devices are slower. This holds some truth, since they necessarily have a limited computing power and memory in order to provide a longer battery lifetime. But, they have smaller screens too, so the amount of pixels they render is smaller as well. There is an ongoing contest between browser vendors on who has the fastest JavaScript engine. Still, in some cases even simple pages and scripts will surface limitations with specific CSS configurations or JavaScript calls. At the end of the day we had to do intensive performance testing on a lot of different devices in order to deliver on our promise.
Features: When it comes to feature parity with one of the HTML standards, and I am not even talking about HTML5, there is big gap between different platforms. Layout handling, for example, has always been a challenge, but newer CSS3 proposals are not supported by a wide range of browsers yet. If you want to use all of the new HTML5 features, you might want to natively wrap your application using frameworks like PhoneGap. Apart from that we tried to hide this complexity from you, build on top of as many modern features as possible, while still supporting the mentioned variety of platforms.
Screens: Dealing with different screen sizes is not enough, you have to deal with different orientations as well, portrait or landscape. On top of those physical differences there are technical fineness's as well. Vendors are competing for the highest screen resolution, which results in different pixel densities. They also came up with proprietary ways of making legacy web content scale on smaller devices, sometimes making it harder to create optimized sites. Device pixels, screen pixels, CSS pixels, if your head is already smoking, QuirksMode gives an overview about all the details in the article "A tale of two viewports". If you don't care, great, because we did so that you don't have to worry about it.
The table below lists common screen resolutions for phones and tablets. You might want to keep them in mind when testing. The graphic illustrates the different sizes by stacking all those different devices on top of each other using both, portrait and landscape orientations. The smallest blue rectangle would be an iPhone 4, the largest green one a surface.
Phone (blue) | Small Tablet (almond / orange) | Tablet (red / green) |
320x480, 320x568, 360x640, 400x640 | 480x800, 533x853, 600x1024 | 617x1097, 640x960, 700x1024, 768x1024, 768x1366, 800x1280 |
Different approaches to the problem
Supporting a wide variety of devices can be approached in two different ways. You can either try to build a single application that works on all platforms, or build individual apps per device. While the former necessarily comes with some limitations, the later comes with high implementation costs as well as a long time to market. Usually you would build native applications when going down the second route. Those applications can still leverage LightSwitch's OData middle-tier as Elizabeth has effectively shown in her blog post "Using LightSwitch OData Services in a Windows 8 Metro Style Application".
Individual Apps: Even if you go down the individual app route using web technology instead of using native code, the nature of HTML still forces a certain level of adaptability from your app. HTML is built on flexible layouts, relative instead of absolute definitions. Even inside of one device category, i.e. one platform the range of devices is huge. Let's take Android as an example and only focus on tablets, they start at 7" and go up to 11". Since this is fundamentally different from our Silverlight model it also required a certain level of rethinking on our side.
Single App: We are aware that supporting a wide variety of devices using a single implementation comes with its price, still we think we maintained the right balance here. All of our out of the box features work on the mentioned spectrum of devices and browsers. Depending on the complexity of your app it might still make sense to build a version specifically targeted at the phone form factor and another one targeted at tablets. We do not prevent you from building applications that are targeted to specific devices and leverage their features. We even support multiple applications as part of one LightSwitch application, I'll give an example for that later.
How we adapt to different form factors
Let's take a closer look at the specific features we implemented to adapt to different devices. This set of features is built-in for you, you get them for free. They are either provided by our underlying platform, jQueryMobile, or we implemented them on top of that. For those features we focused on two different form factors, the phone and the tablet, but we also made sure that applications work reasonably well on a lot of resolutions and screens which do not fall exactly into those categories.
Defaults: HTML and CSS use a very simple layout system which has been created for text markup, not for complex grid systems. By default every container (div) takes up 100% of the available width, but only as much height as it needs. We changed the defaults of all our controls to match this, StretchToContainer for the width and FitToContent for the height. We also removed absolute pixel definitions and replaced them by min and max sizes instead. This guarantees the widest possible browser support and makes it easier to flow the layout into multiple rows if it doesn't fit on the screen. We still support setting the height of a container to StretchToContainer in order to create multiple vertical scrollbars on a page. You should be aware that Android 2.x doesn't support this setting at runtime and will not show scrollbars at all. The following screenshot illustrates the new set of defaults for a view detail screen.
Media queries: CSS media queries allow you to specify style sheets that are specific to a screen resolution. After doing a lot of research around common screen resolutions we decided to settle on two different form factors and style sheets. Our default style is optimized around tablet devices. On top of that we add a few more styles to adapt to the phone. The most notable changes are font sizes, margins and paddings and icon sizes. The logo and splash screen will also automatically adapt. The phone style is also applied when running your application in Windows 8 snapped view and you can write your own specific styles in the user-customizations.css. The following screenshot shows the same application running twice on Windows 8, once in snapped view and once in the normal mode.
Text flow: What to do with text that exceeds the width of the screen? Wrap into multiple lines, cut it and show ellipsis or allow for scrolling? It depends, as it does so often. The default behavior we settled on is that tabs scroll horizontally, headlines, labels and content will only take on line and show ellipsis and normal content wraps into multiple lines. You can change this behavior by using the CSS properties overflow, text-overflow, word-wrap and white-space for individual controls using our PostRender callback. The following screenshot shows the behavior for our default browse and view details screens.
Dialogs: Whether a screen is shown as dialog or really covering the full screen is now only a matter of a simple checkbox at design time, as described in the previous blog post "A new User Experience". Besides the ability to reuse dialogs by calling them from multiple screens, this allows for another feature. On the phone form factor it doesn't matter whether you created a dialog or screen at design time, they will always be shown as full screens in order to maximize the usage of the available space, shown in the following screenshot.
Dynamic tile list: A tile list is nice on a wide screen, but how many columns fit on a smaller screen? Windows 8 solved that problem by introducing horizontal scrolling. Web applications scroll vertically by default, still you want the tiles to fill the available horizontal space. We adapt to Microsoft's modern application guidelines in general, but LightSwitch apps are cross-platform web applications and not native applications. People do not expect them to scroll horizontally. We solved that problem by introducing a new property Adjust Tile Width dynamically. Selecting this option will basically set the width of the tile list between one and two times the width you actually specified. You can see the result in the following screenshot:
Multiple columns: A tablet might have enough space to fit a two column layout, on the phone you got only space for one column though. Dialogs are smaller on the phone as well, the same logic applies. You might want to show one column instead of the default two columns on larger screens. In order to get that behavior we configured the two columns layout in the screen templates with a specific layout. Each row's width sizing mode is set to StretchToContainer with a min and max width. The exact width values are some magic numbers that we calculated for you based on the set of common screen resolutions. The combination of the sizing mode and those magic numbers allows for the specific behavior you can see in the screenshot below. Feel free to change the numbers, remove or add another column if the defaults do not fit your needs.
Header and footer: One of the new features has caused some confusion around our testers. As soon as people discover how this feature really works they love it though. We didn't even have to implement it on our own, jQueryMobile gave it to us for free. The feature I am talking about is dynamically showing and hiding the header and footer. Since headers and footers can cover a large amount of the available space, especially on smaller devices, they can be hidden. Tapping on an empty area of your screen will just hide them. Tap again and they will fade in again. If you are still confused, jQueryMobile also allows you to disable the tap interaction for certain controls by specifying tapToggleBlacklist, or altogether by adding data-tap-toggle='false' to your header and footer.
Implementing custom form factor specific behavior
In addition to those built-in features, you can also implement your own form factor specific behavior. I will show you how to start with small CSS customizations before creating two completely different screens for different form factors. We will use the familiar Northwind database again (http://services.odata.org/Northwind/Northwind.svc). Make sure your project is set-up to have a basic Northwind CRUD application running, as described in our previous blog post.
Form factor specific styles
The home screen of the Northwind app shows a tile list with all customers, like the first screenshot in this blog post. Each tile has three rows, the customer name, the company name and the company address. This looks really great on a tablet screen. On a phone it takes up too much space though. Probably some information can be hidden from this screen since the details screen will show all of them by clicking on one of the tiles. Let's hide the address in the phone form factor. This can be done by two easy steps.
Adding the CSS class: Select the address field in the screen designer. In the properties sheet click "Edit PostRender Code" which opens a code window. Add the following code:
myapp.BrowseCustomers.Address_postRender = function (element, contentItem) {
// Write code here.
$(element).addClass("hidden-on-phone");
};
This decorates our DOM element with an additional CSS class named "hidden-on-phone". We will use this class in the second step to hide the content on smaller screens.
Defining the CSS style: Select "File View" from the solution explorer's toolbar. This will show the underlying project structure of your solution. We will not go into the details of all files for now. The important file you should be looking for is "user-customizations.css", it lives in the "Content" folder of your client project. This file is the central place for all your CSS customizations. This file and "default.htm" are the only ones that we will not override on updates, e.g. from CTP4 to RTM. We already added an empty media-query on the bottom of the file for you. This is the same media query that we use internally to adapt to different form factors. Inside this media query add the following code:
.hidden-on-phone {
display: none;
}
That's it, the screenshot illustrates the few changes we made:
Testing your changes
You can get a good overview about how things looks on different devices by just F5ing the app in your favorite browser and playing around with the window size. If you want to emulate a specific screen size, Internet Explorer provides a very handy feature for you. Open the Development Tools (F12) and select Tools -> Resize (1). This will open a dialog where you can define a specific screen size, e.g. 320x480 for the iPhone 4 (2). Clicking Resize will apply those settings to the currently opened Browse window (3). You can see that on the large screen the address is shown (background), on the small one it is hidden now (foreground).
Different home screens
Let's take it a step further and assume that your application is a bit more sophisticated and you need specific layouts for different form factors. We provide you with a hook in our default.htm to redirect to different home screens based on custom JavaScript logic.
Let's add a second home screen to browse our customer data and name it "BrowseCustomersSmall". For now we will just stick with the default values the screen template gives us. Open the "File View" in the solution explorer and look for "default.htm" in your client project. At the bottom of the file you'll find a script tag that calls msls._run(), without any parameter. You can specify the screen you actually want to run by adding a simple string parameter to it.
Replace the existing code snippet in default.htm with the following one. It uses the media query that you are already familiar with. Instead of using it from CSS we will execute it in JavaScript this time. Based on whether the media query applies or not the home screen will be set to either "BrowseCustomersSmall" or "BrowseCustomers":
<scripttype="text/javascript">
$(document).ready(function () {
if ($.mobile.media("screen and (max-width: 400px) and (orientation: portrait), \
screen and (max-width: 640px) and (max-height: 400px) and (orientation: landscape)")) {
var screen = "BrowseCustomersSmall";
} else {
var screen = "BrowseCustomers";
}
msls._run(screen)
.then(null, function failure(error) {
alert(error);
});
});
script>
F5 the application again and try different screen sizes. This time you have to make sure that you reload the application in order to really see the difference shown in the following screenshot:
Wrapping it up
I got two final tips for you that helped us designing apps that run on different form factors.
First, start small. Start with the smallest set of features that you think you need. Then, if you increase the screen size, think about what additional features would make sense. Do not try to overload your app with features, the more features you have, the harder it will be to make it work across different devices. The Windows 8 user experience guidelines have a good introduction describing this method.
Finally: test, test, test. I recommend testing on different browsers. Internet Explorer is used by Surface and Windows Phones. iPad and iPhone and Android all share the same underlying rendering engine, WebKit. iOS devices are closer to Safari and Android closer to Chrome (*surprise*). But at the end of the day nothing replaces testing on the actual devices.
As always, use the forums and comments to ask questions and provide us with feedback, and stay tuned for more blog posts.
- Heinrich Wendel, Program Manager LightSwitch Team