另一個格線布局提案(轉: [css3-grid-layout] Alternative Grid Layout proposal)

有沒有人想來導讀一下?

-------- Original Message --------
Subject: [css3-grid-layout] Alternative Grid Layout proposal
Resent-Date: Tue, 04 Sep 2012 01:35:07 +0000
Resent-From: www-style@w3.org
Date: Tue, 4 Sep 2012 01:24:21 +0000
From: Linss, Peter <peter.linss@hp.com>
To: www-style@w3.org list <www-style@w3.org>

During the San Diego F2F I was tasked to come up with an alternative
proposal for grid layout. This is addressing two issues, the first being
preserving the concept of named grid lines (and harmonizing those names
with the names in the grid template syntax). I believe the level of
indirection between grid lines and grid items is extremely valuable. The
second issue is my opinion that the current direction of the grid layout
module has become narrowly focused on the task of application UI layout
and is not providing the kind of behavior typically expected from
traditional typographic layout grids. I feel the current draft is more
reminiscent of tables or 2D flexbox than a typographic grid layout paradigm.

To that end, here is my proposal. I believe it's compatible with all the
capabilities of the existing grid layout proposal, while also allowing a
more flexible usage more in keeping with traditional page layout.


First off, let's start with a definition of terms. There are no rows,
columns, or cells. Grid boxes have vertical and horizontal lines, lines
can be identified by their ordinal position, or by an identifier, called
its 'role'. Multiple roles can be assigned to a single grid line and the
same role can be assigned to multiple lines. A rectangular area defined
by four grid lines is called a 'field'.

ISSUE: are grid lines based on physical positions or logical positions?
i.e. in RTL writing mode do grid lines start from the right? What about
vertical writing modes, do horizontal and vertical lines swap? This
proposal presumes grid lines are physical only, logical names can be
addressed later.

ISSUE: are line roles specified by identifiers or strings? This proposal
uses identifiers, this can lead to potential collisions with line
positioning keywords. Using strings would eliminate those conflicts and
also allow roles and fields to contain spaces or other non-identifier
characters.


All grid boxes have four intrinsic lines at their content edges, they
have the roles: 'box-left', 'box-top', 'box-right', and 'box-bottom'.

Additional grid lines are defined by the properties:
'grid-lines-horizontal' and 'grid-lines-vertical'. The 'grid-lines-*'
properties take a comma separated list of 'line sets', the syntax of a
line set is essentially the same as the current definition of the
'grid-definition-*'  properties. Within a line set the position of each
line is based on the position of the preceding line. The position of
each line set begins at the left or top content edge. This allows the
definition of multiple sets of lines used for different purposes. This
further enables content based grid line positioning on non-adjacent grid
lines.

The grammar looks like this:
grid-lines-vertical: <line-list>
grid-lines-horizontal: <line-list>
<line-list>       => [ <line-set> [, <line-set> ]* ]
<line-set>        => [ <line-role>? <line-group> <line-role>? ]+
<line-group>      => <line-minmax> | repeat( <positive-integer> , [
<line-role>? <line-minmax> <line-role>? ]+ ) |
                       repeat-fill( [ <line-role>? <line-minmax>
<line-role>? ]+ )
<line-role>       = [ <identifier> ]*
<line-minmax>     => minmax( <line-separation> , <line-separation> ) |
auto | <line-separation>
<line-separation> => <length> | <percentage> | <fraction> | min-content
| max-content

ISSUE: should negative sizes be allowed between grid lines? I think so.


Grid fields can be defined with the 'grid-fields' property, using the
same syntax and rules of the current 'grid-template' property. The use
of a 'grid-fileds' property defines the initial horizontal and vertical
line sets with line spacing of 'auto' and line roles based on the field
name and line position relative to the field, i.e.:
grid-fields: "one one two" "three four four";

is equivalent to:
grid-lines-vertical: one-left three-left auto three-right four-left auto
one-right two-left auto two-right four-right;
grid-lines-horizontal: one-top two-top auto one-bottom two-bottom
three-top four-top auto three-bottom four-bottom;

In this way there is a direct mapping between fields and lines, any grid
lines with the roles of '*-left', '*-top', '*-right', '*-bottom'
inherently define a field, and can be referred to by the field name
component of the line roles.

