Quickstart: Defining app layouts (Windows Store apps using JavaScript and HTML)

10 out of 19 rated this helpful - Rate this topic

[This topic is featured in Develop great apps for Windows 8.]

You can define app layouts for each of the new Windows UI view states, including portrait, landscape, snapped, and fill.

Objective: After reading this article you will have a solid understanding of how to use HTML, Cascading Style Sheets (CSS), and JavaScript to create a fluid UI that looks good and functions well in all view states.

Prerequisites

The Adaptive layout with CSS sample

This article illustrates the basic concepts involved in defining app layouts by describing how layouts are implemented in the Adaptive layout with CSS sample. This sample is a simulated weather app that displays the current day's weather and the ten-day forecast. It demonstrates how to use CSS and HTML, the CSS grid, the ListView control, and CSS media queries to create a fluid app layout.

Before we get into the details, let's take a look at the structure of the Adaptive layout with CSS sample. The app consists of three HTML pages. The first is a top-level page called App.html that defines the main surface for the app's UI items. It includes a back button, a title and subtitle, an app menu, the app bar buttons, and an area for displaying content (the white area in the following image).

The sample app's main page

The other two HTML pages, Current.html and TenDay.html, define the structure of the content that is displayed in the content area of the main page. The Current.html page displays details about the current day's weather:

The current weather page

The TenDay.html page displays details about the ten-day forecast:

The ten-day forecast page

We'll focus on the parts of the Adaptive layout with CSS sample that define the layout of the app's main page, and the layout of the ten-day forecast.

Here's what app's main page looks like when displaying the ten-day forecast on a 10.6" display at 1366 x 768 resolution in the portrait, landscape, snapped, and fill view state.

The four view states of a Windows Store app

Ensure that the app fills the available screen area

A well-designed app has a UI surface that takes up all of the available screen area. At first this may seem like a difficult challenge given the wide range of device form factors, resolutions, and orientations that an app needs to accommodate. Fortunately, CSS makes it easy.

To ensure that the app takes up all the available screen area:

  1. Use a div element as the top-level container for all other UI elements on the page.
    
    <body>
        <div class="win-ui-dark appGrid">
            <!-- TODO: Define page structure here. -->
        </div>
    </body>
    
    
  2. Use CSS to set the width and height properties of the div to ensure that it fills the screen. The sample does this by setting these properties to 100%. However, it's best to use 100vw (viewport width) and 100vh (viewport height), as shown here:
    
    .appGrid {
        width: 100vw;
        height: 100vh;
        /* TODO: Add other styles here. */
    }
    
    

Define the basic layout of the main page

When you lay out your app UI, it's best to start with the landscape view state. After you define your landscape layout, it's easy to adapt it for the other view states.

Use HTML to define the UI items for the page

An app UI typically contains items such as navigation buttons, headings, menus, controls, and so on. Add these UI items as child HTML elements of the top-level div element for the page.

The following HTML defines the UI items for the Adaptive layout with CSS sample app's top-level page. The items include the back button, title and subtitle, app menu, the main content area, and the app bar buttons.


