Chapter 6
Layout

Compared to other members of my family, I seem to need the least amount of sleep and am often up late at night or up before dawn. To avoid waking the others, I generally avoid turning on lights and just move about in the darkness (and here in the rural Sierra Nevada foothills, it can get really dark!). Yet because I know the layout of the house and the furniture, I don’t need to see much. I only need a few reference points like a door frame, a corner on the walls, or the edge of the bed to know exactly where I am. What’s more, my body has developed a muscle memory for where doorknobs are located, how many stairs there are, how many steps it takes to get around the bed, and so on. It’s really helped me understand how visually impaired people “see” their own world.

If you observe your own movements in your home and your workplace—probably when the spaces are lit!—you’ll probably find that you move in fairly regular patterns. This is actually one of the most important considerations in home design: a skilled architect looks carefully at how people in the home might move between primarily spaces like the kitchen, dining room, and living room, and even within a single workspace like the kitchen. Then they design the home’s layout so that the most common patterns of movement are easy and free from obstructions. If you’ve ever lived in a home where it wasn’t designed this way, you can very much appreciate what I’m talking about!

There are two key points here: first, good layout makes a huge difference in the usability of any space, and second, human beings quickly form habits around how they move about within a space, habits that hopefully make their movement more efficient and productive.

Good app design clearly follows the same principles, which is exactly why Microsoft recommends following consistent patterns with your apps, as described on Designing UX for apps and Design guidance for Windows Store apps. Those recommendations are not in any way whimsical or haphazard: they are the result of many years of research and investigation into what would really work best for apps and for Windows 8 as a whole. The placement of the charms, for instance, as well as commands on an app bar (as we’ll see in Chapter 7, Commanding UI”), arise from the reality of human anatomy, namely how far we can move our thumbs around the edges of the screen when holding a tablet device.

With page layout, in particular, the recommendations on Laying out an app page—about where headers and body content are ideally placed, the spacing between items, and so forth—can seem rather limiting, if not draconian. The silhouette, however, is meant to be a good starting point, not a hard-and-fast rule. What’s most important is that the shape of an app’s layout helps users develop a visual and physical muscle memory that can be applied across many apps. Research has showed that users will actually develop such habits very quickly, even within a matter of minutes, but of course those habits are not exact to specific pixels! In other words, the silhouette represents a general shape that helps users immediately understand how an app works and where to go for certain functions, just like you can easily recognize the letter “S” in many different fonts. This is very efficient and productive. On the other hand, when presented with an app that used a completely different layout (or worse, a layout that was similar to the silhouette but behaves differently), users must expend much more energy just figuring out where to look and where to tap, just as I would have to be much more careful late at night if you moved all my furniture around!

The bottom line is that there are very good reasons behind all the Windows Store app design recommendations, layout included. As I’ve said before, if you’re fulfilling the designer role for your app, study the guidelines referred to above. If someone else is fulfilling that role, make sure they study the guidelines! Either way, we’ll be reviewing the key principles in the first section of this chapter.

After that, our focus will be on how we implement layout designs, not creating the designs themselves. (Although I apparently got the mix of my parent’s genes that bestowed an aptitude for technical communication, my brother got the most of the genes for artistry!) For example, how does an app respond to view state changes to show the correct page design (for full-screen landscape, filled, snapped, and portrait)? How does the app handle varying display sizes and varying pixel densities?

We’ll also spend a little time with the CSS grid and a few other CSS layout features like flexbox and multicolumn text. Generally speaking, these are all CSS standards, so I expect that you already know quite a bit about them or can find full documentation elsewhere.34 We’ll thus cover the basics only briefly, spending more time understanding how these features are best applied within an app and those aspects that are unique to the Windows 8 environment (such as what are called snap points on a pannable/scrollable div).

I’ll remind you again that there are other UI elements like the app bar and flyouts that don’t participate in layout; I’ll cover these in other chapters. There are also auxiliary app pages that service contracts (such as Search and Settings) and exist outside your main navigation flow. These will employ the same layout principles covered in this chapter, but how and when they appear will be covered later.

Principles of Windows Store App Layout

Layout is truly one of the most important considerations in Windows app design. The principle of “content before chrome” means that most of what you display on any given app page is content, with little in the way of commanding surfaces, persistent navigation tabs, and passive graphical elements like separators, blurs, and gradients that don’t in themselves mean anything. Another way of putting this is that content itself should be directly interactive rather than composed of passive elements that are acted upon when the user invokes some other command. Semantic zoom is a good example of such interactive content—instead of needing buttons or menus elsewhere in the app to switch between views, the capability is inherent in the control itself, with the small zoom button appearing only when needed for mouse users. Other app commands, for the most part, are similarly placed on UI surfaces that appear when needed through app bars and other flyouts, as we’ll see in Chapter 7.

In short, “content before chrome” means immersing the user in the experience of the content rather than distracting them with nonessentials. In Windows app design, then, emphasis is given to the space around and between content, which serves to organize and group content without the need for lines and boxes. These essentially transparent “space frames” help consumer’s eyes focus on the content that really matters. Windows app design also uses typography (font size, weight, color, etc.) to convey a sense of structure, hierarchy, and relative importance of different content. That is, because the text on a page is already content, why not use its characteristics—the typography—to communicate what is often done with extraneous chrome? (As with the layout silhouette, the general use of the Segoe UI font within app design is not a hard-and-fast requirement, but a starting point. Having a consistent type ramp for different headings is more important than the font.)

As an example, Figure 6-1 shows a typical desktop or web application design for an RSS reader. Notice the persistent chrome along the top and bottom: search commands, navigation tabs, navigation controls, and so forth. This takes up perhaps 20% of the screen space. In what remains, nearly two-thirds is taken up by organizational elements, leaving 20–25% of the screen space for the content we actually care about: the article.

Figure 6-2 shows a Windows Store app design for the same content. Notice how all the ancillary commands have been moved offscreen. Search would be accomplished through the Search charm; Settings through the Settings charm; adding feeds, refresh, and navigation through commands on to the app bar; and switching views through semantic zoom. Typography is used to convey the hierarchy instead of a folder control, which then leaves the bulk of the display—nearly 75%—for the content. As a result, we can see much more of that content than before, which creates a much more immersive and engaging experience, don’t you think?

Image

FIGURE 6-1 A typical desktop or web application design that emphasizes chrome at the expense of content.

Image

FIGURE 6-2 The same app as Figure 6-1 reimagined with one possible application of Windows app design, where most of the chrome has disappeared, leaving much more space for content. An alternate design could emphasize images much more than text.

Even where typography is concerned, Windows app design encourages the use of distinct font sizes, again called the typographic ramp, to establish a sense of hierarchy. The default WinJS stylesheets—ui-light.css and ui-dark.css—provide four fixed sizes where each level is proportionally larger than the previous (42pt = 80px, 20pt = 40px, etc.), as shown in Figure 6-3. These proportions allow users to easily establish an understanding of content structure with just a glance. Again, it’s a matter of encouraging habit and muscle memory, and Microsoft’s research has shown that beyond this size granularity, users are generally unable to differentiate where a piece of content fits in a hierarchy.

Image

Figure 6-3 The typographic ramp of Windows Store app design, shown in both the ui-dark.css (left) and ui-light.css (right) stylesheets.

Within the body of content, then, Windows app design encourages these layout principles:

• Let content flow from edge to edge.

• Keep ergonomics in mind: pan along the long edge of the view (primarily horizontal in landscape views, vertical in snapped view and possibly portrait).

• Pan on a single axis only to create a sense of stability and to support swiping to select (as with the ListView controls), or employ rails to limit panning directions to a single axis.

• Create visual alignment, structure, and clarity with the Windows 8 silhouette, aligning elements on a grid for consistency. Refer again to Laying out an app page. This shape is what allows a consumer’s eyes to recognize something as a Store app without having to think about it, which provides a feeling of familiarity and confidence.

As I’ve mentioned before, the project templates in Visual Studio and Blend have these principles baked right in and thus provide a convenient starting point for apps. Even if you start with the Blank App template, the others like the Grid App will serve as a reference point. This is exactly what we did with the Here My Am! app in Chapter 2, “Quickstart.”

