In current HTML codification trends, developers prefer construct HTML documents, pages and applications based on columns, against the table technique of row based design. This techniques commonly include the use of floated divs.
With the complexity and inconsistency between browsers implementation of standards, and in order to provide more solid code bases, many developers targeted this layout problems with CSS Frameworks, almost all of them based on columns, and with a very different set of approaches and features. The second generation of CSS Frameworks are a great advance in CSS code standardization, they are providing a richer set of features and a great flexibility to accomplish high complexity designs.
The first generation of CSS Frameworks provided fixed width capabilities, and very limited or null nesting capabilities. This frameworks increased the time of development but only at the very high level of design, leaving the developers alone for most of the job. Even when they helped a lot, there was not a complete solution.
In the second generation of CSS Frameworks we have complex nesting, layout width variability, and support for fixed, liquid, and elastic layouts, But the transition has not been by any means smooth.
Going from fixed, to liquid layouts present a very complex challenge to framework developers. The resulting width of columns are commonly float values, W3C has not created a description or a standard solution to solve this problem, leaving the solution up to the browser developers.
The purpose of this study is determine what solutions we currently have, from Browsers and CSS Frameworks.
Making a wide set of test, varying the number of columns of a block, the algorithm used to get to the rendered layout can be determined by the analysis of the rendered pixels with an image editor.
This study will help to determine if the current Elastic CSS Framework Engine is a good solution for sub pixel rounding, and how this engine compares to the browsers and other frameworks.
A set of blocks of same width, every block, will vary the number of columns from 1 to 10, and an extra block of 15 columns, to test a non continuous block.
Every block is paired with a set of columns equal to the test created with Elastic CSS Framework to serve as control. All tests have a background of alternating pixel colors to simplify measurements.
All testing blocks must lead to a float measure for columns in order to test the rounding of those float values (183px).
The test were run on mayor browsers, and a screen shot will be taken.
Every column will have a 2px padding to provide clear separation between columns, and a solid background color to be measured with image editing software.
Measures will be done with gimp, and then the results will be analyzed in order to try to infer the underlying algorithm.
Test were run on different platforms, and browsers.
Special considerations, Elastic rounding algorithm leads to -1px on test for 10 columns and 1 column, this did not affected the analysis, and corrections were made at measurement.
Try the test case on your browser
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
|---|---|---|---|---|---|---|---|---|---|---|---|
| 10 Columns | 14.7 | 14.7 | 14.7 | 14.7 | 14.7 | 14.7 | 14.7 | 14.7 | 14.7 | 14.7 | |
| Elastic 2.0 | 15 | 15 | 15 | 15 | 15 | 15 | 15 | 14 | 14 | 14 | |
| Firefox 3.0, 3.5 | 15 | 14 | 15 | 15 | 15 | 14 | 15 | 15 | 14 | 15 | |
| Safari | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 14 | |
| Opera | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 14 | 14 | |
| Internet Explorer | 15 | 15 | 15 | 15 | 15 | 15 | 15 | 15 | 15 | 15 | |
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 9 Columns | 16.78 | 16.78 | 16.78 | 16.78 | 16.78 | 16.78 | 16.78 | 16.78 | 16.78 | ||
| Elastic 2.0 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 16 | 16 | ||
| Firefox 3.0, 3.5 | 17 | 17 | 16 | 17 | 17 | 17 | 16 | 17 | 17 | ||
| Safari | 16 | 16 | 16 | 16 | 16 | 16 | 16 | 16 | 16 | ||
| Opera | 16 | 16 | 16 | 16 | 16 | 16 | 16 | 16 | 16 | ||
| Internet Explorer | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | 17 | ||
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 8 Columns | 19.38 | 19.38 | 19.38 | 19.38 | 19.38 | 19.38 | 19.38 | 19.38 | |||
| Elastic 2.0 | 19 | 19 | 19 | 19 | 19 | 20 | 20 | 20 | |||
| Firefox 3.0, 3.5 | 19 | 20 | 19 | 19 | 20 | 19 | 20 | 19 | |||
| Safari | 19 | 19 | 19 | 19 | 19 | 19 | 19 | 19 | |||
| Opera | 18 | 18 | 18 | 18 | 18 | 18 | 18 | 18 | |||
| Internet Explorer | 19 | 19 | 19 | 19 | 19 | 19 | 19 | 19 | |||
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 7 Columns | 22.71 | 22.71 | 22.71 | 22.71 | 22.71 | 22.71 | 22.71 | ||||
| Elastic 2.0 | 23 | 23 | 23 | 23 | 23 | 22 | 22 | ||||
| Firefox 3.0, 3.5 | 23 | 22 | 23 | 23 | 23 | 22 | 23 | ||||
| Safari | 22 | 22 | 22 | 22 | 22 | 22 | 22 | ||||
| Opera | 22 | 22 | 22 | 22 | 22 | 22 | 22 | ||||
| Internet Explorer | 23 | 23 | 23 | 23 | 23 | 23 | 23 | ||||
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 6 Columns | 27.17 | 27.17 | 27.17 | 27.17 | 27.17 | 27.17 | |||||
| Elastic 2.0 | 27 | 27 | 27 | 27 | 27 | 28 | |||||
| Firefox 3.0, 3.5 | 27 | 27 | 27 | 28 | 27 | 27 | |||||
| Safari | 27 | 27 | 27 | 27 | 27 | 27 | |||||
| Opera | 25 | 25 | 25 | 25 | 25 | 25 | |||||
| Internet Explorer | 27 | 27 | 27 | 27 | 27 | 27 | |||||
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 5 Columns | 33.4 | 33.4 | 33.4 | 33.4 | 33.4 | ||||||
| Elastic 2.0 | 33 | 33 | 33 | 34 | 34 | ||||||
| Firefox 3.0, 3.5 | 33 | 34 | 33 | 34 | 33 | ||||||
| Safari | 33 | 33 | 33 | 33 | 33 | ||||||
| Opera | 33 | 33 | 33 | 33 | 33 | ||||||
| Internet Explorer | 33 | 33 | 33 | 33 | 33 | ||||||
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 4 Columns | 42.75 | 42.75 | 42.75 | 42.75 | |||||||
| Elastic 2.0 | 43 | 43 | 43 | 42 | |||||||
| Firefox 3.0, 3.5 | 43 | 43 | 42 | 43 | |||||||
| Safari | 42 | 42 | 42 | 42 | |||||||
| Opera | 42 | 42 | 42 | 42 | |||||||
| Internet Explorer | 43 | 43 | 43 | 43 | |||||||
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 3 Columns | 58.3 | 58.3 | 58.3 | ||||||||
| Elastic 2.0 | 58 | 58 | 59 | ||||||||
| Firefox 3.0, 3.5 | 58 | 59 | 58 | ||||||||
| Safari | 58 | 58 | 58 | ||||||||
| Opera | 57 | 57 | 57 | ||||||||
| Internet Explorer | 58 | 58 | 58 | ||||||||
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 2 Columns | 89.5 | 89.5 | |||||||||
| Elastic 2.0 | 90 | 89 | |||||||||
| Firefox 3.0, 3.5 | 90 | 89 | |||||||||
| Safari | 89 | 89 | |||||||||
| Opera | 89 | 89 | |||||||||
| Internet Explorer | 90 | 90 | |||||||||
| Column | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | |
| 1 Column | 183 | ||||||||||
| Elastic 2.0 | 183 | ||||||||||
| Firefox 3.0, 3.5 | 183 | ||||||||||
| Safari | 183 | ||||||||||
| Opera | 183 | ||||||||||
| Internet Explorer | 183 | ||||||||||
Even though this study only reveals one part of the algorithm of render engines, its a problem that should appear in a wide number of cases
of web development. Further studies could reveal other parts of this algorithm.
var containerWidth;
var columnPercentageWidth;
var columns;
var columnWidth = Math.round(containerWidth * ( columnPercentageWidth / 100 ));
var column;
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}
var computedWidth = columnWidth * columns.length;
var difference = containerWidth - computedWidth;
var positionDivision = columns.length / (Math.abs(difference) + 1);
var increment = (difference > 0);
var direction = -1;
if(difference !== 0)
for(var i = 1; i <= (Math.abs(difference)); i++){
if(direction == -1){
column = columns[columns.length - Math.floor( positionDivision * Math.round(i/2) )];
}
else{
column = columns[Math.floor( positionDivision * Math.round(i/2) ) - 1];
}
if(increment){
column.style.width = columnWidth + 1;
}
else{
column.style.width = columnWidth - 1;
}
direction = direction * -1;
}
var columns;
var columnWidth = Math.floor( containerWidth * ( columnPercentageWidth / 100 ) )
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}
var columns;
var columnWidth = Math.floor( containerWidth * ( Math.floor( columnPercentageWidth ) / 100 ) );
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}
var columns;
width = Math.round( containerWidth * ( columnPercentageWidth / 100 ) )
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}
var containerWidth;
var columnPercentageWidth;
var columns;
var columnWidth = Math.round(containerWidth * ( columnPercentageWidth / 100 ));
var column;
for(var i = 0; i < columns.length; i++){
column.style.width = columnWidth;
}
var computedWidth = columnWidth * columns.length;
var difference = containerWidth - computedWidth;
var positionDivision = columns.length / (Math.abs(difference) + 1);
var increment = (difference > 0);
var direction = -1;
if(difference !== 0)
for(var i = 1; i <= (Math.abs(difference)); i++){
column = columns[columns.length - i )];
if(increment){
column.style.width = columnWidth + 1;
}
else{
column.style.width = columnWidth - 1;
}
}
The code presented here may not be the actual code, because it was not copied, was infered. Also code must have extra considerations for more cases
This results are valid for the cases considered in the study, and are confirmed by the control case. Combinations of diferent types of blocks may lead to a different set of results, but that is outside of the scope of this study.
CSS Frameworks based on grid systems (blueprint, 960gs, bluetrip, and similar) are based on divisions that lead to integer results for every column, this avoids falling in the problem of pixel rounding and do not need a solution, but a limitant on this frameworks is that they do not support liquid layouts. Implementing liquid layouts on this type of frameworks would force to find a pixel rounding solution.
CSS Frameworks based on column combinations that support liquid layouts, have different solutions to deal with this.
YUI uses a set of classes that define different width and float left or right according to the context of its container and the browser.
This still presents the problem that the computed values varies according to the algorithm implemented by the browser. Scaling this to more than 4 columns would lead to a high multiplicity of exceptions on css code.
OOCSS uses a class “last” that its not floated, contrary to the rest of the columns in the unit, this causes that the last column gets reduced
by the space of the rest of the columns, but also depends on the computed withs of the algorithm implemented by the browser. scaling this to more columns would reveal the algorithm errors on browsers leading to more evident problems in the final width of columns.
Elastic provides a JavaScript Based override to the computations making a completely predictable width in all browsers. even though is a little slower because overrides the native render engine computations, it can scale to many columns, and never blocks the flow of content.
Browsers should be more open in their implementations, that way they would help the creation of a standard in rounding widths, and other areas.
An open documentation of this solutions would help web developers understand better the problems, and solutions in browsers, and this would help to advance the standardization and development to a faster ratio.
A standardization on this subject would simplify the advance and improve speed of Elastic CSS Framework Engine performance leaving this operations to a lower level. Also will open the path to a collaboration between framework authors instead of a competition.
If anyone would like to join efforts with Elastic CSS Framework core team, we are open to collaborate with others to improve the features of frameworks and maybe make recommendations to browsers and W3C.
This will open a discussion inside Elastic CSS Framework to see if the current algorithm should be changed to match Firefox, this also is an invitation to everyone to participate.
did you try to run the demo that we used to get this numbers?
the flick when doing rezise cant be avoided because elastic columns cant determine its widths before computing it. there are several thing you could do, avoiding the reset, predefining the width with a class, or something like this could help you alot.
I have a fixed column (on the left) next to a elastic column (on the right) and I have some troubles using IE8 and X-UA-Compatible set to “IE=edge” (the lastest version), I think IE8 uses the Firefox algorithm.
The problem is that the elastic column sometimes goes at the bottom of the fixed column while I’m resizing the window.
Would you implement IE8 algorithm?, How can I handle this error?