<body>
    <div class="win-ui-dark appGrid">
        <header aria-label="Header content" role="banner">
            <button class="win-backbutton" aria-label="Back"></button>
            <h1 class="titlearea win-type-ellipsis">
                <span class="win-type-xx-large titlecontainer" tabindex="0"><span class="pagetitle">Mountains</span><span class="win-type-x-large chevron">&#xE099</span></span>
                <span class="win-type-x-large pagesubtitle">Rainer</span>
            </h1>
        </header>
        <div id="headerFlyout" data-win-control="WinJS.UI.Menu">
            <button data-win-control="WinJS.UI.MenuCommand" data-win-options="{id:'rainierMenuItem', label:'Rainier'}"></button>
            <button data-win-control="WinJS.UI.MenuCommand" data-win-options="{id:'stHelensMenuItem', label:'St. Helens'}"></button>
            <button data-win-control="WinJS.UI.MenuCommand" data-win-options="{id:'olympusMenuItem', label:'Olympus'}"></button>
            <button data-win-control="WinJS.UI.MenuCommand" data-win-options="{id:'bakerMenuItem', label:'Baker'}"></button>
            <button data-win-control="WinJS.UI.MenuCommand" data-win-options="{id:'adamsMenuItem', label:'Adams'}"></button>
        </div>
        <div class="appViewContentMask">
            <div class="appViewContent">
                <div id="current-page" class="page" data-win-control="WinJS.UI.HtmlControl" data-win-options="{uri: '/html/current.html', data: FluidAppLayout.Data.mountains[0].weatherData[0]}"></div>
                <div id="ten-day-page" class="page" data-win-control="WinJS.UI.HtmlControl" data-win-options="{uri: '/html/tenDay.html', data: FluidAppLayout.Data.mountains[0].weatherData}"></div>
            </div>
        </div>
    </div>
    <div id="appbar" class="win-ui-dark appbar" data-win-control="WinJS.UI.AppBar">
        <button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id: 'current', label: 'Current', icon: 'calendarday', type: 'toggle', onclick: FluidAppLayout.transitionPivot, selected: 'false', tooltip: 'Get the current report'}"></button>
        <button data-win-control="WinJS.UI.AppBarCommand" data-win-options="{id: 'tenDay', label: 'Ten day', icon: 'calendarweek', type: 'toggle', onclick: FluidAppLayout.transitionPivot, selected: 'false', tooltip: 'Get the ten day report'}"></button>
    </div>
</body>

Use CSS grids to position UI items on the HTML page

One of the best ways to achieve a fluid and adaptable UI layout is by using CSS grids. That's because the grid can automatically expand to fill the space that it's given, and it exposes a rich set of properties that make it easy to tailor the UI layout for each view state. Because the position of elements in a grid is independent of the order in which they are specified—that is, their position is dictated purely by CSS rather than by their order in HTML markup—it's easy to give elements different arrangements on different screen sizes or in different view states, or even to avoid displaying some elements entirely in some layouts.