The other important guiding principle that’s relevant to layout is “snap and scale beautifully.” This means making sure you design every page in your app to handle all four view states and to be appropriately adaptive across different display resolutions and pixel densities. We’ll look at this subject in the “View States and the Many Faces of Your Display” section below. First, however, let’s look at a little piece of core layout code.

Quickstart: Pannable Sections and Snap Points

In Chapter 5, “Collections and Collection Controls,” we spent a little time looking at when a ListView control was the right choice and when it wasn’t. One of the primary cases where developers have inappropriately attempted to use a ListView is to implement a home or hub page that contains a variety of distinct content groups arranged in columns, as shown in Figure 6-4 and explained on Navigation design for Windows Store apps. At first glance this might look like a ListView, but because the data it’s representing really isn’t a collection, just a layout of fixed content, it makes sense to use tried-and-true HTML and CSS for the job!

I point this out because with all the great controls that WinJS provides, it’s easy to forget that everything you know about HTML and CSS still applies in Store apps. After all, those controls are in themselves just blocks of HTML and CSS with some additional methods, properties, and events.

Image

FIGURE 6-4 The layout of a typical home or hub page of a Store app with a fixed header (1), a horizontally pannable section (2), and content sections or categories (3).

Laying Out the Hub

Let’s see how we’d use straight HTML and CSS to implement the pannable section of the hub page in Figure 6-4. Referring first to Laying out an app page, we know that the padding between groups should be four units of 20px each, or 80px. Most of the groups themselves should be square, except for the second one which is only half the width. On a baseline 1366x768 display, the height of each section would be 768px minus 128px (for the header) minus the minimum 50px on the bottom, which leaves 590px (if we added group headings for each section, we’d subtract another 40px). So a square group on the baseline display would be 590px wide (we’d set the actual height to 100% of its containing grid cell). The total width of the section will then be (590 * 4 full-size sections) + (295 * 1 half-width section) + (80 * 4 for the separator gaps). This equals 2975px. To this we’ll add border columns of 120px on the left (according to the silhouette) and 80px on the right, for a total of 3175px.

To create a section with exactly this layout, we can use a CSS grid within a block element. To demonstrate this, run Blend and create a new project with the Navigation App template (so we just get a basic page with the silhouette and not all the secondary pages). Within the the section element of pages/home/home.html, create another div element and give is a class of hubSections:

<section aria-label="Main content" role="main">
    <div class="hubSections">
    </div>
</section>

In pages/home/home.css, add a few style riles. Give overflow-x: auto to the section element, and lay out the grid in the hubSections div, using added columns on the left and right for spacing (removing the margin-left: 120px from the section and adding it as the first column in the div):

.homepage section[role=main] {
    overflow-x: auto;
}
.homepage .hubSections {
    width: 2975px;
    height: 100%;
    display: -ms-grid;
    -ms-grid-rows: 1fr 50px;
    -ms-grid-columns: 120px 2fr 80px 1fr 80px 2fr 80px 2fr 80px 2fr 80px;
}

With just these styles we can already see the hub page taking shape in Blend by zooming out in the artboard:

Image

Now let’s create the individual sections, each one starting as a div that we add in pages/home/home.html:

<section aria-label="Main content" role="main">
    <div class="hubSections">
        <div class="hubSection1"></div>
        <div class="hubSection2"></div>
        <div class="hubSection3"></div>
        <div class="hubSection4"></div>
        <div class="hubSection5"></div>
    </div>
</section>

and style them into their appropriate grid cells with 100% width and height. I’m showing hubSection1 here as the others are the same with just a different column number (4, 6, 8, and 10, respectively):

.homepage .hubSection1 {
    -ms-grid-row: 1;
    -ms-grid-column: 2; /* 4 for hubSection2, 6 for hubSection3, etc. */
    width: 100%;
    height: 100%;
}

All of this is implemented in the HubPage example included with this chapter.

Laying Out the Sections

Now we can look at the contents of each section. Depending on what you want to display and how you want those sections to interact, you can again just use layout (CSS grids or perhaps flexbox) or use a control like ListView. hubSection3 and hubSection5 have gaps at the end, so they might be ListView controls with variable items. Note that if we created lists with more than 9 or 6 items, respectively, we’d want to adjust the column size in the overall grid and make the section element width larger, but let’s assume the design calls for a maximum of 9 and 6 items in those sections.

Let’s also say that we want each section to be interactive, where tapping an item would navigate to a details page. (Not shown in this example are group headers to navigate to a group page.) We’ll just then use a ListView in each, where each ListView has a separate data source. For hubSection1 we’ll need to use cell spanning, but the rest of the groups can just use declarative templates. The key consideration with all of these is to style the items so that they fit nicely into the basic dimensions we’re using. And referring again back to the silhouette, the spacing between image items should be 10px and the spacing between columns of mixed content (hubSection4 and hubSection5) should be 40px (which can be set with appropriate CSS margins).

Hint If you need to make certain areas of your content unselectable, use the -ms-user-select attribute in CSS for a div element. Refer to the Unselectable content areas with -ms-user-select CSS attribute sample. How’s that for a name?

Snap Points

If you run the HubPage example and pan around a bit using inertial touch gestures (that is, those that continue panning after you’ve released your finger, explained more in Chapter 9, “Input and Sensors”), you’ll notice that panning can stop in any position along the way. You or your designers might like this, but it also makes sense in many scenarios to automatically stop on a section or group boundary. This can be accomplished for touch interactions using CSS styles for snap points as described in the following table. These are styles that you add to a pannable element alongside overflow styles, otherwise they have no effect. Documentation for these (and some others) can be found on the CSS reference for Touch: Zooming and Panning.

Image

In the table, <length> is a floating-point number, followed by an absolute units designator (cm, mm, in, pt, or pc) or a relative units designator (em, ex, or px).

To add snap points for each of our hub sections, then, we only need to add two snap points styles after overflow-x:

.homepage section[role=main] {
    overflow-x: auto;
    -ms-scroll-snap-type: mandatory;
    -ms-scroll-snap-points-x: snapList(0px, 670px, 1045px, 1715px, 1795px);
}

Note that the snap points indicated here include the 120px left border so that each one aligns the section underneath the header text. The 0px point thus snaps to the first section, 670px to the second (80px separator plus 590px width of the first section), and so on. The last snap point of 1795px, however, doesn’t follow this rule because the div can’t pan any further past that point. This means we’ll snap partway into the next-to-last section, but bring the last section and its 80px right border into view.

With these changes you’ll now find that panning around stops nicely (with animations) on the section boundaries. Do note that for a hub page like this, proximity snapping is usually more appropriate. Mandatory snap points are intended more for items that can’t be interacted with or consumed without seeing their entirety, such as flipping between pictures, articles, and so on. (The FlipView control uses these.)

For more on this topic, including some of the other -ms-scroll-* and -ms-content-zoom-* styles, such as scroll rails, refer to the HTML scrolling, panning, and zooming sample . Do note also that snap points are not presently supported on the ListView control, as they are intended for use with your own layout.

Also be clear that snap points are a touch-only feature; if you want to provide the same kind of behavior with mouse and/or keyboard input, you’ll need to do such work manually along the lines of how the FlipView control handles transition between items.

The Many Faces of Your Display

If there’s one certainty about layout for a Windows Store app, it’s that its display space will likely change over the lifetime of an app and change frequently. For one, auto-rotation—especially on tablet and slate devices—makes it very quick and simple to switch between landscape and portrait orientations (unlike having to configure a display driver). Second, a device may be connected to an external display, meaning that apps need to adjust themselves to different resolutions on the fly and possibly also different pixel densities. Third, users have the ability in landscape mode to “snap” apps to the left or right side of the screen, where the snapped app is shown in a 320px wide area and another in the “filled” area that occupies the remainder of the display. This is accomplished using touch or mouse gestures, or using the Windows+. (period) and Windows+> (shift+period) keystrokes. (Snapped view requires a display that’s at least 1366x768; otherwise it’s disabled.)

