W3C home > Mailing lists > Public > www-style@w3.org > October 2010

Re: Positioned Layout proposal

From: Shelby Moore <shelby@coolpage.com>
Date: Thu, 21 Oct 2010 11:51:57 -0400
Message-ID: <4677326e58d010a5290aead2f6d3c0f3.squirrel@sm.webmail.pair.com>
To: shelby@coolpage.com
Cc: "Boris Zbarsky" <bzbarsky@mit.edu>, "www-style list" <www-style@w3.org>
Thinking about a fully generalized model for layout.

I am approaching the problem posited by this thread, from the perspective
that the way to successfully model such an egregarious divergence from
current CSS2.1, is to remap the existing CSS2.1 into a more general
metaphor. Then hypothetically, CSS2.1 becomes sub-classes (macros) of the
general metaphor. If we were to instead build more special cases to try to
understand the interactions, afaics we would be rapidly devolving towards
gridlock in CSS, especially with respect to the limitations of the
implementations of layout engines.

As an initial simplifying thought experiment, I restrict the following to
only one level of hierarchy with only relative element positioning (and
relative element sizing), and positioning relative to the parent document
element as the fixed point(s) of reference. I think perhaps this fully
generalizes CSS layout once it is extended to multi-level hierarchy. Note
I will not consider relative size here, simply to keep the examples less
verbose, but it seems to be straight forward to incorporate.

First the constraint solver walks all the relative relationships find to
find at least one relative reference to parent position. Otherwise, return
a hard fail.

Relative positioning is a 4D vector, [referenced elem, target elem,
direction, distance], where distance may be 'auto' and direction is at
least a 2D vector [ref elem edge, target elem edge], and where edge is at
least a 5 state enumeration [left|top|right|bottom|inline-valign], where
inline-valign (aka vertical-align) is at least a 8 state enumeration


So CSS2.1 { display:inline; vertical-align:baseline; letter-spacing:0;
text-align:left } is equivalent to following, except there is no line,
thus no wrap in relative positioning:


{ position:element; element:prev-sibling; rhpos:left-right; rhmargin:0;
rvpos:baseline; rvmargin:0; rsize:auto }

*Note 'rvpos:baseline' is shorthand for 'rvpos:baseline-baseline'.

**Note 'r?margin' is an enumeration [auto|<length>|flex], where default
'flex' is to justify except that relative sizing can consume these flex


We can add wrapping (thus lines) to relative positioning, it applies to
all children, and if defines the position amongst all possible groupings
of the children where wrapping is allow [none|each|letter|word]:

{ wrap:word }

Wrapping complicates the relative direction and distance constraints, so I
will not detail how to solve them for now.

***Note thus given prior inline emulation example, then {wrap:word} for
parent with {rhmargin:flex} for children, is equivalent to


The constraint algorithm places each 4D relationship vector into a hash
table, hashed on element for each of the elements in the pair of the
relationship. It also places each derivative relationship into these hash
tables. The size of the hash table is the combination n! / (2 * (n-2)!) =
n * (n - 1) / 2. Here is an example:

.rpos { position:element }
.fixed { element:parent; rhpos:left-left; rvpos:top-top }
.adjacent { element:prev-sibling; rhpos:left-right; rvpos:baseline }
.first { element:id; elem-id:first; rhpos:right-left; rvpos:baseline}

<parent style='wrap:
<elem class='rpos fixed' id='first'></elem>
<elem class='rpos adjacent'></elem>
<elem class='rpos first'></elem>

Note the above is unsolvable, because the 3rd elem wants to be left of the
1st elem, but the 1st elem wants to be fixed in the left, top corner of
the (docElem) parent's box.

The 6 (n = 4) entries for the hash tables are:

[parent, 1st elem, [[left, top], [left, top]], [0, 0]]]
[parent, 2nd elem, [[left, baseline], [left, baseline], [1st elem width, 0]]]
[parent, 3rd elem, [[left, baseline], [right, baseline], [0, 0]]]
[1st elem, 2nd elem, [[right, baseline], [left, baseline], [0, 0]]]
[1st elem, 3rd elem, [[left, baseline], [right, baseline], [0, 0]]]
[2nd elem, 3rd elem, [[left, baseline], [right, baseline], [1st elem
width, 0]]]


For each element, if any entries in the hash table are incongruent, then
the constraints have no solution, and hard fail is returned. For example,
the entries for the 3rd elem:

[parent, 3rd elem, [[left, baseline], [right, baseline], [auto, auto]]]
[1st elem, 3rd elem, [[left, baseline], [right, baseline], [auto, auto]]]
[2nd elem, 3rd elem, [[left, baseline], [right, baseline], [1st elem
width, auto]]]

where 'auto' can be any constant value (e.g. 0).

We see it wants to be 'rhpos:right-left' relative to the parent, which is
illegal, since the parent's box must contain the union of its child boxes.

Similarly, when compiling the entries, we would detect incongruencies
where circular derivative relationships want to be in opposite directions
or the same direction but with conflicting (i.e. not auto) distance.


In a possible future post, I may attempt to contemplate how we generally
solve the constraint vectors for wrapping, 2D viewports, 2D paged media,
and 2D overflow containers. Once we generally solve this, we will have I
think fully generalized CSS layout, since the CSS layout primitives can be
expressed in this generalized models. I need to think more about whether
that is true. Also what happens when we introduce multi-levels of
hierarchy. Also there will need to be a metaphor for flowing content into
a container and propagating to other containers, in order to support
dynamic columns.

This is as far as I can take it for now, because I have some other work to
do.  I just wanted to brain dump, in case it can help the brainstorming

>> On 10/20/10 1:38 PM, Shelby Moore wrote:
>>> The pagination algorithm tests the containing block for intersection
>>> with
>>> the page boundary, then it flags the constraint and re-runs the layout
>>> (which calls the various constraint algorithms).  This repeats until
>>> there
>>> are no more intersections.
>> Uh... in general, you never got to "no more interesections".  So I'm not
>> sure what you're talking about here.
> I will uncompact what I meant for the generalized algorithm (this is just
> off top of my head, I've never looked at browser layout code).
> 1) Run layout with the page rectangle as global constraint available to
> all methods. The methods make local decisions about their position and
> extent, e.g. the inline flow algorithm wraps (depending on wrapping style
> etc) to the Min( viewport, container ) where container might be 'auto'.
> The local decisions may cause their parents to clip.
> 2) Enumerate top-down hierarchy (i.e. box model) for paginate each
> container box (margins excluded), and flag any parents which need to be
> constrained.
> 3) If any were flagged, go back to step #1.
> In subsequent calls to step #1, the container will not longer be auto, but
> rather constrained so that it does not intersect the clip boundary.
> I am skipping the complication of prioritizing no clip in the horizontal
> direction, and using that constraint to push content to next page for
> paged media.
> The point being that none of the above depends on how the container boxes
> were positioned and sized.
>>> You optimize this for incremental updates, by making special case rules
>>> about how certain constructs can change layout when changed, e.g.
>>> identifying propogation boundaries, but you still need the generalized
>>> algorithm above for those cases that your special case optimizer can't
>>> handle.
>>> Am I far off base?
>> Dunno.  The one pagination setup in a browser that I'm very familiar
>> with works nothing like this; the second one I've sort of looked at
>> doesn't seem to either.
> I guess there are many ways to skin a cat.
> Sounds like an area I will enjoy experimenting and become more expert in.
Received on Thursday, 21 October 2010 15:52:34 GMT

This archive was generated by hypermail 2.3.1 : Tuesday, 26 March 2013 17:20:33 GMT