Developing Adaptive and Flexible Metro HTML5/JavaScript* Apps

Download Article

Developing Adaptive and Flexible Metro HTML5/JavaScript* Apps [PDF 704 KB]

Objective

HTML5 and JavaScript* have emerged as powerful tools that can be utilized to create full featured compelling Metro style applications on devices running the Windows 8* OS. These applications can target multiple form factors like UltraBook™ devices, and tablets and laptops running Windows 8. In this article we will walk through different considerations in building HTML/JavaScript Metro style apps that are adaptive to any screen size, and use flexible layouts to fit different views of Windows 8 apps, including snap view, filled view, and portrait or landscape. We will look at taking advantage of CSS3 features like grid layouts, flexbox, and media queries. Finally, we will walk through a sample app** to demonstrate these development methodologies.

 

Table of Contents

Introduction

Metro style applications are a new application development platform introduced with Windows 8 that provides the capability of developing applications that can be deployed on multiple form-factor devices running Windows 8. Developers create a common application regardless of the deployment device, the runtime framework manages the behavior of the application on the target device. To develop Metro style apps, developers are encouraged to follow the Metro Style Guidelines, found at:
http://msdn.microsoft.com/en-us/library/windows/apps/hh464920.aspx

One of these guidelines is to develop apps that “Snap and scale beautifully.” The app GUI needs to have a flexible layout, so that it can adapt its user interface to any screen size, re-orient the user interface as the device is rotated (portrait or landscape), and also be flexible enough to adapt to snapped or filled views of Metro style apps. The user interface design needs to take into account all of these aspects during the initial stages of application development.

In this article we will showcase how to take advantage of HTML5/CSS3 features to achieve these design targets. We will walk through a sample app and demonstrate step-by-step procedures to implement these features.

It is assumed that the reader has basic familiarity with HTML and CSS.

 

Overview of HTML5/JavaScript Metro Apps

With Windows 8 and Metro style apps, developers can take advantage of their HTML/JavaScript skills in developing full blown apps with functionality on par with native Windows apps. These apps have full access to all system APIs exposed via WinRT. More details on developing Metro Style apps using JavaScript can be found on this link:

http://msdn.microsoft.com/en-us/library/windows/apps/hh465037.aspx