You definitely want to test your app with all of these variances: view states, display sizes, and pixel densities. View states can be tested directly on any given machine, but for the latter two, the Visual Studio simulator and the Device tab of Blend let you simulate different conditions. Our question now is how an app handles them.

View States

We already got an introduction to the four view states in Chapter 1, “The Life Story of a Windows Store app” (see Figure 1-6). Let’s now add the next level of precision as described in the following table, which includes an image of the space occupied by the app, a description of the view state, and the identifiers for that state as found in both WinRT (in the Windows.UI.ViewManagement.-ApplicationViewState enumeration) and the -ms-view-state media feature for CSS media queries:

Image

Image

Remember again that every page of your app needs to be prepared for all four view states (with some exceptions as described in the sidebar below, “Preferred Orientation and Locking Orientation”). View states are always under the user’s control, so any page can be placed into any view state at any time, even on startup. Repeat this like a mantra, because many designers and developers forget this fact!

Note It’s possible that your app might be launched directly into snapped view, as through a user gesture that pulls the app from the left edge of the screen to a snapped state. So be prepared for this possibility. Remember also that any extended splash screen in your app is a page that is also subject to view states. In fact, it’s highly likely that a user will snap an app that’s taking a while to load! At the same time, you cannot programmatically control your app’s view state on activation, so it never needs to be saved or restored as part of session state.

An app’s design should thus include all view states for each page, just like we did with the Here My Am! wireframes in Chapter 2. At the same time, handling view states for every page this does not mean four distinct implementations of the app. View states are just that: they are different views of the same page content as described on Guidelines for snapped and fill views. That is, switching between view states always maintains the state of the app and the page—it never changes modes or navigates to another page. The only exception to this rule is that if an app can’t reasonably operate in snap state (like a game that needs a certain amount of screen space to be playable), it can display a message to that effect along with instructions to “Tap here to resume,” which reflects the user’s goal in such a gesture. In response to such a tap, the app can call Windows.UI.ViewManagement.Application-View.tryUnsnap, as demonstrated in the Snap sample.35 Don’t use this as an excuse to cut corners, however; try as much as possible to keep the app functional in the snapped state.

Hint Think of the snapped view of a page as a kind of heads-up view in which the most essential information from a page is really highlighted. In other words, see snapped view as an opportunity rather than a burden.

On the flip side, some apps should think about what to do with extra vertical space. A widescreen video in the snapped state will occupy only a small portion of that space, leaving room for, say, additional information about the video, recommendations, playlists, and so on, that wouldn’t normally be available when running full screen. In this way, users will find added value in switching to the snapped state.

View states aside, it’s appropriate for some apps to start in a specific orientation and/or to lock the orientation, effectively ignoring portrait/landscape changes. A movie player, for instance, will generally want to stay in landscape mode, meaning that the fullscreen-landscape and fullscreen-portrait modes are identical—then you can watch videos while laying sideways with a tablet propped up on a chair.

To be clear, the app must still honor the three landscape view states: fullscreen-landscape, filled, and snapped. Preferred orientation is specifically about portrait vs. landscape, and this affects the orientation of your splash screen and other pages in your app. It also enables automatic orientation switching when you switch between your app and others that don’t have the same preference.

To tell Windows about your preferences, check the appropriate Supported Orientation boxes in the Application UI tab of the manifest designer:

Image

The many details about how all this works are found on the InitialRotationPreference page in the documentation. It will also tell you about the Windows.Graphics.Display.-DisplayProperties.autoRotationPreferences and currentOrientation properties to programmatically control orientation behaviors. For demonstrations, refer to the Device auto rotation preferences sample.

Handling View States

As I just mentioned, handling the different view states doesn’t mean changing the mode of an app nor reimplementing a page. Generally, you should try to have feature parity across the states, but in cases like snapped view, especially, the reduced screen real estate will necessitate simplifying the content.

It’s best to think about view states simply in terms of the visibility of elements, the size of elements, and their layout on the page. In this way, most of what you need to do can be achieved through CSS media queries using the -ms-view-state feature. We saw this again in the Here My Am! app of Chapter 2. The Grid App project template also demonstrates this. Here’s how those media queries appear in CSS:

@media screen and (-ms-view-state: fullscreen-landscape) {
   /* ... */
}

@media screen and (-ms-view-state: filled) {
   /* ... */
}

@media screen and (-ms-view-state: snapped) {
   /* ... */
}

@media screen and (-ms-view-state: fullscreen-portrait) {
   /* ... */
}

/* Syntax for combining media queries (comma-separated) */
@media screen and (-ms-view-state: fullscreen-landscape),
screen and (-ms-view-state: fullscreen-portrait), screen and (-ms-view-state: filled) {
   /* ... */
}

It’s also perfectly reasonable to add other clauses to these queries, such as and (min-width: "1600px"), as you might be making various other adjustments based on screen sizes.

For Store apps, use the view state features in media queries instead of the CSS orientation states (landscape and portrait), which are simply derived from the relative width and height of the display and don’t distinguish states like snapped. In other words, the Windows view states are more specific to the platform and reflect states that the standard CSS does not, helping your app understand not only its available real estate but also the mode in which it’s running.36

For example, according to the standard CSS algorithm, both the fullscreen-portrait and snapped states will appear as orientation: portrait because the aspect ratio is more vertical than horizontal. However, snapped view implies a different user intent than fullscreen-portrait: in snapped view you want to show the most essential parts of an app rather than trying to replicate your portrait layout in a 320-pixels-wide space.

The general practice is to place all your full-screen landscape rules at the top of your CSS file and then make specific adjustments within the specific media queries. We did this with Here My Am! in Chapter 2, where the default styles worked for fullscreen-landscape and filled as-is, so we needed specific rules only for snapped and fullscreen-portrait.

Tip When styling your app in Blend, there’s a visual affordance in the Style Rules pane that lets you control the exact insertion point of any new CSS styles in the given stylesheet. With this—the orange line shown in the graphic below and shown in Video 2-2 of the companion content—you can indicate where to insert styles for specific media queries and within that media query:

Image

In a few cases, handling media queries in declarative CSS alone won’t be sufficient. When the primary content display on a page is a horizontally panning ListView with GridLayout, you typically switch that control over to ListLayout in snapped view. You might also, as suggested on Guidelines for snapped and fill views, change a list of buttons to a single drop-down select element to offer the same functionality through a more compact UI. Such things require a little bit of JavaScript.

