I’d like to share with you a mysterious internal secret kept within Microsoft for now a long time. It’s the real story behind the concept of the CSS Grid Layout imagined by Microsoft for IE10 and Windows Store Apps. Most of you probably think that this specification was designed to help developers having a better layout engine for their websites and applications. But the original motivation was completely different. The very first aim was to be able to create a falling block game in an easy way! But I’m sure you’re not convinced yet. That’s why, I’m going to prove it to you using Blend 5 as a companion. Ok, let’s go!
Pre-requisites: to follow this tutorial, you need first to:
- Download/buy & install Windows 8 RTM on your machine: http://msdn.microsoft.com/en-US/windows/apps/br229516.aspx
- Download & install the free edition of Visual Studio 2012 Express RTM for Windows 8: http://msdn.microsoft.com/en-US/windows/apps/br229516.aspx that includes Expression Blend 5 for Visual Studio or use the higher versions.
Step 1: discover the secret behind the CSS Grid Layout thanks to Blend 5
Launch Expression Blend 5 and create a new HTML (Windows Store) project of type Blank App. Name it "TheRealCSSGridStory ":
Replace:
<p>Content goes here</p>
With:
<div class="mainGrid">
</div>
Let’s create a grid containing 10 columns and 20 lines whatever the screen’s resolution by using fraction units. For that, add this CSS rule:
.mainGrid {
display<span class="code-none">: -ms-grid<span class="code-none"><span class="code-none">;
width<span class="code-none"><span class="code-none">: 100%<span class="code-none"><span class="code-none">;
height<span class="code-none"><span class="code-none">: 100%<span class="code-none"><span class="code-none">;
-ms-grid-columns<span class="code-none"><span class="code-none">: (1fr)[10]<span class="code-none"><span class="code-none">;
-ms-grid-rows<span class="code-none"><span class="code-none">: (1fr)[20]<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}</span></</span></span></span></span></span></span></span></span></span>span></</span>
Select in the Live DOM the <div> mainGrid
element and you should obtain this:
Let’s draw a shape inside this beautiful grid. Add this block of HTML inside the main grid:
<div class="shape1">
</div>
And insert this CSS associated with it:
.shape1 {
-ms-grid-column<span class="code-none">: 4<span class="code-none"><span class="code-none">;
-ms-grid-row<span class="code-none"><span class="code-none">: 3<span class="code-none"><span class="code-none">;
-ms-grid-column-span<span class="code-none"><span class="code-none">: 3<span class="code-none"><span class="code-none">;
-ms-grid-row-span<span class="code-none"><span class="code-none">: 2<span class="code-none"><span class="code-none">;
background-color<span class="code-none"><span class="code-none">: red<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}</span></</span></span></span></span></span></span></span></span></span>span></</span>
You should now see that in Blend 5:
Cool, but nothing yet looks like to a falling block gaming piece. Let’s work on that. Add these 2 DIV inside the shape1:
<div class="line1shape1"></div>
<div class="line2shape1"></div>
and replace the previous .shape1
rule by this block of CSS:
.shape1 {
-ms-grid-column<span class="code-none">: 4<span class="code-none"><span class="code-none">;
-ms-grid-row<span class="code-none"><span class="code-none">: 3<span class="code-none"><span class="code-none">;
-ms-grid-column-span<span class="code-none"><span class="code-none">: 3<span class="code-none"><span class="code-none">;
-ms-grid-row-span<span class="code-none"><span class="code-none">: 2<span class="code-none"><span class="code-none">;
display<span class="code-none"><span class="code-none">: -ms-grid<span class="code-none"><span class="code-none">;
-ms-grid-columns<span class="code-none"><span class="code-none">: 1fr 1fr 1fr<span class="code-none"><span class="code-none">;
-ms-grid-rows<span class="code-none"><span class="code-none">: 1fr 1fr<span class="code-none"><span class="code-none">;
width<span class="code-none"><span class="code-none">: 100%<span class="code-none"><span class="code-none">;
height<span class="code-none"><span class="code-none">: 100%<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}
.line1shape1 <span class="code-none"><span class="code-none">{
-ms-grid-column-span<span class="code-none"><span class="code-none">: 2<span class="code-none"><span class="code-none">;
background-color<span class="code-none"><span class="code-none">: red<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}
.line2shape1 <span class="code-none"><span class="code-none">{
-ms-grid-column<span class="code-none"><span class="code-none">: 2<span class="code-none"><span class="code-none">;
-ms-grid-row<span class="code-none"><span class="code-none">: 2<span class="code-none"><span class="code-none">;
-ms-grid-column-span<span class="code-none"><span class="code-none">: 2<span class="code-none"><span class="code-none">;
background-color<span class="code-none"><span class="code-none">: red<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}</span></</span></span></span></span></span></span></span>span></</span>span></</span></span></span></span></span>span></</span>span></</span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span></span>span></</span>
The shape1 is currently spanning on 3 columns and on 2 rows. I’ll then create a new grid inside this area defined by 3 columns and 2 rows in order to have cells having exactly the same size as the cells of the main grid, whatever the resolution will be!
Once done, I’ll create 2 lines in order to mimic the Z shape of the falling block game. You should now have this result:
Even better, play with the various views available in the Device tab and you’ll see that our game is already implementing a responsive design! This is freaking cool, isn’t it? " />
Here is for instance the outputs for the snapped view and the portrait view:
Let’s now resolve another problem. The falling block grid gaming grid is composed of squares. Our current responsive design is stretching 100% width. Building a Windows 8 application for the Windows Store will most of the time meet widescreen monitors (current tablets are 1366x768 or 1920x1080 and most desktop PC have a 16/9 ratio). Let’s then assume that targeting a widescreen ratio is addressing almost all cases. To compute the proper responsive width, you need to do: 9/16 * 10/20 (the ratio of the main gaming grid) which equals to: 28.125%.
Add then this rule to target the main grid in full screen landscape mode:
@media screen and (-ms-view-state: fullscreen-landscape) {
.mainGrid {
width<span class="code-none">: 28.125%<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}
}</span></</span>span></</span>
Let’s now center the gaming grid by using… the CSS Grid Layout again! (And you should now start to believe it was truly designed for a falling block game!).
Switch the body element to –ms-grid
made of 1 column & 1 row:
body {
display<span class="code-none">: -ms-grid<span class="code-none"><span class="code-none">;
-ms-grid-columns<span class="code-none"><span class="code-none">: 1fr<span class="code-none"><span class="code-none">;
-ms-grid-rows<span class="code-none"><span class="code-none">: 1fr<span class="code-none"><span class="code-none">;
width<span class="code-none"><span class="code-none">: 100%<span class="code-none"><span class="code-none">;
height<span class="code-none"><span class="code-none">: 100%<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}</span></</span></span></span></span></span></span></span></span></span>span></</span>
Now simply add this attribute to the CSS associated to the the main grid:
-ms-grid-column-align: center;
And the grid is now centered:
At this stage, you’re probably already shocked. "How could I have missed this incredible secret?" you’re wondering to yourself. Take a breath. Now that you now THE secret, let’s continue this tutorial together to discover other awesome possibilities delivered by the CSS specifications combined together.
Step 2: moving or rotating a shape
My first idea was to try avoiding JS by using as much CSS as possible. I then first tried to use CSS3 Animations to move & animate the shape on the various rows/columns. But the bad news is that you can’t change the –ms-grid-column
or –ms-grid-row
values via CSS3 animations. This will then be the job of some JavaScript code.
I then started to think about how I will rotate the shape. CSS Transforms seemed to be perfectly adapted for that. Let’s check that by doing some experiments. Blend 5 is really cool for that as you can directly see in live the outcome of your changes.
Add a rotation of 90 degrees on shape1
by adding this class to its DIV element:
.shape1rotated {
transform<span class="code-none">: rotate(90deg)<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}</span></</span>span></</span>
I’m sure you weren’t expecting this:
Problem: it’s not properly aligned to the gaming grid. To align our shape to the grid, we need some small adjustments:
.shape1rotated {
transform-origin<span class="code-none">: 33% 50%<span class="code-none"><span class="code-none">;
transform<span class="code-none"><span class="code-none">: rotate(90deg) translateX(-33%)<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}</span></</span></span></span>span></</span>
And we now have the same rotation as a falling block game. Here are 2 screenshots before/after the rotation:
We can even go further than that by using a transition set on shape1 via this:
transition: all 1s ease-out;
And now, removing/adding the .shape1rotated
class on the shape1 DIV will trigger a smooth rotation animation!
At this stage, we could think that this approach is the good one to build our game. But this is unfortunately not yet the case. Here’s why. Try moving the shape by simply changing its –ms-grid-column
property. Blend 5 will reflect the changes directly. When not rotated, the shape can be moved up to the 8th column:
So far so good. But when you’re rotating it by adding the .shape1rotated
class to the DIV:
You see that there is still 1 row available on the right for the shape’s move. If you think we simply need to move it to the 9th row, you’re wrong! Indeed, here is what we’ll obtain on the 9th row:
You probably forgot that we’re currently moving a DIV element displaying a grid layout of 3 columns and 2 rows matching exactly the underlying gaming grid. When moving it, we really have the feeling that this is a block part of the main grid we’re moving. But for this tricks to work, we need at least 3 columns available to contain our shape element. If it’s contained in 2 columns (when set to the 9th column) or less, it will be “compressed” like in the screenshot.
There is 2 ways to resolve that.
- Stop using CSS Transforms and draw the rotated shape using another grid definition. For instance, using 3 div inside the shape instead of 2. But using such an approach will prevent us to have the nice CSS Animations in place.
- Redefined the main grid to work on 12 columns instead of 10 and we’ll use only the columns from 2 to 11 (a sort of clipping area if you wish). This will resolve our “overflow” problem.
Let’s implement the 2nd solution.
Redefine the main grid using that:
.mainGrid {
display<span class="code-none">: -ms-grid<span class="code-none"><span class="code-none">;
-ms-grid-columns<span class="code-none"><span class="code-none">: (1fr)[12]<span class="code-none"><span class="code-none">;
-ms-grid-rows<span class="code-none"><span class="code-none">: (1fr)[20]<span class="code-none"><span class="code-none">;
-ms-grid-column-align<span class="code-none"><span class="code-none">: center<span class="code-none"><span class="code-none">;
width<span class="code-none"><span class="code-none">: 100%<span class="code-none"><span class="code-none">;
height<span class="code-none"><span class="code-none">: 100%<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}</span></</span></span></span></span></span></span></span></span></span></span></span>span></</span>
To have also the proper ratio, you need to update the media query associated:
@media screen and (-ms-view-state: fullscreen-landscape) {
.mainGrid {
width<span class="code-none">: 33.75%<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}
}</span></</span>span></</span>
33.75% = 9/16 *12/20
Let’s also add a "virtual grid" to delimitate the space where we will be able to move the shapes. Inside the main grid DIV, insert this one:
<div class="virtualGrid">
</div>
Associated with this block of CSS:
.virtualGrid {
-ms-grid-column<span class="code-none">: 2<span class="code-none"><span class="code-none">;
-ms-grid-column-span<span class="code-none"><span class="code-none">: 10<span class="code-none"><span class="code-none">;
-ms-grid-row-span<span class="code-none"><span class="code-none">: 20<span class="code-none"><span class="code-none">;
border-right-style<span class="code-none"><span class="code-none">: dashed<span class="code-none"><span class="code-none">;
border-left-style<span class="code-none"><span class="code-none">: dashed<span class="code-none"><span class="code-none">;
background-color<span class="code-none"><span class="code-none">: #505A5A<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}</span></</span></span></span></span></span></span></span></span></span></span></span>span></</span>
It will help to delimitate the gaming area with a grey background and some dashed border lines.
Now, if I’m moving the Z shape on column 9, row 2, here is the result:
If I’m rotating it with CSS Transforms, I can move it correctly on column 10:
Bonus – handling the Portrait mode:
If you’d like to support the portrait mode (which is even better for the falling block grid game), add this CSS Media Query definition:
@media screen and (-ms-view-state: fullscreen-portrait) {
.mainGrid {
width<span class="code-none">: 106.66%<span class="code-none"><span class="code-none">;
<span class="code-none"><span class="code-none">}
}</span></</span>span></</span>
As the ratio need to be computed as = 16/9 * 12/20 = 106,66%.
Step 3: adding some code to handle part of the game logic
Now that we’ve solved the graphics part of the game using only some pure CSS & HTML code, we need the help of JavaScript to move/rotate the shape in the gaming area. We’re going to re-implement the CSS logic via a JS object that’s going to be defined thanks to WinJS.Class.
Open the "TheRealCSSGridStory" in Visual Studio 2012.
Create a TetrisShapeZ.js file in the JS directory and copy/paste this code:
(function () {
"use strict";
var ShapeZ = WinJS.Class.define(
function (columnIndex) {
this._shape1 = document.createElement("div");
var line1 = document.createElement("div");
var line2 = document.createElement("div");
this._shape1.className = "shape1";
line1.className = "line1shape1";
line2.className = "line2shape1";
this._shape1.appendChild(line1);
this._shape1.appendChild(line2);
this._defaultOrientation = true;
if (columnIndex) {
this._currentColPos = columnIndex;
this._shape1.style.msGridColumn = this._currentColPos;
}
else {
this._currentColPos = 1;
}
this._currentLinePos = 1;
this._fixed = false;
},
{
insertIntoGrid: function (element, width, height) {
element.appendChild(this._shape1);
this._gridWidth = width;
this._gridHeight = height;
this._maxLeft = width - 3;
this._maxBottom = height - 1;
},
rotate: function () {
if (!this._fixed && this._defaultOrientation) {
WinJS.Utilities.addClass(this._shape1, "shape1rotated");
this._defaultOrientation = false;
this._maxLeft = this._gridWidth - 2;
}
else {
if (!this._fixed && this._currentColPos < this._maxLeft) {
WinJS.Utilities.removeClass(this._shape1, "shape1rotated");
this._defaultOrientation = true;
this._maxLeft = this._gridWidth - 3;
}
}
},
_moveHorizontally: function (direction) {
if (!this._fixed && (this._currentColPos < this._maxLeft || direction === -1) && (this._currentColPos > 2 || direction === 1)) {
this._currentColPos = this._currentColPos + direction;
this._shape1.style.msGridColumn = this._currentColPos;
}
},
moveLeft: function () {
this._moveHorizontally(-1);
},
moveRight: function () {
this._moveHorizontally(1);
},
moveDown: function () {
if (!this._fixed) {
this._currentLinePos = this._currentLinePos + 1;
this._shape1.style.msGridRow = this._currentLinePos;
if (this._currentLinePos === this._maxBottom) {
this._fixed = true;
}
}
}
}
);
WinJS.Namespace.define("CSSTetris", { ShapeZ: ShapeZ });
} ());
Simply read the code to understand what it’s doing. It should be commented enough to be self-explicit.
Add a reference to this script file in default.html and keep only this block of HTML inside the body:
<div class="mainGrid">
<div class="virtualGrid">
</div>
</div>
Jump into default.js.
The cool part of having well-documented code is that we now have interesting IntelliSense details like for the constructor:
or the rotate
function:
To properly use this code, add this block of JS just after processAll
call:
document.addEventListener("keydown", OnKeyDown, false);
mainGrid = document.getElementsByClassName("mainGrid")[0];
myShape = new CSSTetris.ShapeZ(4);
myShape.insertIntoGrid(mainGrid, 12, 20);
init();
And add these 2 functions:
function init() {
setInterval(function () {
myShape.moveDown();
}, 1000);
}
function OnKeyDown(event) {
switch (event.keyCode) {
case KEYCODE_X:
myShape.rotate();
break;
case KEYCODE_LEFT:
myShape.moveLeft();
break;
case KEYCODE_RIGHT:
myShape.moveRight();
break;
case KEYCODE_DOWN:
myShape.moveDown();
break;
}
}
And we’re done! We now have a very basic game using CSS Grid Layout coupled with CSS Transforms & Animations for the graphics part and a couple of JS lines of code to have the beginning of the basics of a falling block game.
You can download the final Visual Studio solution corresponding to the 3 steps of this tutorial here: http://david.blob.core.windows.net/win8/TheRealCSSGridStory.zip
So, are you now convinced that CSS Grid Layout was made to simplify the creation of falling block games? " />
This article is part of the HTML5 tech series from the Internet Explorer team. Try-out the concepts in this article with 3 months of free BrowserStack cross-browser testing @ http://modern.IE.
David Rousset is a Developer Evangelist at Microsoft, specializing in HTML5 and web development. This article originally appeared on his MSDN blog, Coding4Fun on 12 Feb 2013.You can follow him @davrous on Twitter.