When both 'grid-lines-*' properties and 'grid-fields' properties are
present, the number of lines and roles assigned by the 'grid-fields'
property remain, but the line spacing for the initial line set gets
overridden by the spacing in the 'grid-lines-*' properties. Additional
line roles defined in the 'grid-lines-*' properties are added to the
roles defined by the 'grid-fields' property.

ISSUE: should 'grid-fields' use a list of strings or lists of
identifiers separated by a commas? i.e.:
grid-fields: one one two, three four four;
Using identifiers seems more consistent, is it's decided to use strings
for field and role names, the horizontal grid lines can still be denoted
by commas, i.e.:
grid-flieds: "one" "one" "two", "three" "four" "four";

Some examples of the use of multiple line sets:
Given the grid fields:
+----------+-----+
|   one    | two |
+-------+--+-----+
| three |   four |
+-------+--------+
The vertical grid lines could be specified as:
grid-lines-vertical: one-left auto one-right two-left 1fr, three-left
1fr three-right four-left auto four-right;
allowing both fields 'one' and 'four' to be sized based on their
content. This is not possible under the current model.

You can also use line sets to place grid lines absolutely, i.e.:
grid-lines-vertical: 10px, 50px, 150px, 300px;


In the 'grid-lines-*' properties, the 'repeat()' function simply repeats
the pattern of lines and roles within it (there being no restriction on
the re-use of line roles). Additionally a 'repeat-fill()' function may
be useful, it would take a pattern of line positions and roles and would
repeat the pattern as many times as necessary to fill the box. There can
only be on 'repeat-fill()' within a line set. I'm willing got consider
this at risk as there are some interesting issues with 'repeat-fill()'
and auto line positioning vs auto grid container sizing. Perhaps a very
restricted use for level 1?


Now here's where I take a significant departure from the current model,
placing items within the grid. Rather than have the notion of grid
columns, rows, and spans, we simply use absolute or relative positioning
(and possibly 'page' and 'fixed'), and existing positioning properties
with a few convenient shorthands added for brevity.

In this model, all containing blocks are grid containers. We can either
define the 'grid-lines-*' properties automatically turn their box into a
containing block, or we can add an explicit 'containing-block' property
with the values of 'auto' or 'always' (which I really want for other
reasons as well).


As an example, given a grid container:

#grid {
    grid-lines-vertical: 50px images-left 50px text-left 50px
images-right 250px text-right 50px;
}

and the following markup:
<div id='grid'>
  <p>Some text...
  <img src='foo.gif'>
   more text...</p>
</div>

You can place the text and images with the following CSS:
p {
  position: absolute;
  left: grid-line(text-left);
  right: grid-line(text-right);
}
img {
  position: absolute;
  left: grid-line(image-left);
  right: grid-line(image-right);
}

The 'grid-line(<grid-line>)' function takes a single identifier,
followed by an optional integer, or a single integer. When an identifier
is followed by an integer, it means use the nth grid line with that
role. If a line of the given role cannot be located, the 'grid-line()'
function comutes to 'auto'. If a single integer is used, it is
specifying the ordinal number of the grid line to use. Ordinal values
begin counting at 0 and count the grid lines as defined in the
respective 'grid-lines-*' property, negative numbers count from the end.
If the ordinal value exceeds the number of defined grid lines,
additional grid lines are appended with a position of 'auto'.

The 'grid-line()' function can be used for the value of 'left', 'top',
'right', 'bottom', 'width', and 'height'. When used for 'width' (or
'height'), if both 'left' and 'right' are not 'auto', then width is
treated as 'auto'. If 'right' is 'auto', then the right side is located
at the grid line specified in 'width' as though relatively positioned
from the left side working rightwards. If 'right' is not 'auto' and
'left' is 'auto', then the left side is located by the grid line
specified relative to the right side working leftwards. 'height' works
similarly with the 'top' and 'bottom' sides.

You can mix and match left/top/right/bottom values with grid-line()
functions and any other allowable values for those properties. In the
preceding example, 'top' and 'bottom' are left to their default 'auto'
values. This has the effect of snapping the left and right edges of the
<p> and <img> elements to their assigned grid lines while leaving their
vertical positions where they would be if statically positioned.

Relative positioning works by locating the appropriate grid line
relative to the position the box would have if it were statically
positioned. For example, if a box is relatively positioned and the
'left' property is set to 'grid-line(column-left)', then the left edge
gets positioned at the first grid line to the right of the element's
static position with the role of 'column-left'. If the grid line is
specified with an ordinal value, then the nth grid line from the static
position is used. Negative numbers are allowed.