For these purposes you can employ the standard Media Query Listener API in JavaScript. This interface (part of the W3C CSSOM View Module, see http://dev.w3.org/csswg/cssom-view/) allows you to add handlers for media query state changes. To listen for the snapped state, for instance, you can use code like this:

var mql = window.matchMedia("(-ms-view-state: snapped)");
mql.addListener(styleForSnapped);

function styleForSnapped() {
    if (mql.matches) {
        //...
    }
}

// Set up listeners for other view states: full-screen, fill, and device-portrait
// or send all media queries to the same handler and check the current state therein.

You can see that the media query strings you pass to window.matchMedia are the same as used in CSS directly, and in the handler you can, of course, perform whatever actions you need from JavaScript.

Tip Be sure to test your view states on the resuming event, as display characteristics might have changed, such as plugging in a different monitor or going to the Settings charm > Change PC Settings > Ease of Access and toggling Make Everything on the Screen Bigger. That is, it’s possible to bring your app from the background (suspended state) directly into snapped view, and screen dimensions might also have changed while you’re suspended. So test your layout when resuming into snapped view and when resuming into different screen dimensions.

When handling view states (or window.onresize events), you can obtain exact dimensions of your app window through the window.innerWidth and window.innerHeight properties. The document.body.-clientWidth and document.body.clientHeight properties will be the same, as will be the clientWidth and clientHeight properties of any element (like a div) that occupies 100% of the document body. Within the resize event, the args.view.outerWidth and args.view.outerHeight properties are also available.

In CSS there are also variables for the viewport height and viewport width: vh and vw. You can prefix these with a percentage number, such that 100vh is 100% of the viewport height, and 3.5vw is 3.5% of the viewport width. These variables can also be used in CSS calc expressions.

The current view state is available through the Windows.UI.ViewManagement.Application-View.value property. This value comes from the Windows.UI.ViewManagement.Application-ViewState enumeration as shown in the earlier table. We’ve seen a few uses of this in earlier chapters. For instance, page controls (discussed in Chapter 3, “App Anatomy and Page Navigation”) typically check the view state within their ready method and directly receive those states within their updateLayout method. In fact, every method of the groupedItems page control in the Grid App project template is sensitive to the view state. Take a look at the code in pages/groupedItems/groupedItems.js:

// A few lines and comments are omitted
var appView = Windows.UI.ViewManagement.ApplicationView;
var appViewState = Windows.UI.ViewManagement.ApplicationViewState;
var nav = WinJS.Navigation;
var ui = WinJS.UI;

ui.Pages.define("/pages/groupedItems/groupedItems.html", {

    initializeLayout: function (listView, viewState) {
       if (viewState === appViewState.snapped) {
           listView.itemDataSource = Data.groups.dataSource;
           listView.groupDataSource = null;
           listView.layout = new ui.ListLayout();
       } else {
           listView.itemDataSource = Data.items.dataSource;
           listView.groupDataSource = Data.groups.dataSource;
           listView.layout = new ui.GridLayout({ groupHeaderPosition: "top" });
       }
    },

    itemInvoked: function (args) {
        if (appView.value === appViewState.snapped) {
            // If the page is snapped, the user invoked a group.
            var group = Data.groups.getAt(args.detail.itemIndex);
            nav.navigate("/pages/groupDetail/groupDetail.html", { groupKey: group.key });
        } else {
            // If the page is not snapped, the user invoked an item.
            var item = Data.items.getAt(args.detail.itemIndex);
            nav.navigate("/pages/itemDetail/itemDetail.html",
                { item: Data.getItemReference(item) });
            }
        },

        ready: function (element, options) {
           // ...
           this.initializeLayout(listView, appView.value);
           // ...
        },

    // This function updates the page layout in response to viewState changes.
    updateLayout: function (element, viewState, lastViewState) {
        var listView = element.querySelector(".groupeditemslist").winControl;
        if (lastViewState !== viewState) {
            if (lastViewState === appViewState.snapped ||
                viewState === appViewState.snapped) {
                var handler = function (e) {
                    listView.removeEventListener("contentanimating", handler, false);
                    e.preventDefault();
                }
                listView.addEventListener("contentanimating", handler, false);
                this.initializeLayout(listView, viewState);
            }
        }
    }
});

First, the initializeLayout method that’s called from both ready and updateLayout checks the current view state and adjusts the ListView control accordingly. If you remember from Chapter 5, it’s perfectly allowable to change a ListView’s layout and data source properties on the fly; here we use a ListLayout with a list of groups for snapped view and a GridLayout with grouped items in all others. This demonstrates how we’re showing the same content but in a more concise manner by hiding the individual items in snapped view. Because of this, itemInvoked also has to check the view state because the list items are groups in snapped view and should navigate to a group details page instead of an item page.

As for updateLayout, this is invoked from a window.onresize event handler in the PageControlNavigator code (see js/navigator.js in the Grid App project template). That handler passes the new and previous view states to updateLayout. If that function detects that we’re switching to or from snapped state, it resets the ListView through initializeLayout. And because we’re changing the ListView’s data source, there’s no need to play entrance or transition animations. The little trick that’s played with the contentanimating event here simply suppresses those.

The fullscreen-landscape and fullscreen-portrait view states suggest something of how a device is actually oriented in physical space, but such information is more accurately derived from properties of the Windows.Graphics.Display.DisplayProperties object. Specifically, the currentOrientation property contains a value from Windows.Graphics.Display.DisplayOrientations that indicates how the device is rotated in relation to its nativeOrientation (and an orientationchanged event fires when needed). This can tell you, for example, whether the device is being held upside-down against the sky, which would be useful for any kind of augmented reality app such as a star chart.

Similarly, the APIs in Windows.Devices.Sensors, specifically the SimpleOrientationSensor and OrientationSensor classes can provide more information from the hardware itself. These are covered in Chapter 9.

Screen Size, Pixel Density, and Scaling

I don’t know about you, but when I first read that the snapped area was always 320 pixels—real pixels, not a percentage of the screen width—it really set me wondering. Wouldn’t that give a significantly different user experience on different monitors? The answer is actually no. 320 pixels is about 25% of the baseline 1366x768 target display, which means that the remaining 75% of the screen is a familiar 1024x768. And on a 10-inch screen, it means that snap area is about the 2.5 physical inches wide. So far so good.

With a large monitor, on the other hand, let’s say a 2560x1440 monster, those 320 pixels would only be 12.5% of the width, so the layout of the whole screen looks quite different. However, given that such monitors are in the 24-inch range, those 320 pixels still end up being about 2.5 physical inches wide, meaning that the snapped area gives essentially the same visual experience as before, just now with much more vertical space to play with and much more remaining screen space.

This now brings up the question of pixel density—what happens if your app ends up on a really small screen that also has a really high resolution? Obviously, 320 pixels on the latter display would be little more than an inch wide. Anyone got a magnifying glass?

Fortunately, this isn’t anything a Store app has to worry about…almost. The main user benefit for such displays is greater sharpness, not greater density of information. Touch targets need to be the same size on any size display no matter how many pixels it occupies, because human fingers don’t change with technology! To accommodate this, Windows automatically scales down the effective resolution that’s reported to apps, which is to say that whatever coordinates you use within your app (in HTML, CSS, and JavaScript) are automatically scaled up to the necessary device resolution when the UI is actually rendered. This happens at within the low-level HTML/CSS rendering engine in the app host so that everything is drawn directly against native device pixels for maximum sharpness.

As for the “almost” above, the one place where you do need to care about pixel density is with raster graphics, as we discussed in Chapter 3 for your splash screen and tiles. We’ll return to this shortly in the “Graphics that Scale Well” section below.

Display sizes and pixel densities can both be tested again using the Visual Studio simulator or the Device tab in Blend. The latter, shown in Figure 6-5, indicates the applicable DPI and scaling factor. 100% scale means the device resolution is reported directly to an app. 140% and 180%, on the other hand, indicate that scaling is taking place. With the 10.6” 2560x1440 setting with 180%, for example, the app will see dimensions of 1422x800 (2560/1.8 by 1440/1.8), which is very close to the standard 1366x768 display; similarly, the 10.6: 1920x1080 setting with 140% scaling will appear to the app as 1371x771 (1920/1.4 by 1080/1.4). In both cases, a layout designed for 1366x768 is completely sufficient though you can certainly be as precise as you want.

Tip If you have an app with a fixed layout (see “Fixed Layouts and the ViewBox Control” later on), you can address pixel density issues by simply using graphical assets that are scaled to 200% of your standard design. This is because a fixed layout can be scaled to arbitrary dimensions, so a 200% image scales well in all cases. Such an app does not need to provide 100%, 140%, and 180% variants of its images.

Image

FIGURE 6-5 Options for display sizes and pixel densities in Blend’s Device tab.

As noted earlier with view states, you can programmatically determine the exact size of your app window through the window.innerWidth and window.innerHeight properties, the document.body.-clientWidth and document.body.clientHeight properties, and the clientWidth and clientHeight properties of any element that occupies 100% of the body. Within window.onresize, you can use these (or the args.view.outerWidth and args.view.outerHeight properties) to adjust the app’s layout for changes in the overall display. Of course, if you’re using something like the CSS grid with fractional rows and columns to do your layout, much of that will be handled automatically.

In all cases, these dimensions will already reflect automatic scaling for pixel densities, so they are the dimensions against which you want to determine layout. If you want to know the physical display dimensions, on the other hand, you’ll find these in the window.screen.width and window.-screen.height properties. Other aspects of the display can be found in the Windows.Graphics.-Display.DisplayProperties object, such as the logicalDPI and the current resolutionScale. The latter is a value from the Windows.Graphics.Display.ResolutionScale enumeration, one of scale100Percent, scale140Percent, and scale180Percent. The actual values of these identifiers are 100, 140, and 180 so that you can use resolutionScale directly in calculations.

Working with different device capabilities provides a great opportunity to work with remote debugging as described on Running apps on a remote machine. This will help you test your app on different displays without needing to set up Visual Studio on each one, and it also gives you the benefit of multimonitor debugging. You only need to install and run the remote debugging tools on the target machine and make sure it’s connected with a cable to the same network as your development machine. (You might need to buy a small USB-Ethernet adapter if your device doesn’t have a suitable port—remote debugging doesn’t work over the Internet, and it doesn’t work over wireless networks.) The Remote Debugging Monitor running on the remote machine will announce itself to Visual Studio running on your development machine. Note that the first time you run the app remotely, you’ll be prompted to obtain a developer license for that machine, so it will need to be connected to the Internet during that time.

Graphics That Scale Well

Variable screen sizes and pixel densities can present a bit of a challenge to apps, not just in layout but also in making sure that graphical assets always look their best. You can certainly draw graphics directly with the HTML5 canvas; what I want to specifically address are predrawn assets.

HTML5 scalable vector graphics (SVGs) are very handy here. You include inline SVGs in your HTML (including page fragments), or you can keep them in separate files and refer to them in an img.src attribute. One of the easiest ways to use an SVG is to place an img element inside a proportionally sized cell of a CSS grid and set the element’s width and height styles to 100%. The SVG will then automatically scale to fill the cell, and since the cell will resize with its container, everything is handled automatically.

One caveat with this approach is that the SVG will be scaled to the aspect ratio of the containing grid cell, which isn’t always what you want. To control this behavior, make sure the SVG has viewBox and preserveAspectRatio attributes where the viewBox aspect ratio matches that defined by the SVG’s width and height properties:

    <svg
     xmlns:svg="http://www.w3.org/2000/svg"
     xmlns="http://www.w3.org/2000/svg"
     xmlns:xlink="http://www.w3.org/1999/xlink"
     version="1.0"
     width="300"
     height="150"
     viewBox="0 0 300 150"
     preserveAspectRatio="xMidYMid meet">

Of course, you don’t always have nice vector graphics. Bitmaps that you include in your app package, pictures you load from files, and raster images you obtain from a service won’t be so easily scalable. In these cases, you’ll need to be aware of and apply the current scaling factor appropriately.

For assets in your app package, we already saw how to work with varying pixel densities in Chapter 3 through the .scale-100, .scale-140, and .scale-180 file name suffixes. These work for any and all graphics in your app, just as they do for the splash screen, tile images, and the other graphics referenced by the manifest. So if you have a raster graphic named banner.png, you’ll create three graphics in your app package called banner.scale-100.png, banner.scale-140.png, and banner.scale-180.png. You can then just refer to the base name in an element or in CSS, as in <img src= "images/banner.png"> and background-image: url(’images/banner.png’), and the Windows resource loader will magically load the appropriately scaled graphic automatically. (If files with .scale-* suffixes aren’t found, it will look for banner.png directly.) We’ll see even more such magic in Chapter 17, “Apps for Everyone,” when we also include variants for different languages and contrast settings that introduce additional suffixes of their own.

If your developer sensibilities object to this file-naming scheme, know that you can also use similarly named folders instead. That is, create scale-100, scale-140, and scale-180 folders in your images folder and place appropriate files with unadorned names (like banner.png) therein.

In CSS you can also use media queries with max-resolution and min-resolution settings to control which images get loaded. Remember, however, that CSS will see the logical DPI, not the physical DPI, so the cutoffs for each scaling factor are as follows (the DPI values here are slightly different from those given in documentation because they come from empirical tests; the docs suggest 134, 135, and 174 dpi, respectively):

@media all and (max-resolution: 133dpi) {
    /* 100% scaling */
}

@media all and (min-resolution: 134dpi) {
    /* 140% scaling */
}

@media all and (min-resolution: 172dpi) {
    /* 180% scaling */
}

As explained in the Guidelines for scaling to pixel density, such media queries are especially useful for images you obtain from a remote source, where you might need to amend the specific URI or the URI query string. See Chapter 13, “Tiles, Notifications, the Lock Screen, and Background Tasks,” in the section “Using Local and Web Images” for how tile updates handle this for scale, contrast, and language.

Programmatically, you can again obtain logicalDpi and resolutionScale properties from the Windows.Graphics.Display.DisplayProperties object. Its logicaldpichanged event (a WinRT event) can also be used to check for changes in the resolutionScale, since the two are always coupled. Usage of these APIs is demonstrated in the Scaling according to DPI sample.

If your app manages a cache of graphical assets, by the way, especially those downloaded from a service, organize them according to the resolutionScale for which that graphic was obtained. This way you can obtain a better image if and when necessary, or you can scale down a higher resolution image that you already obtained. It’s also something to be aware of with any app settings you might roam, because the pixel density and screen size may vary between a user’s devices.

Adaptive and Fixed Layouts for Display Size

Just as every page of your app needs to be prepared for different view states, it should also be prepared for different screen sizes. On this subject, I recommend you read the Guidelines for scaling to screens, which has good information on the kinds of display sizes your app might encounter. From this we can conclude that the smallest snapped view you’ll ever encounter is 320x768, the minimum filled view is 1024x768, and the minimum full-screen views (portrait and landscape) are 1280x800 and 1366x768. These are your basic design targets.

From there, displays only get larger, so the question becomes “What do you do with more space?” The first part of the answer is “Fill the screen!” Nothing looks more silly than an app running on a 27” monitor that was designed and implemented with only 1366x768 in mind, because it will only occupy a quarter to half of the screen at best. As I’ve said a number of times, imagine the kinds of reviews and ratings your app might be given in the Windows Store if you don’t pay attention to details like this!

The second part of the answer depends on your app’s content. If you have only fixed content, which is common with games, then you’ll want to use a fixed layout that scales to fit. If you have variable content, meaning that you should show more when there’s more screen space, then you want to use an adaptive layout. Let’s look at both of these in turn.

In PC Settings (Settings charm > Change PC Settings in the lower-right corner), there is an option within Ease of Access to “Make everything on your screen bigger.” Turning this on effectively enlarges the display by about 40%, meaning that the system will report a screen size to the app that’s about 30% smaller than the current scaled resolution (similar to the 140% scaling level). Fortunately, this setting is disabled if it would mean reporting a size smaller than 1024x768, which always remains the minimum screen size your app will encounter. In any case, when this setting is changed it will trigger a Windows.Graphics.Display.DisplayProperties.-logicalDpiChanged event.

Fixed Layouts and the ViewBox Control

A fixed layout is the best choice for apps that aren’t oriented around variable content, because there isn’t more content to show on a larger screen. Such an app instead need to scale its output to fill the display as best it can, depending on whether it needs to maintain an aspect ratio.

An app can certainly obtain the dimensions of its display window and redraw itself accordingly. Every coordinate in the app would be a variable in this case, and elements would be resized and laid out relative to one another. Such an approach is great when an app can adapt its aspect ratio to that of the screen, thereby filling 100% of the display.

You can do the same thing with a fixed aspect ratio by placing limits on your coordinates, perhaps by using an absolute coordinate system to which you then apply your own scaling factor.

Because this is the more common approach, WinJS provides a built-in layout control for exactly this purpose: WinJS.UI.ViewBox (not to be confused with the SVG viewBox attribute). Like all other WinJS controls, you can declare this using data-win-control in HTML as follows, where the ViewBox element can contain one and only one child element:

<div data-win-control="WinJS.UI.ViewBox">
    <div class="fixedlayout">
        <p>Content goes here</p>
    </div>
</div>

This is really all you ever see with the ViewBox as it has no other options or properties, no methods, and no events—very simple! Note also that because the ViewBox is just a control, you can use it for any fixed aspect-ratio content in an otherwise adaptive layout; it’s not only for the layout of an entire page.

To set the reference size of the ViewBox—the dimensions against which you’ll write the rest of your code—simply set the width and height styles of the child element in CSS. For example, to set a base size of 1024x768, we’d set those properties in the rule for the fixedlayout class:

.fixedlayout {
    width: 1024px;
    height: 768px;
}

Once instantiated, the ViewBox simply listens for window.onresize events, and it then applies a CSS 2D scaling transform to its child element based on the difference between the reference size and the actual size. This preserves the aspect ratio. This works to scale the contents up as well as down. Automatic letterboxing or sidepillars are also applied around the child element, and you can set the appearance of those areas (really any area not obscured by the child element) by using the win-viewbox class. As always, scope that selector to your specific control if you’re using more than one ViewBox in your app, unless you want styles to be applied everywhere.

The basic structure above is what you get with a new app created from the Fixed Layout App project template in Visual Studio and Blend. As shown here, it creates a layout with a 1024x768 reference size, but you can use whatever dimensions you like.

The CSS for this project template reveals that the whole page itself is actually styled as a CSS flexbox to make sure the ViewBox is centered, and that the fixedlayout element is given a default grid:

html, body {
    height: 100%;
    margin: 0;
    padding: 0;
}

body {
    -ms-flex-align: center;
    -ms-flex-direction: column;
    -ms-flex-pack: center;
    display: -ms-flexbox;
}

.fixedlayout {
    -ms-grid-columns: 1fr;
    -ms-grid-rows: 1fr;
    display: -ms-grid;
    height: 768px;
    width: 1024px;
}

If you create a project with this template in Blend, add a border style to the fixedlayout rule (like border: 2px solid Red;), and fiddle with the view states and the display settings on the Device tab. Then you can see how the ViewBox provides all the scaling for free. To show this more obviously, the FixedLayout example for this chapter changes the child element of the ViewBox to a canvas on which it draws a 4x3 grid (to match the aspect ratio of 1024x768) of 256px squares containing circles. As shown in Figure 6-6 (after the sidebar), the squares and circles don’t turn into rectangles and ovals as we move between view states and display sizes, and letterboxing is handled automatically (applying a background-color style to the win-viewbox class).

If you use raster graphics within a ViewBox, size them according to the maximum 2560x1440 resolution so that they’ll look good on the largest screens and they’ll still scale down to smaller ones (rather than being stretched up). Alternately, you can use load different graphics (through different img.src URIs) that are better suited for the most common screen size.

Note that resolution scaling will still be applicable. If you’re running on a high-density 10.6” 2560x1440 display (180% scale), the app and thus the ViewBox will still see smaller screen dimensions. But if you’re supplying a graphic for the native device resolution, it will look sharp when rendered on the screen.

Image

FIGURE 6-6 Fixed layout scaling with the WinJS.UI.ViewBox controls, showing letterboxing on a full-screen 1366x768 display (left) and in snapped view (right).

Adaptive Layouts

Adaptive layouts are those in which an app shows more content when more screen space is available. Such a layout is most easily achieved with a CSS grid where proportional rows and columns will auto-matically scale up and down; elements within grid cells will then find themselves resized accordingly. This is demonstrated in the Visual Studio/Blend project templates, especially the Grid App project template. On a typical 1366x768 display you’ll see a few items on a screen, as shown at the top of Figure 6-7. Switch over to a 27” 2560x1440 and you’ll see a lot more, as you can see at the bottom of the figure.

Image

FIGURE 6-7 Adaptive layout in the Grid App project template shown for a 1366x768 display (top) and a 2560x1440 display (bottom).

To be honest, the Grid App project template doesn’t do anything different for display size than it already does for view states. Because it uses CSS grids and proportional cell sizes, the cell containing the ListView control automatically becomes bigger. The ListView control is listening for window.onresize on its own, so we don’t need to separately instruct it to update its layout.

The overall strategy for an adaptive layout, then, is straightforward:

• Use a CSS grid where possible to handle adaptive layout automatically.

• Listen for window.onresize as necessary to reposition and resize elements manually, such as an HTML canvas element.

• Have controls listen to window.onresize to adapt themselves directly. This is especially important for collection controls like ListView.

As another reference point, refer to the Adaptive layout with CSS sample, which really takes the same approach as the Grid App project template, relying on controls to resize themselves. In the sample, you will see that the app isn’t doing any direct calculations based on window size.

Hint If you have an adaptive layout and want a background image specified in CSS to scale to its container (rather than being repeated), style background-size to either contain or 100% 100%.

It should be also clear to you as a developer that how an app handles different screen sizes is also a design matter. The strategy above is what you use to implement a design, but the design still needs to think about how everything should look. The following considerations, which I only summarize here, are described on Guidelines for scaling to screens:

• Which regions are fixed and which are adaptive?

• How do adaptive regions makes use of available space, including the directions in which that region adapts?

• How do adaptive and fixed regions relate in the wireframe?

• How does the app’s layout overall makes use of space—that is, how does whitespace itself expand so that content doesn’t become too dense?

• How does the app make use of multicolumn text?

Answering these sorts of questions will help you understand how the layout should adapt.

Using the CSS Grid

Starting back in Chapter 2, we’ve already been employing CSS grids for many purposes. Personally, I love the grid model because it so effortlessly allows for relative placement of elements and scaling easily to different screen sizes.

Because the focus of this book is on the specifics of Windows 8, I’ll leave it to the W3C specs on http://www.w3.org/TR/css3-grid-layout/ and http://dev.w3.org/csswg/css3-grid-align/ to explain all the details. These specs are essential references for understanding how rows and columns are sized, especially when some are declared with fixed sizes, some are sized to content, and others are declared such that they fill the remaining space. The nuances are many!

Because the specs themselves are still in the draft stages as of this writing, it’s good to know exactly which parts of those specs are actually supported by the HTML/CSS engine used for Store apps.

For the element containing the grid, the supported styles are simple. First use the -ms-grid and -ms-inline-grid display models (the display: style). We’ll come back to -ms-inline-grid later.

Second, use -ms-grid-columns and -ms-grid-rows on the grid element to define its arrangement. If left unspecified, the default is one column and one row. The repeat syntax such as -ms-grid-columns: (1fr)[3]; is supported, which is most useful when you have repeated series of rows or columns, which appear inside the parentheses. As examples, all the following are equivalent:

-ms-grid-rows:10px 10px 10px 20px 10px 20px 10px;
-ms-grid-rows:(10px)[3] (20px 10px)[2];
-ms-grid-rows:(10px)[3] (20px 10px) 20px 10px;
-ms-grid-rows:(10px)[2] (10px 20px)[2] 10px;

How you define your rows and columns is the really interesting part, because you can make some fixed, some flexible, and some sized to the content using the following values. Again, see the specs for the nuances involving max-content, min-content, minmax, auto, and fit-content specifiers, along with values specified in units of px, em, %, and fr. Windows Store apps can also use vh (viewport height) and vw (viewport width) as units.

Within the grid now, child elements are placed in specific rows and columns, with specific alignment, spanning, and layering characteristics using the following styles:

-ms-grid-column identifies the 1-based column of the child in the grid.

-ms-grid-row identifies the 1-based row of the child in the grid.

-ms-grid-column-align and -ms-grid-row-align specify where the child is placed in the grid cell. Allowed values are start, end, center, and stretch (default).

-ms-grid-column-span and -ms-grid-row-span indicate that a child spans one or more rows/columns.

-ms-grid-layer controls how grid items overlap. This is similar to the z-index style as used for positional element. Since grid children are not positioned directly with CSS and are instead positioned according to the grid, -ms-grid-layer allows for separate control.

Be very aware that row and column styles are 1-based, not 0-based. Really re-program your JavaScript-oriented mind to remember this, as you’ll need to do a little translation if you track child elements in a 0-based array.

Also, when referring to any of these -ms-grid* styles as properties in JavaScript, drop the hyphens and switch to camel case, as in msGrid, msGridColumns, msGridRowAlign, msGridLayer, and so on.

Overall, grids are fairly straightforward to work with, especially within Blend where you can immediately see how the grid is taking shape. Let’s now take a look at a few tips and tricks that you might find useful.

Overflowing a Grid Cell

One of the great features of the grid, depending on your point of view, is that overflowing content in a grid cell doesn’t break the layout at all—it just overflows. (This is very different from tables!) What this means is that you can, if necessary, offset a child element within a grid cell so that it overlaps an adjacent cell (or cells). Besides not breaking the layout, this makes it possible to animate elements moving between cells in the grid, if desired.

A quick example of content that extends outside its containing grid cell can be found in the GridOverflow example with this chapter’s companion content. For the most part, it creates a 4x4 grid of rectangles, but this code at the end of the doLayout function (js/default.js), places the first rectangle well outside its cell:

children[0].style.width = "350px";
children[0].style.marginLeft = "150px";
children[0].style.background = "#fbb";

This makes the first element in the grid wider and moves it to the right, thereby making it appear inside the second element’s cell (the background is changed to make this obvious). Yet the overall layout of the grid remains untouched.

I’ll cast a little doubt on this being a great feature because you might not want this behavior at times, hoping instead that the grid would resize to the content. For that behavior, try using an HTML table.

Centering Content Vertically

Somewhere in your own experience with CSS, you’ve probably made the bittersweet acquaintance with the vertical-align style in an attempt to place a piece of text in the middle of a div, or at the bottom. Unfortunately, it doesn’t work: this particular style works only for table cells and for inline content (to determine how text and images, for instance, are aligned in that flow).

As a result, various methods have been developed to do this, such as those discussed in http://blog.themeforest.net/tutorials/vertical-centering-with-css/. Unfortunately, just about every technique depends on fixed heights—something that can work for a website but doesn’t work well for the adaptive layout needs of a Store app. And the one method that doesn’t use fixed heights uses an embedded table. Urk.

Fortunately, both the CSS grid and the flexbox (see “Item Layout” later on) easily solve this problem. With the grid, you can just create a parent div with a 1x1 grid and use the -ms-grid-row-align: center style for a child div (which defaults to cell 1, 1):

<!-- In HTML -->
<div id="divMain">
    <div id="divChild">
        <p>Centered Text</p>
    </div>
</div>

/* In CSS */
#divMain {
    width: 100%;
    height: 100%;
    display: -ms-grid;
    -ms-grid-rows: 1fr;
    -ms-grid-columns: 1fr;
}

