Walkthrough: Reader app (JavaScript and HTML)
Apps that incorporate a reading experience are quickly becoming popular as tablet PCs become more prevalent. Here we highlight the new Cascading Style Sheets (CSS) and HTML5 features that you can use to create great reader apps. After reading this article you will have a solid understanding of key layout features that are used in these apps and will also have a basic reader app to start from. Now, let's learn more about creating great reader experiences in Windows.
This article accompanies the Dynamic CSS region templates sample.
Introduction
Windows 8 offers developers and designers a range of the latest HTML5 and CSS3 features to create new and unique reading experiences. Specifically, thanks to new technologies such as CSS regions and CSS grid, you can create digital publications that combine the advantages of HTML—fluid layouts, markup-based styling, accessible content, interactivity—with innovative layouts that have so far been possible only in the print domain.
Webpages have traditionally been rendered as single columns of vertical content. Paragraphs are piled on top of paragraphs and other images, with the occasional floating element thrown in to offer some visual variety. "Pages" were created only by segmenting the content into different HTML documents; there was no markup-based way to take a stream of content and segment that content into individual screens of text. A lack of strong controls for creating columns and adapting to screen size meant that many pages were optimized for a specific resolution only, such as 1024 × 768. You see a great example of a bad experience when text optimized for small resolutions is rendered on a high resolution, widescreen monitor. The following image shows a single-column layout on a monitor with a 4:3 aspect ratio.
The following image shows the same layout on a widescreen monitor with empty white space.
In contrast, a page designed to take full advantage of a page's available space would likely feature multiple columns of text, thereby filling the screen while making sure that individual lines of text are not too long to read easily. Through some small refinements, titles can increase their size to be more eye-catching, and images can be given a more prominent position while text wraps around them.
The following image shows an improved reader layout for widescreen monitors.
Print designers have long been able to design such layouts with ease, because they have to contend only with a fixed page size known well in advance. To create pages that adapt intelligently to different screen sizes, use tools such as CSS grid and media queries to design your layout.
This article, together with the Dynamic CSS region templates sample, describes how to create great-looking layouts for reading that adapt to a wide variety of screen sizes. In Reviewing new CSS reading features, we go over several new features that you can use to present text and images in innovative new ways. In Putting features together to create a great reading experience, we continue by showing how to combine these features to build an adaptive reading experience that combines great design with fluid and engaging layouts.
Reviewing new CSS reading features
Let's look at the new CSS features that you're likely to use in your reader app: multi-column, hyphenation, grid, regions, and exclusions.
Multi-column
CSS multi-column enables designers to format the inline content (such as text) of an element—for example, a div element or the body of a page—into multiple adjacent columns of equal size, located next to each other. For example, simply adding the CSS property column-count: 2; to a div produces a layout like the one in the following image.
Multi-column does have some limitations, however. Columns must all be the same size and positioned next to each other. Moreover, content from a multi-column element obeys the CSSoverflow property; that is, it clips, adds a scroll bar, or overflows by creating additional columns next to the original element. For these reasons, CSS regions may be a better option for text layout in situations where a more varied page layout is called for, or where there is a possibility that the inline content of an element could overflow the element. The following image shows new columns being created by overflowing the multi-column element.
Note CSS multi-column has a "balance" option to ensure that all columns are the same length and that the last column is not significantly shorter than the others. This functionality does not ensure that columns have the same number of lines, or that lines align across columns. The content in a multi-column element should be formatted so that all elements are a multiple of a single line height, including padding on paragraphs and images.
The following image shows how a balanced multi-column layout (outlined in light blue) formats content as opposed to an unbalanced layout (outlined in red).
Note This example is well balanced because the elements within the columns are matched to the column dimensions and spacing.
The following code shows how the two layouts were created.
<!DOCTYPEHTML>
<html>
<head>
<style>
#div1{
width: 180px;
height: 300px;
column-fill: balance;
column-count: 2;
float: left;
border: solid 2px lightblue;
margin-right: 8px;
}
#div2{
width: 180px;
height: 300px;
column-fill: auto;
column-count: 2;
float: left;
border: solid 2px red;
margin-right: 8px;
}
</style>
</head>
<body>
<div id="div1" class="div1">
Hi friend! Hi friend! Hi friend! Hi friend!
Hi friend! Hi friend! Hi friend! Hi friend!
Hi friend! Hi friend! Hi friend! Hi friend!
Hi friend! Hi friend! Hi friend! Hi friend!
Hi friend! Hi friend! Hi friend! Hi friend!
</div>
<div id="div2">
Hi friend! Hi friend! Hi friend! Hi friend!
Hi friend! Hi friend! Hi friend! Hi friend!
Hi friend! Hi friend! Hi friend! Hi friend!
Hi friend! Hi friend! Hi friend! Hi friend!
Hi friend! Hi friend! Hi friend! Hi friend!
</div>
</body>
</html>
Hyphenation
Multi-column enables us to lay out inline content, such as text, in relatively narrow columns. But content in these columns can appear ragged, especially when the average word length is a large percentage of the column width. In particular, when columns are combined with the text-align: justify property, large "rivers" of whitespace can appear between words, giving the content an irregular and unattractive appearance as shown here.
Accordingly, we now offer the ability to automatically hyphenate words, so that lines can be more densely packed while still supporting a strong reading experience as seen here.
Hyphenation is defined in part in the W3C CSS Text Level 3 specification. CSS hyphenation offers the ability to:
- Specify a minimal word-fragment length following and preceding a hyphenation break.
- Specify a maximum number of consecutive hyphenated lines.
- Prevent the last words in paragraphs and columns from being hyphenated.
- Specify a custom hyphenation character.
Hyphenation in Windows 8 and Internet Explorer 10 is supported for a wide variety of languages and is recommended for use for both CSS multi-column and regions.
Grid
The core idea behind CSS grid layout is to partition a page into a defined set of rows and columns and then to set the position and size of elements based on those rows and columns by using CSS. Because the size of rows and columns can be defined as fixed, flexible, or size-to-content, it's easy to build a layout that completely fills the entire screen, regardless of screen size.
For example, the image below shows an example of a reader that has been divided into two columns and three rows with multiple elements. Some of the elements must remain fixed while others grow or shrink as the size of the browser window changes.
With CSS grid layout, you can achieve this design by starting with the following markup.
<div id="grid">
<div id="logo">Reader Logo</div>
<div id="page">Page</div>
<div id="bookmarks">Bookmarks</div>
<div id="content">Reader Content</div>
<div id="controls">Controls</div>
</div>
The div element named grid
is set as a grid container by applying the CSS style display: grid. You can define the various rows and columns either as flexible, which grows or shrinks them based on available space, or as auto-sizing, which sizes the row or column to the width or height of the largest element placed inside it.
#grid {
display: -ms-grid;
-ms-grid-columns: auto 1fr;
-ms-grid-rows: auto 1fr auto;
}
After the grid is defined, you can position individual elements in specific grid cells. As the following markup shows, not only can you specify that an element is placed in a specific row and column in CSS, but you can also specify that an element is to span multiple rows and columns, and specify an element's horizontal or vertical alignment within a cell or range of cells.
#logo { -ms-grid-column: 1; -ms-grid-row: 1 }
#page { -ms-grid-column: 1; -ms-grid-row: 3 }
#bookmarks { -ms-grid-column: 1; -ms-grid-row: 2; -ms-grid-row-align: start }
#content { -ms-grid-column: 2; -ms-grid-row: 1; -ms-grid-row-span: 2 }
#controls { -ms-grid-column: 2; -ms-grid-row: 3; -ms-grid-column-align: center }
Elements can be specified to have a fixed size or to fill the available width or height of the cell they're in. In the preceding example, the content
element grows along with the screen, while other elements, such as the logo
element, stay fixed in size.
Note Because Cascading Style Sheets, Level 3 (CSS3) is still an emerging specification, in Internet Explorer 10 usage, all of the grid properties are prefixed with "-ms-".
By combining the grid layout feature with CSS3 media queries, you can specify entirely different layouts for different resolutions, aspect ratios, and screen orientations. For example, you can define a different number of rows or columns for different screen sizes, assigning fewer columns for a vertical "portrait" layout and more columns for a horizontal "landscape" layout. You could also specify that rows and columns are sized differently.
In addition, 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 even to avoid displaying some elements entirely in some layouts.
Regions
CSS regions enable a designer to flow content from one containing element into another, thereby connecting flows of content across different areas within a page or across multiple pages. This ability makes new and innovative designs possible. For example, the following illustration shows text flowing across two regions within a page. Note that, unlike CSS multi-column, regions allow containers to be different sizes, and not immediately adjacent to one another.
Regions can also be used to flow a single content stream, such as the text of an article, across multiple virtual pages, as seen in the following illustration.
Note that "pages" in this context are not pages created by the printing process, such as pages that are styled using an "@page" CSS rule, but are instead div elements containing one or more regions. By placing these div elements in a scrollable area (usually with snap points to simulate page flipping), we can create a paginated reading experience where a single stream of content is displayed across multiple, full-screen pages and not as a single scrolling region of content. In addition, regions can be combined with CSS grid and media queries to create pages that precisely fit a particular screen size, and to generate the exact number of pages needed to display a particular stream of content.
The following diagram shows a high-level overview of how regions work. The content from a source document (Content.html) flows into multiple regions in a master template document (Master.html).
It's important to note that regions do not participate in the CSS style cascade across a document, so that a style rule placed on a specific region (for example, text-color: red) does not affect the content inside the region. But style rules placed on the content document itself do affect the content, although they don't affect any elements within the master template document.
Likewise, content the displayed by regions within the master template does not exist in the master template's Document Object Model (DOM); looking at Master.html via the DOM explorer in Microsoft Visual Studio, for example, shows that each region is empty. Instead, the content displayed by regions exists within a hidden iframe element that exists in Master.html and points to Content.html.
Exclusions
CSS exclusions offer a way to cause inline content to wrap around an element, such as an image, title, or text callout. Exclusions are similar to CSS floats, except that instead of being pushed to one side or the other of a containing block, exclusions can be positioned in the middle of a containing block and have content flow around both sides as shown here.
Exclusions can interact with multiple surrounding elements at once. For example, an exclusion can be surrounded by multiple regions and cause the content in each region to wrap around it. For more info about how to do that, see Putting features together to create a great reading experience.
Note that regions can only be rectangular blocks in Internet Explorer 10. However, multiple regions can be placed next to one another to create nonrectangular regions.
Putting features together to create a great reading experience
Now that we’ve reviewed the individual technologies that you can use to create a great reading experience in Windows 8, let’s discuss how to combine these technologies. We'll do that together by examining the Dynamic CSS region templates sample. First, we'll cover how to combine a grid, media queries, regions, and exclusions to create individual page templates. Then we'll look at JavaScript code that instantiates these page templates until all content in the source document has been displayed. Finally, we'll look at additional JavaScript code that can adjust the number of pages based on a change to the screen size or font size.
Laying out page templates
The following image shows page 1 of the sample, which includes:
- A title ("Dynamic region Templates Sample").
- Two regions laid out as two parallel columns.
- An exclusion set in the middle of the page (the picture of Saturn), which causes the surrounding content in the two regions to wrap around it.
.
The following image illustrates the grid structure of page 1.
Specifically, page 1 consists of a single div element that becomes a grid through the use of the display: -ms-grid property. Other elements such as regions and exclusions are laid down on this single div. This grid is given the entire width and height of the application area, which may be the entire screen or a subset of it if the app is in filled or snapped mode.
Page 1 is divided into 4 columns and 5 rows. Each column is the size of 1 fractional unit (1FR), which represents one share of all the space available to the grid after fixed-size and auto-sized tracks (rows or columns) are laid out. In this case, because there are no fixed-size or auto-sized tracks, each column gets 25% of the width of the application area.
In contrast, the first of the 5 rows on page 1 is an auto-sized row, while the remaining rows are fractionally sized. The page title is in row 1, which takes up as much space as needed to display the "Dynamic region Templates Sample" title. The remaining height of the application area is taken up by the remaining rows, each of which receives just under 25% of the application area.
Within the grid are two regions. Each region spans two columns and four rows: the first region is positioned at row 2, column 1 (CSS grids are 1-indexed), and the second region is at row 2, column 3. Each region is simply a div element that has the -ms-flow-from: content property set. Because each region shares the same flow name, the content stream starts with the first region, continues to the next, and advances through subsequent regions on different pages.
The grid is also used to position an exclusion in the middle of the page. In this case the exclusion is a div element with an image of Saturn positioned in the middle of it by means of the CSSflexbox display attribute. The exclusion is created by setting the -ms-wrap-flow: both property. When the exclusion has been positioned in the middle of the grid (row 3, column 2, spanning two rows and two columns), any inline content that intersects with the exclusion will wrap around it. This behavior is in contrast with traditional floating elements, which would have to be positioned toward one side of the page.
Note that the regions and exclusion are all positioned to stretch across rows and columns. This happens either because the app is running on a device with a different screen resolution or because the app has been switched between full screen and filled modes. As the app resolution changes in size, the elements on the screen also increase and decrease in size. This is clearly optimal behavior for regions because we want the text areas to fill but not overflow the application area. Any text that does not fit in a region simply moves down to the next region. Depending on the content, a designer may not want an image to stretch with the screen size, preferring instead to maintain the aspect ratio or the absolute size of the image. This is easily accomplished by changing the –ms-grid-row-align and –ms-grid-column-align properties from their default values of stretch.
The next two images show the grid breakdown of pages 2 and 3 of the Dynamic CSS region templates sample. Both pages are laid out in a 4 × 4 grid where every row and column is an equal fractional size of the available space. The only substantial difference between the pages (aside from the obvious difference in background images) is that page 2 features a single region that spans two rows and two columns in the middle of the page, whereas page 3 features multiple regions staggered across the page. Together, all of these pages demonstrate a single stream of content reaching across multiple pages via CSS regions.
Here is page 2.
And here's page 3.
Note The content shown in all three pages above is fully justified and hyphenated. These style properties are set on the content (from the content.html file) instead of on any of the regions themselves. This is because, as mentioned earlier, the style cascade does not penetrate regions.
Instantiating page templates
We have discussed how to create pages with regions, a grid, and exclusions. You can see these pages as page1.html, page2.html, and page3.html in the Dynamic CSS region templates sample project. However, we have not yet examined how these pages are dynamically created when the app is activated. Because the number of pages that are needed to display a content document varies with screen size and font size—smaller screens or larger fonts mean fewer words per page, and therefore more pages required–pages must be instantiated dynamically at run time. Now let's talk about how to instantiate predefined templates in a Windows Store app, one at a time, until the entire content document is displayed. Later on, we'll discuss how to adapt the number of pages based on run-time changes to screen size or font size.
The following example shows the structure of the Dynamic CSS region templates sample. Specifically, a div element with an id attribute of appContainer
contains both a source iframe element and a pageContainer
div element that contains the individual pages. The iframe points to the content.html document that is to be displayed across multiple regions in multiple pages. Because the iframe has the -ms-flow-into: content property, it is not displayed. The appContainer
div and individual pages take up all the available screen space allocated to the app because they all have width and height set to 100% of the application area.
#sourceIFrame {
-ms-flow-into: content;
width: 500px;
height: 500px;
border: 1px solid black;
}
#pageContainer
{
display: -ms-box;
height: 100vh;
}
The pageContainer
div has been configured as a flexbox through the use of the display: -ms-box property, and the body element has been set to overflow: scroll. Because the default behavior of a flexbox is to place any elements inside it next to each other and therefore to grow horizontally as needed, the user can scroll through the pages by panning horizontally with mouse or touch input.
Note A designer could easily switch to a vertical paging model by setting the -ms-box-orient: vertical property. Also, for touch scrolling to work properly, the iframe element must be in the same parent element as the pages that are being scrolled. This is obviously not an issue when the scrolling element is the body element itself, but could be an issue in other configurations of iframe and pages.
Now that we've seen how the reading app will ultimately be structured, we need to discuss how pages are placed in the app. The following example code from regionTemplatePage.js shows the pageTemplates
array.
var pageTemplates = [
'/html/page1.html',
'/html/page2.html',
'/html/page3.html',
'/html/pageDefault.html'
];
The pageTemplates
array points to the various files from the app package that correspond to the pages that will be instantiated and the referenced files are used by the createPages
function. The createPages
function is called by an event listener function that is triggered by the load event for the source iframe.
The following code shows createPages
, the function that selects and instantiates page templates from the pageTemplates
array.
function createPages() {
var targetPage;
if (currentPage > (pageTemplates.length - 1)) {
targetPage = pageTemplates[pageTemplates.length - 1];
} else {
targetPage = pageTemplates[currentPage];
}
var flexboxElement = document.getElementById('pageContainer');
WinJS.UI.Fragments.render(targetPage, flexboxElement).then(function () {
msSetImmediate(function () {
currentPage += 1;
var flexboxElement = document.getElementById('pageContainer');
var pages = flexboxElement.querySelectorAll('.page');
var lastPage = pages[pages.length - 1];
var regions = lastPage.querySelectorAll(searchClass);
var lastRegion = regions[regions.length - 1];
if (lastRegion.msRegionOverflow === 'overflow') {
createPages();
}
});
});
}
This function performs three primary steps: selecting a template, rendering the template, and determining whether an additional template is needed. In the first step, the targetPage variable is set to the appropriate page template, depending on the page number that is currently being rendered. By means of a counter incremented by createPages
, the SDK sample is set to render each page in the pageTemplates
array once before rendering the next page. If the sample has reached the end of the array, the last page template is used until the content is exhausted.
When the page template has been identified, createPages
calls the fragment loader function from the Windows Library for JavaScript to render the selected template into the pageContainer
element. The fragment loader takes all the content within the body element of the target page file (page1.html in the sample) and inserts it into a specified element (in this case, the pageContainer
element). Because the fragment loader is an asynchronous call, the rest of the createPages
function must wait until after the then function is activated.
After then is activated, createPages
navigates the DOM to find the last region on the last page. createPages
tests this region to find out if there is still additional content to be rendered from the content document. If the msRegionOverflow property is not set to "overflow", the content document has been fully rendered, either in the last region or in a region elsewhere on the last page. If the msRegionOveflow property is set to "overflow", additional content remains to be rendered. In this case, createPages
is called and begins again.
Note The initial createPages
call exits before the then function is activated because of the msSetImmediate call. This means that createPages
is not truly recursive, because only one instance of createPages is placed on the call stack at a time.
Note The template-selection mechanism that createPages uses is obviously something that could be adapted by designers to suit their own needs. For example, rather than reusing the last template over and over again, the function could be altered to repeatedly cycle through the array of templates as long as there is additional content to display. Alternatively, the simple array and template-selection mechanism used by createPages
could be replaced by arbitrarily complex logic that could select a page template based on other factors, such as the section of the source content that is being rendered, or the specific resolution or orientation of the host device.
Finally, the createPages
function is suitable for reader apps where each page is different from the next. However, some apps, such as electronic books, may require both more pages and fewer distinct types of pages. For these apps, especially when the source content is lengthy (for example, the complete text of a long book) it is recommended that multiple page templates be loaded at once. For example, instead of a Page1.html file that contains a single template, an ebook reading app could have a Pages.html file that contains multiple templates, each of which is a simple two-column or three-column layout. By loading these pages all together into memory, overall performance can be significantly increased. Note that following this methodology may cause createPages
to "overshoot" the number of needed pages and to leave some pages without content. This issue can be remedied through the use of the resizePages
function, which is described in further detail in the next section.
Resizing page templates
Previously we described how to create page templates by using a grid, regions, and exclusions, and how to instantiate those page templates inside the app at run time. However, it is also important to be able change the number of pages at run time in response to external events. Even if the content document stays unchanged, changes to the application area may cause the app's view state to change from full screen to filled or snapped mode, and thus cause pages to shrink or grow. Changes to the application area can cause different amounts of content to fit on each page and may require pages to be added or removed from the app.
The following code example shows the resizePages
function, which enables the addition or removal of pages in response to an external event. In the case of the Dynamic CSS region templates sample, resizePages
is called in response to an app view state change, but the function could also be called in response to an event such as a font resize.
function resizePages() {
var flexboxElement = document.getElementById('pageContainer');
var pages = flexboxElement.querySelectorAll('.page');
var lastPage = pages[pages.length-1];
var regions = lastPage.querySelectorAll(searchClass);
var lastRegion = regions[regions.length-1];
if (lastRegion.msRegionOverflow === 'overflow') {
createPages();
}
else {
var firstRegion = regions[0];
while (firstRegion.msRegionOverflow === 'empty')
{
flexboxElement.removeChild(lastPage);
currentPage = currentPage - 1;
lastPage = pages[currentPage-1];
regions = lastPage.querySelectorAll(searchClass);
firstRegion = regions[0];
}
}
}
The resizePages
function first locates the last region on the last page. Like the createPages
function, it then tests this region to determine whether there is additional content that requires additional pages to show. If there is content to show, resizePages
callscreatePages
. Alternatively, if there is no additional content on the last region, the function finds the first region on the last page. If the msRegionOverflow property indicates that the region is not empty, we know that the content document ends on the last page; no further action is taken because there is content at least in the first region on the last page and no content is overflowing the last region. Alternatively, if the first region on the last page is empty, there is no content on the last page and it is entirely empty. The resizePages
function removes the last page and calls itself to test the new last page.
In combination, resizePages
and createPages
are sufficient to add and remove pages from an app. The example page template designs can flex to fill a variety of screen sizes, but a designer may want to have pages that restructure themselves in different view states, orientations, or screen resolutions, as opposed to simply adjusting to fill excess space. The Dynamic CSS region templates sample illustrates how to create page templates that restructure themselves in different view states. The following image shows how the sample restructures itself to be a series of vertically stacked pages, where each page is a single region in snapped mode. This approach involves hiding all content except one region per page template, setting only one element to be a region while in snapped mode, and reorienting the pageContainer
element to display pages vertically.
To understand how this is done, look at the markup for Page1.html. There are two regions in page 1, each with a specific ID and class assigned to it. Both regions are part of the fullregion
class, whereas only the first region is part of the snappedregion
class. The fullregion
class defines its elements as having the –ms-flow-from property set to content, which means that the material in the iframe element will be displayed within them. The snappedregion
class is initially set the same way.
When the rules in the media query for the snapped view state are activated, only the snappedregion
class still sets the –ms-flow-from property to content; the fullregion
class sets the property instead to null. This has the effect of making only one of the div elements in the page a region; content flows from that region to regions on other pages. Also, because other pages in the app also have the snappedregion
class set only on their first region elements, when the app is put in snapped mode only one region on every page shows content from the source document. The following code shows the HTML and CSS for page 1.
<body>
<div id="page1" class="page">
<div id="logoContainer">
<img id="floatLogo" src="/images/Saturn.jpg" />
</div>
<h1 id="title">Dynamic region Templates Sample</h1>
<div id="page1region1" class="fullregion snappedregion"></div>
<div id="page1region2" class="fullregion"></div>
</div>
</body>
.fullregion {
-ms-flow-from: content;
}
.snappedregion
{
-ms-flow-from: content;
}
/*In snapped mode the fullregion class no longer receives content from the content stream, but the snappedregion class does.*/
@media (-ms-view-state: snapped)
{
.fullregion
{
-ms-flow-from: null;
}
.snappedregion
{
-ms-flow-from: content;
}
/*Change the navigation direction to vertical for RTL and LTR languages*/
#pageContainer
{
-ms-box-orient: block-axis;
}
The styling classes, in combination with the styling rules set in the following code, show how the app is transformed in snapped mode. Specifically, all elements but the first region are set to display: none, while the first region is set to take up the entire page (minus the first row, which is set aside for the article title). When this process is replicated across all pages, and the pageContainer
array is set to lay out pages in a block direction (vertical in the following code example), the app transforms into a series of full-page regions that show different portions of the same content document. In combination with the resizePages
and createPages
algorithms described earlier, the app will create enough pages in snapped mode to completely render the source document.
/* In snapped mode we set the first region to take up the entire page, and hide all other regions. */
@media all and (-ms-view-state: snapped)
{
#page1region1
{
-ms-grid-row: 2;
-ms-grid-column: 1;
-ms-grid-row-span: 4;
-ms-grid-column-span: 4;
padding: 0;
}
#page1region2
{
display: none;
}
#logoContainer
{
display: none;
}
}
Note The basic mechanism for adjusting the layout of a page, as described above, could be applied to other designs as well. For example, a designer might wish to add or remove a region from a page depending on whether the page is in 16:9 or 4:3 mode. This adjustment could be accomplished by using CSS media queries to modify the –ms-flow-from and display properties as needed. Media queries can also be used to change the structure of a CSS grid. For example, content layout could change from a 2 × 4 grid to a 4 × 2 grid, depending on whether the screen is in portrait or landscape mode. A designer might also use CSS media queries to display or not display various images or other elements, depending on the screen resolution and orientation.
About the author
Chris Jones is a Program Manager on the Windows Presentation and Composition team. During Windows 8 development, he has worked on CSS control styling and on layout features such as CSS Regions, CSS3 Floats, Grid, Flexbox, and Multi-column. He can be reached at cjon@microsoft.com.