Laying out the main page

  1. The Adaptive layout with CSS sample app applies a CSS grid to the top-level div on the App.html page by setting the display property of the div -ms-grid. This top-level grid defines the overall structure for positioning UI items on the app's main page.
  2. Next, the sample app defines the columns and rows of the grid by setting the value of the -ms-grid-columns and -ms-grid-rows properties.

    The following CSS code applies a grid to the top-level div on the sample's main page. This grid is used to position the items that make up the app's header (the back button, title and subtitle, and app menu), and to set the position of the main content area.

    
    .appGrid {
        display: -ms-grid;
        -ms-grid-columns: 120px 1fr;
        -ms-grid-rows: 120px 1fr; 
    
        /* Other properties omitted for clarity. */
    
    }
    
    

    The preceding CSS code creates a grid with two columns and two rows. The first column is 120 pixels wide, and the second column is one "fractional unit" wide. That means the column width automatically expands to fill the available space that is not taken up by the first column. The rows are defined in a similar manner.

    This illustration shows how the grid divides the app's main page:

    The main page grid
  3. Next, the Adaptive layout with CSS sample sets the position of UI items by assigning each item to a particular cell in the grid. To do that, the sample applies the -ms-grid-column and -ms-grid-row properties to elements on the page. The CSS grid supports several other properties for positioning items relative to the cell boundaries, and allowing items to span multiple columns or rows. To learn more, see Grid alignment

    The following CSS code positions the header element of the sample app's main page in column 1, row 1 of the top-level grid, and allows the element to span both columns of the grid. The code also creates a "child" grid within column 1, row 1 of the top-level grid. The child grid is used to position the individual items that make up the header (the back button, title and subtitle, and app menu).

    
    header[role=banner] {
        -ms-grid-column: 1;
        -ms-grid-row: 1;
        -ms-grid-column-span: 2;
    
        /* Child grid for positioning header items.*/
        display: -ms-grid;
        -ms-grid-columns: 120px 1fr;
        -ms-grid-rows: 1fr;
    }
    
    

    The preceding code creates a grid in the area highlighted in blue in the following image.

    The child grid for the sample app's header

    The Adaptive layout with CSS sample uses nested div elements to define the main content area where the current forecast and the ten-day forecast appears:

    
    <div class="appViewContentMask">
        <div class="appViewContent">
            <div id="current-page" class="page" data-win-control="WinJS.UI.HtmlControl" data-win-options="{uri: '/html/current.html', data: FluidAppLayout.Data.mountains[0].weatherData[0]}"></div>
            <div id="ten-day-page" class="page" data-win-control="WinJS.UI.HtmlControl" data-win-options="{uri: '/html/tenDay.html', data: FluidAppLayout.Data.mountains[0].weatherData}"></div>
        </div>
    </div>
    
    

    Notice in the preceding example that the Windows Library for JavaScript HtmlControl control is used to dynamically include the HTML pages for the current and ten-day forecasts. To learn more about Windows Library for JavaScript controls, see Quickstart: Adding WinJS controls and styles.

    The following CSS code positions the appViewContentMask div in column 2, row 2 of the top-level grid. It also sets properties to ensure that the content fills the entire grid cell, that any content that doesn't fit in the cell is hidden.

    
    .appViewContentMask {
        -ms-grid-column: 2;
        -ms-grid-row: 2;
        width: 100%;
        height: 100%;
        overflow: hidden;
    
        /* Other properties omitted for clarity. */
    
    }
    
    

    The following CSS code turns the appViewContent div into a child grid containing a single cell that fills the area defined by the appViewContentMask div. Using a child grid makes it easy to reflow the content when the view state changes the size or orientation of the app.

    
    
    .appViewContent {
        width: 100%;
        height: 100%;
        display: -ms-grid;
        -ms-grid-columns: 1fr;
        -ms-grid-rows: 1fr;
    }
    
    

    The CSS code assigns the grid for the content area to the area highlighted in blue:

    The grid in the sample app's content area

Define the basic layout of the ten-day forecast

The ten-day forecast is a collection of items that are managed and displayed by using a Windows Library for JavaScript ListView control. Each item consists of an image and a set of strings, including the date, high and low temperatures, "feels like" temperature, and chance of snow:

Layout of items in the ten-day forecast

The following HTML page defines the UI for the items in the ten-day forecast. The Adaptive layout with CSS sample uses Windows Library for JavaScript templates and data binding to supply data to the ListView control. This topic focuses on laying out your UI, so we won't get into templates and data binding here. If you want to learn more about templates and data binding, see How to use templates to bind data.


<body>
    <div class="tenDayPage">
        <div id="tenDayTemplate" class="tenDayTemplate" data-win-control="WinJS.Binding.Template">
            <div class="tenDayGrid">
                <div class="win-type-x-large tenDayDate" data-win-bind="innerHTML: date">
                </div>
                <div class="tenDayImg">
                    <img data-win-bind="src: imgSrc" />
                </div>
                <div class="win-type-x-large tenDayHighLow">
                    <span class="tenDayHigh" data-win-bind="innerHTML: hi"></span>
                    <span>/</span>  
                    <span class="tenDayLow" data-win-bind="innerHTML: low"></span>
                </div>
                <div class="tenDayFeelsLike">
                    <span>Feels like </span>
                    <span data-win-bind="innerHTML: feelsLike"></span>
                </div>
                <div class="tenDayChanceOfSnow">
                    <span>Chance of snow is </span>
                    <span data-win-bind="innerHTML: chanceOfSnow"></span>
                </div>
            </div>
        </div>
        <div id="tenDayListView" class="tenDayListView" data-win-control="WinJS.UI.ListView" data-win-options="{layout: {type: WinJS.UI.GridLayout}}"></div>
    </div>