#divChild {
    -ms-grid-row-align: center;
    -ms-grid-column-align: center;

    /* Horizontal alignment of text also work with the following */
    /* text-align: center; */
}

The solution is even simpler with the flexbox layout, where flex-align: center handles vertical centering, flex-pack: center handles the horizontal, and a child div isn’t needed at all. This is the same styling that’s used in the Fixed Layout App project template to center the ViewBox:

<!-- In HTML -->
<div id="divMain">
    <p>Centered Text</p>
</div>

/* In CSS */
#divMain {
    width: 100%;
    height: 100%;
    display: -ms-flexbox;
    -ms-flex-align: center;
    -ms-flex-direction: column;
    -ms-flex-pack: center;
}

Code for both these methods can be found in the CenteredText example for this chapter. (This example is also used to demonstrate the use of ellipsis later on, so it’s not exactly as it appears above.)

Scaling Font Size

One particularly troublesome area with HTML is figuring out how to scale a font size with an adaptive layout. I’m not suggesting you do this with the standard typography recommended by Windows app design as we saw earlier in this chapter. It’s more a consideration when you need to use fonts in some other aspect of your app such as large letters on a tile in a game.

With an adaptive layout, you typically want certain font sizes to be proportional to the dimensions of its parent element. (It’s not a concern if the parent element is a fixed size, because then you can fix the size of the font.) Unfortunately, percentage values used in the font-size style in CSS are based on the default font size (1em), not the size of the parent element as happens with height and width. What you’d love to be able to do is something like font-size: calc(height * .4), but, well, the value of other CSS styles on the same element are just not available to calc.