The following additional functions are defined for the values of the
'position' property:
grid-lines(<grid-line>[4])
grid-lines-relative(<grid-line>[4])
grid-field(<identifier>)
grid-field-relative(<identifier>)

ISSUE: should the order of lines in 'grid-lines()' be TRBL or LTBR? TRBL
is used in this proposal.
ISSUE: should 'grid-lines()' be allowed to take less than 4 grid lines?
If so, what are the unspecified lines defined as?

The 'grid-*()' functions when used in the position property act as
shorthands for the 'position', 'left', 'top', 'right' 'bottom', 'width',
and 'height' properties. For example, the following are equivalent:

position: grid-field(foo);
position: grid-lines(foo-top foo-right foo-bottom foo-left);
position: absolute; left: grid-line(foo-left); top: grid-line(foo-top);
right: grid-line(foo-right); bottom: grid-line(foo-bottom); width: auto;
height: auto;

The 'grid-*-relative()' versions of the functions set the 'position' to
'relative'.

For the 'grid-field()' functions, if lines with the roles based on the
field name are not present, those edges compute to 'auto'.
(Alternatively, 'width' and/or 'height' could compute to 'grid-line(1)',
meaning the next grid line).


Using positioning to place grid items, I believe, results in the same
grid line positioning algorithm as currently specified in the grid
layout module. The only added complexity is that, when placing the grid
lines, the grid container must traverse its positioned descendants to
find boxes positioned on pairs of grid lines in order to determine which
content is used to compute the line position (for those grid lines whose
positions are based on content).


One behavior of the current grid layout proposal not addressed so far is
the automatic sizing of the grid container (since positioned children do
not normally impact the size of the containing block). I have three
proposals for how to achieve the current behavior, in personal
preference order:
1) add a new value to 'width' and 'height', call it, 'all-content'. This
behaves similarly to 'fit-content' except that is also takes into
account positioned children.
2) keep 'display: grid' (which is otherwise no longer necessary). This
would behave like a normal containing block with the additional behavior
that 'width' and 'height' values of 'auto' account for grid positioned
children.
3) define 'auto' for grid containers to simply take grid positioned
children into account.


The other behavior of the current grid layout proposal not addressed so
far is the automatic grid item placement algorithm. I propose that this
behavior can be achieved using the yet-to-be defined 'collisions'
behavior that was also discussed at the San Diego F2F. I believe the
collisions behavior should have a property defining the direction to
search for alternative placement of positioned items when they collide
with other positioned items. When using grid positioning, subsequent
grid lines can be searched for by role, if specified, or physical position.


Currently, the 'justify-self' and 'align-self' properties are defined to
work on grid items. I propose that these be changed to apply to
positioned items when the relevant edges compute to 'auto'.


An additional behavior that I considered would be the ability to center
items on grid lines. One thought I had there is to add the properties
'center' and 'middle', which would have the default value of 'auto'.
When set for positioned items to something other than 'auto' and 'left'
and 'right' (or 'top' and 'bottom') are 'auto', these would place the
box's center (or middle) at the specified location (which could be any
location, not necessarily a grid line). This also would eliminate the
need for the 'center' value of 'position' in css3-positioning.


In addition, we need to specify the behavior of grid lines when the grid
container is fragmented. I propose the following property:
grid-break: slice | clone;
which behaves similarly to 'box-decoration-break'. 'clone' would repeat
all the grid lines in each fragment starting at the beginning. When used
on the viewport, this can easily create page grids.


Feedback welcome,

Peter

--------

As a sanity check, I recreated all the examples from the current grid
layout proposal in terms of this proposal. They are included below:


Example 1
<style type="text/css">
    #grid {
        width: 100%;
        height: 100%;

        /* Two columns: the first sized to content, the second receives
the remaining space,   */
        /* but is never smaller than the minimum size of the board or
the game controls, which */
        /* occupy this column. */
        grid-lines-vertical: auto minmax(min-content, 1fr);

        /* Three rows: the first and last sized to content, the middle
row receives the        */
        /* remaining space, but is never smaller than the minimum height
of the board or stats */
        /* areas. */
        grid-lines-horizontal: auto minmax(min-content, 1fr) auto
    }

    /* Each part of the game is positioned between grid lines by
referencing the index of */
    /* the line in the order top right bottom left, which establishes
bounds for the part. */
    #title    { position: grid-lines(0 1 1 0) }
    #score    { position: grid-lines(2 1 3 0) }
    #stats    { position: grid-lines(1 1 2 0); justify-self: start }
    #board    { position: grid-lines(0 2 2 1) }
    #controls { position: grid-lines(2 2 3 1); align-self: center }
