[css3-page] Margin boxes: replacing 6.3.2 Variable Dimension Computation Rules

Hi,

Let’s talk about margin boxes. (Warning: long email)

First, I would like to ask a question to all implementations that have 
margin boxes: What do you do about section 6.3.2, Variable Dimension 
Computation Rules? Do you implement it exactly as specified? Maybe only 
in some cases? Or do you do something else entirely?

(Please CC more people if you know of other affected implementations.)


Short version
=============

I think that − as currently spec’ed − 6.3.2 is unreasonably hard to 
implement, under-specified and not even sufficient for the desired 
behavior. I would like to get consensus that it needs to be re-written.

If we can establish that a replacement is needed, I have a proposal 
below that we can discuss.


Longer version
==============

(You can skip this if you agree and don’t need to be convinced.)

The Variable Dimension Computation Rules determine how, for example, the 
width is distributed between top-left, top-center and top-right margin 
boxes.

The current spec is hard to implement
-------------------------------------

The specified behavior is a quadratic optimization problem of up to 9 
variables under both equality and inequality constraints.

I studied for a while Karesh–Kuhn–Tucker, modified Simplex algorithms 
and other stuff that looked like it could solve this kind of problem. 
But this is just beyond my reach, although I have a decent understanding 
of university-level math.

The alternative is to use an existing solver. These are often part of 
big numerical libraries that are not otherwise needed for CSS, contain 
more code than my whole layout engine, and are generally hard to 
integrate. I’m not even talking of the runtime cost of such algorithms.

I understand how quadratic optimization can give attractive results, but 
I argue that the burden on implementations is simply to high. It does 
not compare to anything else in CSS. (I mean, implementing 3D transforms 
from scratch is pretty hard, but this is about sizing three simple boxes!)

The current spec is under-specified
-----------------------------------

Let us take a document where we can set all margins to zero while 
staying under the various constraints. This is obviously the optimal 
solution to the quadratic problem. But since the widths do not appear at 
all in the goal function, we may still have an infinity of solutions!

In other words, the optimization does not tell us how to set the used 
values for 'width'.

The current spec does not encode the desired behavior
-----------------------------------------------------

Quoted from Example 18:

> As the intrinsic width of the top-left contents is approximately
> twice the intrinsic width of that of the top-right, the top-left
> margin box is approximately twice as wide as the top-right margin
> box.

This "proportional" behavior seems interesting to have, but it is 
nowhere to be found in normative text!

Related to the previous section: when multiple optimal solutions exist, 
general-purpose solvers will only give one. There is no guarantee that 
it will widths anywhere near proportional.

(Side note: maybe avoid to mix the "intrinsic" and "preferred" 
terminologies)
http://dbaron.org/css/intrinsic/
http://www.w3.org/TR/CSS21/visudet.html#float-width


Proposed replacement
====================

Even without the quadratic stuff, what the current spec tries to do with 
'auto' margins is still very complicated. 9 properties that may or may 
not be 'auto' yield a lot of combinations.

Therefore, I suggest the following rule:

  | If the margin-left or margin-right property of any of the
  | three boxes computes to 'auto', the used value is zero.

It *is* a loss a functionality, but I think the trade-off is worth it.
This change is essential in my proposal, which is still nowhere near 
Fixed Dimension Computation Rules’s simplicity.

Remember that this is three simple boxes with (most often) very small 
texts. Any fancy layout is probably overkill.

Other than that, a side effect is that margin boxes with 'width: auto' 
will never grow larger than their preferred width. I don’t know if this 
is a problem; authors can always use percentage widths if 'auto' does 
not give the desired behavior.

"is instantiated" is used with reference to 6.2 instead of "is not empty 
or if its computed width is not ‘auto’".

Indentation matters in the algorithm below. I don’t know if there is
a way to reduce nesting while keeping the text clear enough.

===== Start of proposed text =====

6.3.2. Margin Box Variable Dimension Computation Rules

The following rules apply to ‘top-left’, ‘top-center’ and ‘top-right’
margin boxes, which are referred to in this section as A, B, and C,
respectively.

If the margin-left or margin-right property of any of the three boxes
computes to 'auto', the used value is zero.

The width of the containing block is known at this point, so percentages
can be resolved. Only the used values for 'auto' widths remain to
be determined.

Definitions:

*preferred outer width* of a box
     If width is 'auto', the preferred width plus left and right margins,
     borders and padding.
     Otherwise, the outer width.

*preferred minimum outer width* of a box
     If width is 'auto', the preferred minimum width plus left and right
     margins, borders and padding.
     Otherwise, the outer width.

*overall available width*
     the width of the containing block minus the left and right margins,
     borders and padding of all three boxes.

Algorithm:

* If B is not <a href="#populating-margin-boxes">instantiated</a>

   * If both A’s and C’s widths are 'auto'

     * If the sum of their preferred minimum outer widths is greater
       than the width of the containing block, their used width are
       their respective preferred minimum widths.

     * Otherwise, if the sum of their preferred outer widths is less
       than the width of the containing block, their used width are
       their respective preferred widths.

     * Otherwise, their used widths are chosen to add up to the
       overall available width, proportionally to their respective
       preferred width.

   * Otherwise, if one of A’s or C’s width is 'auto', its used width is
     the "shrink-to-fit" width with the available width set to:

     * the containing block’s width
     * minus the outer width of the other box.
     * minus the box’s margins, borders and padding

* Otherwise (if B *is* instantiated)

   * If B’s width is 'auto'

     * If neither A’s width or C’s width are 'auto', B’s used width is
       the "shrink-to-fit" width with the available width set to:

       * the containing block’s width
       * minus the greater of A’s and C’s outer widths

     * Otherwise

       * Define AC-minimum as the greater of A’s and C’s preferred
         minimum outer width, and AC-preferred as the greater of their
         preferred outer width.

       * If B’s preferred minimum outer width plus AC-minimum is greater
         than the width of the containing block, the used width for B
         is its preferred minimum width.

       * Otherwise, if B’s preferred outer width plus AC-preferred is
         less than the width of the containing block, the used width
         for B is its preferred minimum width.

       * Otherwise, set any 'auto' width proportionally to the
         corresponding preferred width so that they add up to the
         overall available width minus any non-'auto' width.

   * If A’s width or C’s width or both are still 'auto', their
     respective used width are the "shrink-to-fit" width with the
     available width set to:

     * half of the containing block’s width
     * minus half of the outer width of B.
     * minus the box’s (A or C) margins, borders and padding


Now that all three boxes have a determined outer width, they are
positioned so that:

* The left outer edge of A is flush with the left edge of the
   containing block
* The outer area of B is centered in the containing block.
* The right outer edge of C is flush with the right edge of the
   containing block.

Although the rules above try to avoid it, the boxes may end up
overlapping each other.

===== End of proposed text =====

The note on negative widths and what follows (how the rules translate
for margin boxes on the bottom, left and right sides) are kept as-is.

A, B and C could be replaced by "the {left,center,right} box" in the
text, whichever is a better style.


That’s it (!)
Thoughts?

-- 
Simon Sapin

Received on Monday, 11 June 2012 21:45:54 UTC