One exception to this is the vh value (which can be used with calc). If you know, for instance, that the text you want to scale is contained within a grid cell that is always going to be 10% of the viewport height, and if you want the font size to be half of that, then you can just use font-size: 5vh (5% of viewport height).

Another method is to use an SVG for the text, wherein you can set a viewBox attribute and a font-size relative to that viewBox. Then scaling the SVG to a grid cell will effectively scale the font:

<svg viewBox="0 0 600 400" preserveAspectRatio="xMaxYMax">
    <text x="0" y="150" font-size="200" font-family="Verdana">
        Big SVG Text
    </text>
</svg>

You can also use JavaScript to calculate the desired font size programmatically based on the clientHeight property of the parent element. If that element is in a grid cell, the font size (and line height) can be some percentage of that cell’s height, thereby allowing the font to scale with the cell.

You can also try using the WinJS.UI.ViewBox control. If you want content like text to take up 50% of the containing element, wrap the ViewBox in a div that is styled to be 50% of the container and style the child element of the ViewBox with position: absolute. Try dropping the following code into default.html of a new Blank app project for a demonstration:

<div style="height:50%;">
    <div data-win-control="WinJS.UI.ViewBox">
        <p style="position:absolute;">Big text!</p>
    </div>
</div>

Item Layout

So far in this chapter we’ve explored page-level layout, which is to say, how top-level items are positioned on a page, typically with a CSS grid. Of course, it’s all just HTML and CSS, so you can use tables, line breaks, and anything else supported by the rendering engine so long as you adapt well to view states and display sizes.