</style>

<div id="grid">
    <div id="title">Game Title</div>
    <div id="score">Score</div>
    <div id="stats">Stats</div>
    <div id="board">Board</div>
    <div id="controls">Controls</div>
</div>


Example 2
<style type="text/css">
    #grid {
        width: 100%;
        height: 100%;
    }
    @media (orientation: portrait) {
        #grid {
            /* The lines and fields of the grid are defined visually
using the         */
            /* grid-fields property.  Each string is a row, and each
word a field.     */
            /* The number of vertical lines is determined by the number
of words + 1.  */
            /* The number of horizontal lines is determined by the
number of           */
            /* strings + 1. Note the number of words in each string must
be identical. */
            grid-fields: "title stats"
                         "score stats"
                         "board board"
                         "ctrls ctrls";

            /* Grid lines created with the template property can be
assigned a sizing */
            /* function with the grid-lines-vertical and
grid-lines-horizontal properties. */
            grid-lines-vertical: auto minmax(min-content, 1fr);
            grid-lines-horizontal: auto auto minmax(min-content, 1fr) auto
        }
    }

    @media (orientation: landscape) {
        #grid {
            /* Again the grid-field defines fields of the same name, but
this time */
            /* positioned differently to better suit a landscape
orientation.      */
            grid-fields: "title board"
                         "stats board"
                         "score ctrls";

            grid-lines-vertical: auto minmax(min-content, 1fr);
            grid-lines-horizontal: auto minmax(min-content, 1fr) auto
        }
    }

    /* The grid-area property places a grid item into named region
(area) of the grid. */
    #title    { position: grid-field(title) }
    #score    { position: grid-field(score) }
    #stats    { position: grid-field(stats) }
    #board    { position: grid-field(board) }
    #controls { position: grid-field(ctrls) }
</style>

<div id="grid">
    <div id="title">Game Title</div>
    <div id="score">Score</div>
    <div id="stats">Stats</div>
    <div id="board">Board</div>
    <div id="controls">Controls</div>
</div>



Example 3
<style type="text/css">
    #grid {
        /* The grid-lines-vertical and horizontal properties also
support assigning a role to grid lines */
        /* which can then be used to position grid items.  The line
roles are assigned on    */
        /* either side of a line placement function where the line would
logically exist. */
        grid-lines-vertical:
            start        auto
            track-start  0.5fr
            thumb-start  auto
            fill-split   auto
            thumb-end    0.5fr
            track-end    auto
            end;
    }

    /* grid-column and grid-row accept a starting and optional ending
line. */
    /* Below the lines are referred to by role. Beyond any semantic
advantage, the names  */
    /* also allow the author to avoid renumbering the grid-row-position
and      */
    /* column properties of the grid items.  This is similar to the
concept demonstrated in the */
    /* prior example with the grid-template property during orientation
changes, but    */
    /* grid lines can also work with layered grid items that have
overlapping areas of    */
    /* different shapes like the thumb and track parts  in this example. */
    #lower-label { position: absolute; left: grid-line(start); right:
grid-line(track-start); align-self: center }
    #track       { position: absolute; left: grid-line(track-start);
right: grid-line(track-end); align-self: center }
    #upper-label { position: absolute: left: grid-line(track-end);
right: grid-line(end); align-self: center }

    /* Fill parts are drawn above the track so set z-index to 5. */
    #lower-fill  { position: absolute; left: grid-line(track-start);
right: grid-line(fill-split); align-self: center; z-index: 5 }
    #upper-fill  { position: absolute: left: grid-line(fill-split);
right: grid-line(track-end); align-self: center; z-index: 5 }

    /* Thumb is the topmost part; assign it the highest z-index value. */
    #thumb       { position: absolute; left: grid-line(thumb-start);
right: grid-line(thumb-end); z-index: 10 }
</style>