</body>

Notice that the previous example uses WinJS CSS classes for typography to set an extra large font size for the date and high/low temperatures strings. That's what class="win-type-x-large" means in the tenDayDate and tenDayHighLow div elements.

The sample app uses the following CSS code to ensure that the TenDay.html page and its ListView control fills the parent container (the content area of the app's main page).


/* Size Page to full size of parent container */
.tenDayPage
{
    height: 100%;
}

/* List View Control */
.tenDayListView
{
    width: 100%;
    height: 100%;
}

Use CSS media queries to apply view-specific layouts and styles

By using CSS media queries, you can easily define different styles to apply to the HTML items in your app depending on the view state. You can use a separate media query for each view state, or you can combine media queries to apply the same set of styles to multiple view states. Here we describe the media queries that the Adaptive layout with CSS sample uses to lay out its main page and the items in the ten-day forecast.

Laying out the main page for snapped view

If you use the sample app, you'll notice that the size and layout of UI items don't change much between portrait, landscape, and fill view states. When the app is snapped, however, you see the following changes:

  • The UI items in the app header get smaller.
  • The layout changes from two columns and two rows, to a single-column where the header occupies the first row, and the main content area occupies the second row.

Header layout differences between snapped and other view states

These changes are applied through a CSS media query that defines a different set of styles specifically for the snapped view state:


@media (-ms-view-state: snapped) {
    .appGrid {
        display: -ms-grid;
        -ms-grid-columns: 1fr;
        -ms-grid-rows: 120px 1fr;
        width: 100%;
        height: 100%;
    }

    header[role=banner] {
        -ms-grid-column: 1;
        -ms-grid-row: 1;
        -ms-grid-columns: 60px 1fr;
        -ms-grid-rows: 1fr;
        display: -ms-grid;
    }

        header[role=banner] .win-backbutton {
            -ms-grid-column: 1;
            -ms-grid-row: 1;
            margin-left: 20px;
            margin-top: 75px;
        }

        header[role=banner] .titlearea {
            -ms-grid-column: 2;
            -ms-grid-row: 1;
            margin-top: 69px;
            max-width: 260px;
        }

    .appViewContentMask {
        -ms-grid-column: 1;
        -ms-grid-row: 2;
    }

In the preceding CSS code, a new value for the –ms-grid-columns property changes the app's main grid (appGrid) from two columns to one. The CSS code also defines a new child grid for the items in the app's header, and positions the header items in the new grid. Finally, the code moves the content area (appViewContentMask) from column 2, row 2 of the old grid, to column 1, row 2 of the new grid.

Laying out the ten-day forecast for different view states

You'll notice changes in the layout of the individual items in the ten-day forecast when you change between snapped and the other view states. In the portrait, landscape, and fill view states, the items are laid out vertically using a single-column grid. In the snapped view state, the items switch to a horizontal orientation using a two-column grid.

The following image shows what an items in the ten-day forecast looks like in the different view states.

Item layout differences between snapped and other view states

To achieve the different, view-dependent layouts, the sample uses CSS media queries to apply styles based on the current view state. The sample defines one set of styles for the portrait, landscape, and fill view states, and another set just for the snapped view state:


/* Define the template for full-screen, fill, and device-portrait */
@media (-ms-view-state: fullscreen-landscape), (-ms-view-state: filled), (-ms-view-state: fullscreen-portrait)
{
    .tenDayGrid
    {
        width: 190px;
        height: 250px;
        overflow: hidden;
        padding: 10px;
        display: -ms-grid;
        -ms-grid-columns: 1fr;
        -ms-grid-rows: (auto)[5];
    }

    .tenDayDate
    {
        -ms-grid-column: 1;
        -ms-grid-row: 1;
    }

    .tenDayImg
    {
        -ms-grid-column: 1;
        -ms-grid-row: 2;
    }

    .tenDayHighLow
    {
        -ms-grid-column: 1;
        -ms-grid-row: 3;
    }

    .tenDayFeelsLike
    {
        -ms-grid-column: 1;
        -ms-grid-row: 4;
    }
    .tenDayChanceOfSnow
    {
        -ms-grid-column: 1;
        -ms-grid-row: 5;
    }
}

/* Define the template for snapped */
@media (-ms-view-state: snapped)
{
    .tenDayDate
    {
        font-weight: 600;
    }

    .tenDayDate > .day
    {
       font-weight: 200;
    }

    .tenDayHighLow
    {
        font-weight: 300;
    }

    .tenDayFeelsLike, .tenDayChanceOfSnow
    {
        font-weight: 300;
    }

    .tenDayGrid
    {
        width: 250px;
        height: 150px;
        overflow: hidden;
        padding: 10px;
        display: -ms-grid;
        -ms-grid-columns: auto 1fr;
        -ms-grid-rows: (auto)[4];
    }

    .tenDayDate
    {
        -ms-grid-column: 1;
        -ms-grid-row: 1;
        -ms-grid-column-span: 2;
    }

    .tenDayImg
    {
        -ms-grid-column: 1;
        -ms-grid-row: 2;
        -ms-grid-row-span: 3;
    }

    .tenDayHighLow
    {
        -ms-grid-column: 2;
        -ms-grid-row: 2;
    }

    .tenDayFeelsLike
    {
        -ms-grid-column: 2;
        -ms-grid-row: 3;
    }
    .tenDayChanceOfSnow
    {
        -ms-grid-column: 2;
        -ms-grid-row: 4;
    }
}

Use JavaScript to process window resize events, if necessary

It's best to define as much of your app's layout as possible by using CSS and media queries. However, sometimes you'll need to use JavaScript to handle layout issues that CSS can't address.

For example, the sample app uses a Windows Library for JavaScript ListView control to display the items in the ten-day forecast, and switches the ListView between list and grid mode depending on the view state. When the sample app is in portrait, landscape, or fill view, the ListView uses grid mode to flow items vertically and horizontally to fill the parent container. When the app is snapped, the ListView uses list mode to arrange items in a vertical list.

To create view-specific layouts in JavaScript, register an event listener for the window resize event. In the event listener code, query the ViewManagement.ApplicationView.value property for the current view and configure the layout accordingly.

To learn more about how to detect screen resize events and view state changes in a Windows Store app, see the Windows 8 app developer blog post called Getting the most out of your pixels - adapting to view state changes.

The following example shows the Adaptive layout with CSS sample app's event listener for the window resize event. The event listener checks whether the app has entered the snapped view state and, if so, changes the ListView to list mode. If not, the event listener checks whether the app has just switched from snapped to some other view state. If that's the case, the ListView changes to grid mode.


window.addEventListener("resize", function (e) {
    var currentViewState = Windows.UI.ViewManagement.ApplicationView.value;
    var snapped = Windows.UI.ViewManagement.ApplicationViewState.snapped;

    if (currentViewState === snapped) {
        that.listView.layout = new WinJS.UI.ListLayout(); 
    }
    else if (lastViewState === snapped && currentViewState !== snapped) {
        that.listView.layout = new WinJS.UI.GridLayout();
    }

    lastViewState = currentViewState;
});

Summary

You should now understand how to use HTML, CSS, and JavaScript to create a fluid UI for your app that looks good and functions well in all view states.

Related topics

Adaptive layout with CSS sample
CSS media queries sample
Hands-on labs for Windows 8
Developing reader apps
Media Queries specification
ListView control
Windows Internet Explorer 10 Consumer Preview Guide for Developers

 

 

Build date: 11/28/2012

Did you find this helpful?
(1500 characters remaining)
© 2013 Microsoft. All rights reserved.