It’s also important to work with item layout in the flexible areas of your page. That is, if you set up a top-level grid to have a number of fixed-size areas (for headings, title graphics, control bars, etc.), the remaining area can vary greatly in size as the window size changes. In this section, then, let’s look at some of the tools we have within those specific regions: CSS transforms, flexbox, nested and inline grids, multicolumn text, CSS figures, and CSS connected frames. A general reference for these and all other CSS styles that are supported for Windows Store apps (such as background, borders, and gradients) can be found on the Cascading Style Sheets topic.

CSS 2D and 3D Transforms

It’s really quite impossible to think about layout for elements without taking CSS transforms into consideration. Transforms are very powerful because they make it possible to change the display of an element without actually affecting the document flow or the overall layout. This is very useful for animations and transitions; transforms are used heavily in the WinJS animations library that provides the Windows 8 look and feel for all the built-in controls. As we’ll explore in Chapter 11, “Purposeful Animations,” you can make direct use of this library as well.

CSS transforms can be used directly, of course, anytime you need to translate, scale, or rotate an element. Both 2D and 3D transforms (http://dev.w3.org/csswg/css3-2d-transforms/ and http://www.w3.org/TR/css3-3d-transforms/) are supported for Windows Store apps, specifically these styles:37

Image

Full details can be found on the Transforms reference. Know also that because the app host uses the same underlying engines as Internet Explorer, transforms enjoy all the performance benefits of hardware acceleration.

Flexbox

Just as the grid is magnificent for solving many long-standing problems with page layout, the CSS flexbox module, documented at http://www.w3.org/TR/css3-flexbox/, is excellent for handling variable-sized areas wherein the content wants to “flex” with the available space. To quote the W3C specification:

In [this] box model, the children of a box are laid out either horizontally or vertically, and unused space can be assigned to a particular child or distributed among the children by assignment of ‘flex’ to the children that should expand. Nesting of these boxes (horizontal inside vertical, or vertical inside horizontal) can be used to build layouts in two dimensions.

As the flexbox spec is presently in draft form, the specific display styles for Store apps are display: -ms-flexbox (block level) and display: -ms-inline-flexbox (inline).38 For a complete reference of the other supported properties, see the Flexible Box (“Flexbox”) Layout documentation:

Image

As with all styles, Blend is a great tool in which to experiment with different flexbox styles because you can see the effect immediately. It’s also helpful to know that flexbox is used in a number of places around WinJS and in the project templates, as we saw with the Fixed Layout template earlier. The ListView control in particular takes advantage of it, allowing more items to appear when there’s more space. The FlipView uses flexbox to center its items, and the Ratings, DatePicker, and TimePicker controls all arrange their inner elements using an inline flexbox. It’s likely that your own custom controls will do the same.

Nested and Inline Grids

Just as the flexbox has both block level and inline models, there is also an inline grid: display: -ms-inline-grid. Unlike the block level grid, the inline variant allows you to place several grids on the same line. This is shown in the InlineGrid example for this chapter, where we have three div elements in the HTML that can be toggled between inline (the default) and block level models:

//Within the activated handler
document.getElementById("chkInline").addEventListener("click", function () {
    setGridStyle(document.getElementById("chkInline").checked);
});

setGridStyle(true);


//Elsewhere in default.js
function setGridStyle(inline) {
    var gridClass = inline ? "inline" : "block";

    document.getElementById("grid1").className = gridClass;
    document.getElementById("grid2").className = gridClass;
    document.getElementById("grid3").className = gridClass;
}


/* default.css */
.inline {
    display: -ms-inline-grid;
}

.block {
    display: -ms-grid;
}

When using the inline grid, the elements appear as follows:

Image

When using the block level grid, we see this instead:

Image

Fonts and Text Overflow

As discussed earlier, typography is an important design element for Store apps, and for the most part the standard font styles using Segoe UI are already defined in the default WinJS stylesheets. In the Windows SDK there is a very helpful CSS typography sample that compares the HTML header elements and the win-type-* styles, demonstrating font fallbacks and how to use bidirectional fonts (left to right and right to left directions).

Speaking of fonts, custom font resources using the @font-face rule in CSS are allowed in Store apps. For local context pages, the src property for the rule must refer to an in-package font file (that is, a URI that begins with / or ms-appx:///). Pages running in the web context can load fonts from remote sources.

Another piece of text and typography is dealing with text that overflows its assigned region. You can use the CSS text-overflow: ellipsis; style to crop the text with a …, and the WinJS stylesheets contain the win-type-ellipsis class for this purpose. In addition to setting text-overflow, this class also adds overflow: hidden (to suppress scrollbars) and white-space: nowrap. It’s basically a style you can add to any text element when you want the ellipsis behavior.

The W3C specification on text overflow, http://dev.w3.org/csswg/css3-ui/#text-overflow, is a helpful reference as to what can and cannot be done here. One of the limitations of the current spec is that multiline wrapping text doesn’t work with ellipsis. That is, you can word-wrap with the word-wrap: break-word style, but it won’t cooperate with text-overflow: ellipsis (word-wrap wins). I also investigated whether flowing text from a multiline CSS region (see next section) into a single-line region with ellipsis would work, but text-overflow doesn’t apply to regions. So at present you’ll need to shorten the text and insert ellipsis manually if it spans multiple lines.

For a demonstration of ellipsis and word-wrapping, see the CenteredText example for this chapter.

Multicolumn Elements and Regions

Translating the multicolumn flow of content that we’re so accustomed to in print media has long been a difficult proposition for web developers. While it’s been easy enough to create elements for each column, there was no inherent relationship between the content in those columns. As a result, developers have had to programmatically determine what content could be placed in each element, accounting for variations like font size or changing the number of columns based on the screen width or changes in device orientation.

CSS3 provides for doing multicolumn layout within an element (see http://www.w3.org/TR/css3-multicol). With this, you can instruct a single element to lay out its contents in multiple columns, with specific control over many aspects of that layout. The specific styles supported for Windows Store apps (with no pesky little vendor prefixes!) are as follows:

Image

The reference documentation for these can be found on Multi-column layout.

Again, Blend provides a great environment to explore how these different styles work. If you’re placing a multicolumn element within a variable-size grid cell, you can set column-width and let the layout engine add and remove columns as needed, or you can use media queries or JavaScript to set column-count directly.

CSS3 multicolumn again only applies to the contents of a single element. While highly useful, it does impose the limitation of a rectangular element and rectangular columns (spans aside). Certain apps like magazines need something more flexible, such as the ability to flow content across multiple elements with more arbitrary shapes, and columns that are offset from one another. These relationships are illustrated in Figure 6-8, where the box in the upper left might be a title, the inset box might contain an image, and the text content flows across two irregular columns.

Image

FIGURE 6-8 Using CSS regions to achieve a more complex layout with irregular text columns.

To support irregular columns, CSS Regions (see http://dev.w3.org/csswg/css3-regions/) are coming online and are supported in Store apps (see Regions reference). Regions allow arbitrarily (that is, absolutely) positioned elements to interact with inline content. In Figure 6-8, the image would be positioned absolutely on the page and the column content would flow around it.

The key style for a positioned element is the float: -ms-positioned style which should accompany position: absolute. Basically that’s all you need to do: drop in the positioned element, and the layout engine does the rest. It should be noted that CSS Hyphenation, yet another module, relates closely to all this because doing dynamic layout on text immediately brings up such matters. Fortunately, Store apps support the –ms-hyphens and the -ms-hyphenation-* styles (and their equivalent JavaScript properties). The hyphenation spec is located at http://www.w3.org/TR/css3-text/; documentation for Store apps is found on the Text styles reference.

The second part of the story consists of named flows and region chains (which are also part of the Regions spec). These provide the ability for content to flow across multiple container elements, as shown in Figure 6-9. Region chains can also allow the content to take on the styling of a particular container, rather than being defined at the source. Each container, in other words, gets to set its own styling and the content adapts to it, but commonly all the containers share similar styling for consistency.

Image

FIGURE 6-9 CSS region chains to flow content across multiple elements.

How this all works is that the source content is defined by an iframe that points to an HTML file (and the iframe can be in the web or local context, of course). It’s then styled with -ms-flow-into: <element> (msFlowInfo in JavaScript) where <element> is the id of the first container:

<!-- HTML -->
<iframe id="s1-content-source" src="/html/content.html"></iframe>
<div class="s1-container"></div>
<div class="s1-container"></div>
<div class="s1-container"></div>

/* CSS */
#s1-content-source {
    -ms-flow-into: content;
}

Note that -ms-flow-into prevents the iframe content from displaying on its own.

Container elements can be any nonreplaced element—that is, any element whose appearance and dimensions are not defined by an external resource, such as img—and can contain content between its opening and closing tabs, like a div (the most common) or p. Each container is styled with -ms-flow-from: <element> (msFlowFrom in JavaScript) where the <element> is the first container in the flow. The layout then happens in the order elements appear in the HTML (as above):

.s1-container {
    -ms-flow-from: content;
    /* Other styles */
}

This simple example was taken from the Static CSS regions sample, which also provides a few other scenarios. There are two other applicable projects here as well, the Dynamic CSS regions sample and the Dynamic CSS region templates sample, where the latter is the source for Figure 6-8 above. In all these cases, be aware that styling for regions is limited to properties that affect the container and not the content—content styles are drawn from the iframe HTML source. This is why using text-overflow: ellipsis doesn’t work, nor will font-color and so forth. But styles like height and width, along with borders, margin, padding, and other properties that don’t affect the content can be applied.

What We’ve Just Learned

• Layout that is consistent with Windows 8 design principles—specifically the silhouette and typography—helps users focus immediately on content rather than having to figure out each specific app.

• The principle of “content before chrome” allows content to use 75% or more of the display space rather than 25% as is common with chrome-heavy desktop or web applications.

• In some cases, such as a home or hub page of an app with varied and content that does not come from a single collection, it’s best to just use plain HTML/CSS layout rather than using a control.

• Pannable HTML sections can use snap points to automatically stop panning at particular intervals within the content.

• The CSS grid is a highly useful mechanism for adaptive page-level layout, and it can also be used inline. The CSS flexbox is most useful for inline content, though it has uses at the page level as well, as for centering content vertically and horizontally.

• Every page of an app (including the extended splash screen) can encounter all four view states, so an app design must show how those states are handled. Media queries and the Media Query Listener API can be used to handle the view states declaratively and programmatically.

• Apps can specify a preferred orientation in their manifest and also lock the orientation at run time.

• The window.onresize event is best for knowing when the window size has changed, due to view states and/or changes in screen size and pixel density.

• Handling varying screen sizes is accomplished either through a grid-based adaptive layout or a fixed layout utilizing the WinJS.UI.ViewBox control that does automatic scaling of its content.

• The chief concern with pixel density is providing graphics that scale well. This means either using vector graphics or providing scaled variants of each raster graphic.

• Windows Store apps can take advantage of a wide range of CSS 3 options, including the grid, flexbox, transforms, multicolumn text, and regions.