<div id="grid">
    <div id="lower-label">Lower Label</div>
    <div id="upper-label">Upper Label</div>
    <div id="track">Track</div>
    <div id="lower-fill">Lower Fill</div>
    <div id="upper-fill">Upper Fill</div>
    <div id="thumb">Thumb</div>
</div>




Example 4 - actually n/a
<style type="text/css">
    #grid {
        grid-lines-vertical: 150px 1fr; /* two columns */
        grid-lines-horiztonal: 50px 1fr 50px /* three rows  */
    }
</style>



Example 5
<style type="text/css">
    #grid {
        grid-lines-vertical: 150px 1fr;
        grid-lines-horizontal: 50px 1fr 50px
    }

    #item1 {  position: grid-lines(0 2 3 1) }
</style>



Example 6
<style type="text/css">
    /* equivalent layout to the prior example, but using line roles */
    #grid {
        grid-lines-vertical: 150px item1-left 1fr item1-right;
        grid-lines-horiztonal: item1-top 50px 1fr 50px item1-bottom
    }

    #item1 {
 position: grid-lines(item1-top item1-right item1-bottom item1-left);
 /* alternatively */
 position: grid-field(item1);
    }
</style>




Example 7
<style type="text/css">
    /* using the grid-field syntax */
    #grid  {
        grid-fields: ". a"
                     "b a"
                     ". a";
        grid-lines-vertical: 150px 1fr;
        grid-lines-horizontal: 50px 1fr 50px
    }

    #item1 { position: grid-field(a) }
    #item2 { position: grid-field(b) }
    #item3 { position: grid-field(b) }

    /* Align items 2 and 3 at different points in the Grid field "b".  */
    /* The grid-field() position sets all four edges of the box, */
    /* individual sides can be overridden to align items. */
    #item2 { bottom: auto }
    #item3 { left: auto; top: auto }</style>




Example 8
<style type="text/css">
    #grid {
        grid-lines-vertical: 150px 1fr;
        grid-lines-horizontal: 50px 1fr 50px
    }
</style>



Example 9
<style type="text/css">
    #grid {
        grid-lines-vertical: first nav 150px main 1fr last;
        grid-lines-horizontal: first header 50px main 1fr footer 50px last;
    }
</style>



Example 10
<style type="text/css">
    #grid {
        grid-lines-vertical: 10px content 250px 10px content 250px 10px
content 250px 10px content 250px 10px;
    }

    /* Equivalent definition. */
    #grid {
        grid-lines-vertical: 10px repeat(4, content 250px 10px);
    }
</style>




Example 11
div { grid-lines-vertical: 100px 1fr max-content minmax(min-content, 1fr) }



Example 12
    /* examples of valid line definitions */
    grid-lines-horizontal: 1fr minmax(min-content, 1fr);
    grid-lines-horizontal: 10px repeat(2, 1fr auto minmax(30%, 1fr));
    grid-lines-horizontal: (10px);
    grid-lines-horizontal: calc(4em - 5px)




Example 13
<style type="text/css">
    #grid {
        width: 500px;
        grid-lines-vertical:
            a     auto
            b     minmax(min-content, 1fr)
            b c d repeat(2, e 40px)
                  repeat(5, auto);
    }
</style>
<div id="grid">
    <div style="position: absolute; left: grid-line(a); right:
grid-line(b); width:50px"></div>
    <div style="position: absolute; left: grid-line(8); width:50px"></div>
</div>
<script type="text/javascript">
    // Returns 'a 50px b 320px b c d repeat(2, e 40px) repeat(4, 0px) 50px'.
    var gridElement = document.getElementById("grid");
    window.getComputedStyle(gridElement,
null).getPropertyValue("grid-lines-vertical");
</script>




Example 14
<style type="text/css">
    #grid {
        grid-fields: "head head"
                     "nav  main"
                     "foot ."
    }
    #grid > a {
        position: grid-field(nav);
    }
</style>




Example 15  - n/a as no positioned children of a grid container simply
use static positioning


Example 16
<style type="text/css">
#item {
    /* the following two property definitions are equivalent */
    /* both place the item between the first and third line */
    /* which is covering the first and second row of the Grid */
    top: grid-line(0); bottom: grid-line(2);
    top: grid-line(0); height: grid-line(2);
}
</style>



Example 17 & 18 - n/a shorthands no longer present