In this article, for demonstration purposes, we will use a sample HTML5/JavaScript-based Metro style app called “Tower of Hanoi” (ToH) (http://en.wikipedia.org/wiki/Tower_of_Hanoi). The objective of the ToH app is for users to solve the “Towers of Hanoi” puzzle by moving disks from pole #1 to pole #3, using pole #2 as temporary location. ToH will also provide a button to animate the solution if the users so chooses, and the solution is presented via an animation with a configurable delay. In addition, controls for changing some of the application configurations and display of status information are also included in the sample app.

The ToH app’s final design is shown below:

We also have an app bar button to show “About Game” information. Below is the screenshot for app bar and “About Game.”

The big goal of this app is to target different form-factor devices, all the way from tablets (e.g., 10.6”” 1024x768) to big screen devices (e.g., up to 27’’, 2560x1440). We would also like the app to adapt to portrait view and Metro fill/snap views automatically. The disks, poles, and text sizes will have to scale up and down seamlessly depending on available screen real estate.

 

Device Size, Views, Orientation, and App Layouts

Device size is a factor of screen resolution or pixels and size of the screen. Windows 8 and Metro style apps support different combinations of them and have the ability to scale to any size. Metro style apps or games typically have UI resources in either image format (e.g., png or jpeg) or some custom graphics like SVG. If developers use SVG assets, they will automatically scale. If developers have custom images, they might store different sizes of the image for each scale; Windows 8 will automatically pick the correctly scaled version. It is recommended to use Windows 8 facilities for automatic resource loading with naming conventions for the three different zoom levels (100%, 140%, 180%). This will handle most of the pixel densities and screen resolution for the images. More information on device size and scaling can be found here:

http://blogs.msdn.com/b/b8/archive/2012/03/21/scaling-to-different-screens.aspx

In the ToH app, we do not have any graphics resources or images. All the UI elements are implemented in plain HTML5/CSS3 elements. For ToH, we would like to support all screen sizes from 10.6”” (1024x768), 1366x769 (the minimum to support snapped/fill views) and above. To test and interactively design the app, developers can use “Blend for Visual Studio*,” which shows quick, interactive layout of the app in different screen sizes. The screenshot below shows all the available options in the Blend tool.

Windows Metro style apps allow the users to change the app display into a snapped view or a filled view. More details can be found at this link: http://msdn.microsoft.com/en-us/library/windows/apps/hh465371.aspx

For the ToH app, below are the final layouts for the filled view and snapped view, respectively.

Filled view:

Please note the disk and pole sizes are automatically reduced. The status text is also automatically realigned, pushing the “Status:” text below the Moves/Elapsed text.

Snapped view:

In the snapped view, all the elements including text size are automatically scaled down. For lack of space, the snapped view is not showing the control buttons; it only shows the current status text.

Developers are encouraged to reduce the amount of information shown in snapped view, due to the limited space. One strategy is to show the minimal amount of information that a user would find useful. Some apps may not be able to achieve a snapped view that is useful to users; in which case, the app should not be loaded (maybe have an app or game logo). Users will have to use a form factor that accommodates the filled or full view to use the app. The store app is a good example of this, as shown in the Snapped view screenshot above.

Users can also change the orientation of their devices to a landscape or portrait mode. We want our sample app to automatically reorient and scale the disk sizes, poles, and status/controls to fit the appropriate orientation. The previous screenshots show landscape mode; below is the screenshot for portrait mode for our sample app. In this mode the poles are reoriented vertically, very similar to the snapped view. But in this case the app occupies the full screen and shows all the app pieces, unlike the snapped view.

In this section, we have discussed device screen sizes, views, orientation, and how we want the ToH app layout to be in each of these cases. In the next few sections we will discuss how to implement these features.

 

App Layouts and View Design

As part of the design of a new app, it’s recommended to identify core UI pieces of the app. Developers need to break the app’s full UI into pieces that can be moved around or scaled as required.

For example, in ToH app we can break the UI into several pieces: three towers that constitute the core piece, game controls, and status. Since our core piece is a little big (not flexible as a whole), we can break it down into three separate towers that can be moved around as needed. But we still need a way to identify which tower is #1, #2, or #3, as it’s part of the game’s core logic (e.g., the game ends when all disks are moved to tower #3).

The screenshot below shows the breakdown:

We have four UI pieces:

  • Tower #1
  • Tower #2
  • Tower #3
  • Game panel - Combines both controls (4.1) and status (4.2)

We want to be able to move these pieces in any configuration, dynamically at runtime. We want the app to respond to any view change or portrait change by updating the location of these pieces. All these pieces are also expected to scale up or down appropriately.

Ideally, it is preferable if we could accomplish all of this inside the presentation layer. Let the system’s (in this case HTML5/CSS3) presentation facilities handle the four pieces automatically in the most optimized and HW-accelerated way. Fortunately, the latest CSS3 standard makes this possible. We can use CSS3 media queries to listen to view or orientation change(s), and respond by reconfiguring the app layouts using new CSS3 flexbox and grid layout features. All of this, without even a single line of JavaScript code. Also, this nicely separates the presentation layer from the underlying app core logic or game model.

 

Using CSS3 Grid and Flexbox Layouts

As we have seen, all the elements in our sample app are just plain HTML elements (no HTML5 canvas element). Below is the HTML markup for the four pieces we identified in the last section.

[xhtml:nogutter:nocontrols]<div class="gamePage" id="gameHost"> <div class="pole" id="pole1"> <div class="dbase"></div> <div class="dpole"></div> <div class="dhost" id="dhost1"></div> </div> <div class="pole" id="pole2"> <div class="dbase"></div> <div class="dpole"></div> <div class="dhost" id="dhost2"></div> </div> <div class="pole" id="pole3"> <div class="dbase"></div> <div class="dpole"></div> <div class="dhost" id="dhost3"></div> </div> <div class="controls"> <div class="gctrl"> <button class="btn" id="ResetButton">Reset Game</button><br /><br /> Number of Disks: <span id="numDisksLabel"></span><br /> <input type="range" class="btn" id="numDisksSlider" min="1" max="12" /> </div> <div class="gctrl"> <button class="btn" id="StartAnimButton">Solve and Animate</button><br/><br/> Animation Delay: <span id="animationSpeedLabel"></span><br /> <input type="range" class="btn" id="animationSpeedSlider" min="0" max="100" /> </div> <div id="status"></div> </div> </div> [/xhtml:nogutter:nocontrols]

Each of our pieces has a corresponding div Id or class, as seen in the above markup:

  • Tower #1 - #pole1
  • Tower #2 - #pole2
  • Tower #3 - #pole3

Game panel – Combines both controls (4.1) and status (4.2) – “.controls”, “gctrl”, “#status”

We will use these identifiers in CSS styling to control their size, style, and layout(s).

CSS3 introduces new flexible layout options. The main ones among them are the grid layout and flexbox layout. Metro apps support both.

Grid layout lets the developers define the layout in terms of columns and rows. The users can then define which element will occupy which box (box formed by the intersections of rows and columns). We can even assign combined box(es) to elements. For example, we can combine row 2 (r2) with columns 3 (c3) and 4 (c4). So we get a combination of box r2-c3 and box r2-c4. This is a very powerful way to position and align different layout pieces of the app. The child elements of the grid can further define their own rules.

Without the grid layout feature, users have to resort to different rudimentary position techniques (e.g., absolute position, table layout, etc.) that are not as flexible as grid layout. For our sample app, we mainly use grid layout to specify where all our main pieces (the four pieces discussed above) will go, and how they will be laid out in different views (snap, fill, portrait, etc.). But grid layouts are capable of much more than this. For more detailed info, please refer to the documentation at: http://www.w3.org/TR/css3-grid-layout/

For the ToH app, we define the default (landscape, full screen) grid layout in CSS3 styling as shown below:

[xhtml:nogutter:nocontrols] #gameHost { width: 100%; height: 100%; display: -ms-grid; -ms-grid-columns: 0px 1fr 1fr 1fr 0px; -ms-grid-rows: 1fr 14rem; } [/xhtml:nogutter:nocontrols]

Here, we define five columns (values separated by space) and two rows. Users can define the size in terms of fractions, among other metrics. In this case, we want each of the towers to have the same space, so we define 1fr for each (equal ratio for all 3). Please note, users can also use absolute pixel to define the columns or row sizes. In the above example, we used 0px for columns 1 and 5, as a place holder for future modifications if we decide we want some kind of border for our game (maybe of 4 pixel breathing space between device borders). We can leave such placeholders so we don’t have to modify all of the elements’ positioning again, every time we change the grid layout initialization. So in this case, we could make columns 1 and 5 of 4px each, and everything will work with a nice 4px border on both sides.

For rows, we left 14 rem (relative sizing based on system font size) for our game panel controls, and everything else for the towers. Using relative font sizing (rem metric) helps to scale the size of control panel depending on the screen size. We do not have to use absolute metrics, which will not scale automatically.

For each tower, we define the corresponding element id to use grid positioning as shown below (each tower occupying 1fr columns 2-4):

[xhtml:nogutter:nocontrols]#pole1 { -ms-grid-column: 2; -ms-grid-row: 1; } #pole2 { -ms-grid-column: 3; -ms-grid-row: 1; } #pole3 { -ms-grid-column: 4; -ms-grid-row: 1; } [/xhtml:nogutter:nocontrols]

This only positions three major pieces. We still have the controls (buttons and status text) to position. Controls will use the 2nd row, and all columns (1 to 5). Below is the grid assignment for this element (“.controls”).

[xhtml:nogutter:nocontrols].controls { -ms-grid-column: 1; -ms-grid-row: 2; -ms-grid-column-span: 5; display: -ms-flexbox; -ms-flex-align: center; -ms-flex-pack: start; -ms-flex-wrap: wrap; color: hsl(30, 88%, 50%); } [/xhtml:nogutter:nocontrols]

This element has two sub-elements, as seen in the HTML markup. One for the fixed buttons/controls and another for the game status. Notice it uses flexbox (display: -ms-flexbox) as its display, which we will cover next.

Flexbox is another nice CSS3 layout feature that serves a slightly different purpose than grid layout. It is very useful particularly when you have a bunch of elements that you want to throw inside a div, and make them automatically align, flex, stretch, or pack depending on total available space. A good example is command icons or game controls that are aligned horizontally or vertically. Using flexbox, we can specify the elements to be automatically centered, stretched, packed in the middle, etc. Further, we can specify some children as a fixed rigid size or how flexible they should be. The fixed items (non flexible) take up their required size, and any remaining space is automatically distributed among theflexible items.

In the ToH app, we apply this flexbox layout to our controls panel. The controls panel has two sub pieces, the buttons/controls (4.1) and game status (4.2). The default Windows 8 metro slider input controls for HTML5/JavaScript are of fixed size. So we combine all buttons, input slider controls into one “fixed” (non-flex) piece, and keep the status section (which has game moves, time elapsed, and game status items) to take up all the remaining space automatically (flexible item). Please see the flexbox CSS3 markup below for the “controls” (piece #4) panel.

[xhtml:nogutter:nocontrols].controls { -ms-grid-column: 1; -ms-grid-row: 2; -ms-grid-column-span: 5; display: -ms-flexbox; -ms-flex-align: center; -ms-flex-pack: start; -ms-flex-wrap: wrap; color: hsl(30, 88%, 50%); } [/xhtml:nogutter:nocontrols]

To further customize the status box (which is now a flexible item inside the “controls” flexbox), we make it another flexbox. This is another nice feature of these advanced grid and flexbox layouts. They can be nested!

Below is the CSS3 flexbox markup for the status item (piece #4.2).

[xhtml:nogutter:nocontrols]#status { display: -ms-flexbox; -ms-flex: 1; -ms-flex-align: start; -ms-flex-pack: start; margin: 0.5rem; font-size: 1.5rem; -ms-flex-wrap: wrap; } [/xhtml:nogutter:nocontrols]

As seen above, we make this status piece a flexible item (-ms-flex :1 ) and further set its display to “-ms-flexbox”. This allows the pieces inside the status element (game moves, elapsed time, and game status) to automatically flex their size as needed. Based on testing the app in different views, it was required to mark the game status as a flexible item, keeping game moves and elapsed time as non-flexible. Below is the markup. The first one is for non-flexible game moves/elapsed time. The second item is the flexible game status message with –ms-flex: auto.

[xhtml:nogutter:nocontrols].gsts { -ms-flex: none; margin: 0.5rem; text-align: center; } .gstsmsg { margin: 0.5rem; -ms-flex: auto; text-align: left; } [/xhtml:nogutter:nocontrols]

Flexbox, in general, is capable of much more. Please refer to the document below for more details on flexbox:

http://dev.w3.org/csswg/css3-flexbox/

To summarize, our sample app uses one grid layout and two flexbox layouts. App UI pieces 1, 2, 3, 4, 4.1, and 4.2 (please refer previous screenshots with annotations) all are inside the main grid layout. UI piece 4 has its own flexbox to manage pieces 4.1 and 4.2. Further, we need flexibility inside UI piece 4.2, which also uses another flexbox (this flexbox is the child of the previous flexbox, which itself is a child of the main grid layout!)

With these app layouts configured, our sample app is ready to respond to orientation or view changes.

 

Using Media Queries

CSS3 layout features provide excellent capabilities and flexibility to configure app layouts. But we need a way to know when the user rotates the device into portrait mode, or when the user changes the metro app view to a filled or snapped view mode.

HTML5/CSS3 Media Queries provide this capability. Windows 8 Metro style apps further integrated snapped and filled views inside CSS3 media queries as well. So the media queries cover all orientations and view possibilities on devices running Windows 8 Metro style apps.

Media queries let developers define or override element styles based on view state and orientation changes.

For example, in the snapped view portion of our sample app, we want to hide game controls/buttons, and just display the status panel. We can define a new media query rule and override the corresponding element’s display property to none, as shown in the code snippet below.

[xhtml:nogutter:nocontrols]@media screen and (-ms-view-state: snapped) { .gctrl { display: none; } [/xhtml:nogutter:nocontrols]

Whenever the device enters the snapped view, this style rule (display : none) will be automatically applied to that element.

Media queries are very powerful and easy to use. Complex app layout changes can be achieved for different view states. More detailed information on media queries and how to use them is available here:

http://www.w3.org/TR/css3-mediaqueries/

For the ToH app, we designed most of our element sizes and layout sizes size based on a relative font size (rem –root em metric). This metric will apply relative size of the main root font size (defined inside root “html” element). So in our media queries, we just have to change this root element font size, and everything will automatically resize relative to it.

For example, in snapped view the space is very limited even for higher resolutions. So we would apply a media query like the one below:

[xhtml:nogutter:nocontrols]@media screen and (-ms-view-state: snapped) and (min-height:2500px) { html { font-size: 15px; } } @media screen and (-ms-view-state: snapped) and (min-height:1920px) { html { font-size: 11px; } } [/xhtml:nogutter:nocontrols]

The above rules will be applied based on the minimum display height (1920 or 2500px). Since the root font-size changed, all the corresponding element and layout sizes that used “rem” metric, will automatically scale/resize as well (this includes our grid layout sizes).

Another important scenario is that of portrait and snapped views in general. They both have the vertical display (landscape width becomes the height), and screen width of the device is reduced. In our sample app, this complicates our position of towers. We need to position the towers in vertical alignment, one below each. Since both of these views require this, we can combine the media queries for them with a “,” in between (so you can combine several media queries into one as needed). Please see the media query markup for this scenario below:

[xhtml:nogutter:nocontrols]@media screen and (-ms-view-state: fullscreen-portrait), screen and (-ms-view-state: snapped) { #gameHost { width: 100%; height: 100%; display: -ms-grid; -ms-grid-columns: 4rem 1fr 4rem; -ms-grid-rows: 1fr 1fr 1fr 19rem; } .controls { -ms-grid-column: 1; -ms-grid-row: 4; -ms-grid-column-span: 3; display: -ms-flexbox; -ms-flex-align: center; -ms-flex-pack: start; color: hsl(30, 88%, 50%); -ms-flex-wrap: wrap; } #pole1 { -ms-grid-column: 2; -ms-grid-row: 1; } #pole2 { -ms-grid-column: 2; -ms-grid-row: 2; } #pole3 { -ms-grid-column: 2; -ms-grid-row: 3; } .disk { height: 1.6rem; margin-left: auto; margin-right: auto; border: 1px solid #666666; [/xhtml:nogutter:nocontrols]

We redefine our grid layout to contain a single big column with more rows (one each for the towers, and one for controls). These layouts will be automatically applied when the device goes into a snapped or portrait views.

If users need to further customize the snapped view, they could define another media query after the above query. For example, in the ToH, app we need to use all the available space so we set columns 1 and 3 to 0 size, and leave one big main column (column 2) as shown below:

[xhtml:nogutter:nocontrols]@media screen and (-ms-view-state: snapped) { #gameHost { -ms-grid-columns: 0px 1fr 0px; -ms-grid-rows: 1fr 1fr 1fr 14rem; } #status { -ms-flex: 1; } .gctrl { display: none; } html { font-size: 8px; } .disk { height: 1.9rem; } } [/xhtml:nogutter:nocontrols]

These rules will override the ones defined for the snapped view earlier in the CSS markup.

Similarly, users can apply different style rules depending on the required views or orientations, by using appropriate media queries for them.

 

Putting It All Together and Validating It in Windows 8 Simulator

The ToH app now has flexible and adaptive layouts, which respond to any device orientation or system view state(s). Developers can use the Windows 8 Simulator to validate the app for all use cases. Currently, the simulator supports the screen sizes below:

It can emulate different view states as well: portrait, filled, or snapped views. We can combine both screen sizes and views to come up with several combinations for testing. Not all combinations are valid, for example, there are no snapped/filled views in portrait mode.

The simulator can be chosen as the debug target inside Visual Studio, which will automatically start the simulator and launch the app inside. The simulator has buttons to rotate the device 360 degrees, so we can test all orientations. We can choose the check box for the required device screen resolution. For example, in the screenshot below we show a test case for 27’’ 2560x1440 device in portrait mode (which is not that common)-just shows what’s possible with the simulator.

Developers can use Blend in Visual Studio during design and development stages and finally verify their apps inside the simulator (for another level of verification).

 

Summary

With Windows 8 and Metro style apps, developers can target different form-factor devices from tablets to big screen devices. Developing HTML5/JavaScript Metro style apps that are flexible and adaptive to multiple devices is critical. In this article we discussed the challenges of different screen sizes, Windows 8 Metro views, device orientations and how to solve them. We used a sample app as a case study to demonstrate how to apply the latest HTML5/CSS3 features like grid layout, flexbox, and Media Queries. These features allow us to implement our solution entirely in presentation layer (HTML5/CSS3). Developers are encouraged to take advantage of these features, to make their apps work on different form-factor devices, and comply with Metro guidelines like “Snap and scale beautifully”.



*Other names and brands may be claimed as the property of others.

**This sample source code is released under the Intel Sample Source Code License Agreement

For more complete information about compiler optimizations, see our Optimization Notice.

Comments