Example 19
<style type="text/css">
    #grid {
        display: grid;
        grid-lines-horizontal: header auto main 1fr footer auto;
    }

    #header { position: absolute; top: grid-line(header); bottom:
grid-line(main) }
    #main   { position: absolute; top: grid-line(main); bottom:
grid-line(footer) }
    #footer { position: absolute; top: grid-line(footer); bottom:
grid-line(box-bottom) }

    /* Equivalent to the above using grid line numbers instead of names. */
    #header { position: absolute; top: grid-line(0); bottom: grid-line(1) }
    #main   { position: absolute; top: grid-line(1); bottom: grid-line(2) }
    #footer { position: absolute; top: grid-line(2); bottom: grid-line(3) }
</style>



Example 20
<style type="text/css">
    #grid { grid-lines-vertical: 20px; grid-lines-horizontal: 20px }
    #A { position: absolute; left: grid-line(0); top: grid-line(0) }
    #B { position: absolute; left: grid-line(5); top: grid-line(0);
height: grid-line(2) }
    #C { position: absolute; left: grid-line(0); top: grid-line(1);
width: grid-line(2) }
</style>

<div id="grid">
    <div id="A">A</div>
    <div id="B">B</div>
    <div id="C">C</div>
</div>



Example 21 - depends on to be defined collisions property
<style type="text/css">
    form {
 display: block;
        grid-lines-vertical: labels auto controls auto oversized auto;
        grid-lines-horizontal: repeat-fill(row auto) buttons auto
    }
    form > input, form > select {
        /* Place all controls in the "controls" column and automatically
find the */
        /* next available row. */
        position: absolute;
        left: grid-line(controls);
        top: grid-line(row);
        collision: down;
    }
    form > label {
        /* Place all labels in the "labels" column and automatically
find the next
        /* available row. */
        position: absolute;
        right: grid-line(controls);
        top: grid-line(row);
        collision: down;
    }

    #department {
        /* Auto place this item in the "oversized" column in the first
row where an area that  */
        /* spans three rows won't overlap other explicitly placed items
or areas or any items */
        /* automatically placed prior to this area. */
        position: absolute;
        left: grid-line(oversized);
        top: grid-line(row);
        collision: down;
        height: grid-line(3);
    }

    /* Place all the buttons of the form in the explicitly defined grid
area. */
    #buttons {
        position: absolute;
        top: grid-line(buttons);
        right: grid-line(box-right);
    }
</style>
<form action="#">
    <label for="firstname">First name:</label>
    <input type="text" id="firstname" name="firstname" />
    <label for="lastname">Last name:</label>
    <input type="text" id="lastname" name="lastname" />
    <label for="address">Address:</label>
    <input type="text" id="address" name="address" />
    <label for="address2">Address 2:</label>
    <input type="text" id="address2" name="address2" />
    <label for="city">City:</label>
    <input type="text" id="city" name="city" />
    <label for="state">State:</label>
    <select type="text" id="state" name="state">
        <option value="WA">Washington</option>
    </select>
    <label for="zip">Zip:</label>
    <input type="text" id="zip" name="zip" />

    <div id="department">
        <label for="department">Department:</label>
        <select id="department" name="department" multiple>
            <option value="finance">Finance</option>
            <option value="humanresources">Human Resources</option>
            <option value="marketing">Marketing</option>
        </select>
    </div>

   <div id="buttons">
       <button id="cancel">Cancel</button>
       <button id="back">Back</button>
       <button id="next">Next</button>
   </div>
</form>




Example 22
<style type="text/css">
    #grid {grid-lines-vertical: 1fr 1fr; grid-lines-horizontal: 1fr 1fr }
    #A { position: absolute; left: grid-line(0); bottom: grid-line(2);
width: grid-line(2) }
    #B { position: absolute; left: grid-line(0); top: grid-line(0);
z-index: 10 }
    #C { position: absolute; left: grid-line(1); top: grid-line(0);
margin-left: -20px }
    #D { position: absolute; right: grid-line(2); top: grid-line(1) }
    #E { position: absolute; z-index: 5;
         justify-self: center; align-self: center
    }
</style>

<div id="grid">
    <div id="A">A</div>
    <div id="B">B</div>
    <div id="C">C</div>
    <div id="D">D</div>
    <div id="E">E</div>
</div>

Received on Monday, 10 September 2012 16:46:19 UTC