- From: Anton Prowse <prowse@moonhenge.net>
- Date: Mon, 09 Feb 2009 20:39:29 +0100
- To: "www-style@w3.org" <www-style@w3.org>
Andrew Fedoniouk wrote: > > Anton Prowse wrote: >> >> Andrew Fedoniouk wrote: >>> >>> As we know[1] rendering of static children of elements happens in >>> following order: >>> >>> for all static children: >>> step #1: draw backgrounds of these elements; >>> step #2: draw content of these elements (text in particular); >>> >>> This algorithm (strange, imo) works in most cases but plays >>> badly with negative margins. >> >> I do not find this algorithm strange. It provides a way of ensuring, >> by default, that text remains visible under various situations in >> which overflow and negative margins are thrown into the mix. If, for >> each box in turn, the text were painted at the same time as the >> backgrounds, it would not be possible to achieve this visibility. > > Static text can be covered by background of positioned elements. > Text can be clipped by overflow:ed elements. Of course. Positioned elements lie on a higher painting layer. Overflow:hidden explicitly instructs the UA to clip the contents. > Following your "this visibility" (as far as I understand it) > requirement we should draw the text on top of everything and > never clip it. > So clarification "this visibility" principle of yours will be appreciated. It's not a principle "of mine"; it's a description of the status quo, which certainly does not result in the behaviour that you extrapolated. Consider D1 and D2 as consecutive divs. D1 is static with a fixed height; perhaps it's a horizontal menu. D2 is static. Currently, the backgrounds of D1 and D2 are painted before the text of D1 and D2. When the user increases the text size via their browser, the contents of D1 may overflow. Happily, the text is still visible because it is painted /after/ the background of D2. This outcome cannot arise if the background and contents of D1 are painted together, before the background of D2. >>> Here is an example of very strange (non-intuitive if you wish) effect >>> that can >>> be observed when negative margins are used: >>> >>> <html> >>> <head> >>> <style> >>> div.red { width:100px; height:100px; >>> background-color:red; margin-bottom:-100px; } >>> div.green { width:90px; height:90px; >>> background-color:green; } >>> div.red:hover { background-color:orange; } >>> div.green:hover { background-color:lime; } >>> </style> </head> >>> <body> >>> <div class="red">This text has red background.</div> >>> <div class="green"></div> >>> </body> >>> </html> >>> >>> Problem here is that due to [1] these two elements have non-trivial >>> overlaying structure. >>> Each point inside div.green participate in following layers: >>> >>> 1) background of div.red >>> 2) background of div.green >>> 3) content(text) of div.red >>> 4) content(text) of div.green >>> >>> This clearly produces logical errors in detection of div.red:hover >>> and div.green:hover >>> conditions. Try to move mouse over and near the text. >> >> This is not a logical error. The text boxes are the "uppermost" of >> the boxes under the mouse and so receive the hover event. These text >> boxes form part of the red element, which is thus the element to which >> the event is propagated. (Events apply to (pseudo-)elements, not >> boxes, of course.) When you hover over the green box away from the >> text, the green box is the "uppermost" of the boxes under the mouse >> and so its corresponding element reacts to the hover event. > > I understand what is written in the spec. I imagine so; yet "this clearly produces logical errors in detection of [hover conditions]" is a rather odd choice of phrase given that the hover detection is working as specified. Question is "why?". Presumably the question you are actually asking is, "is this behaviour, which is a consequence of the algorithm currently specified, desirable?". Whilst I imagine that this edge-case behaviour was hardly the motivating factor for the current design of the stacking mechanism, it is an expected consequence of the algorithm and is it not troublesome. I am not clear about what specifically you are objecting to here. That hovering over the red box's text fires a hover event on the red element and not the green, even though the green element's background is foremost? This can be solved by positioning the green element. > Why text box of element "A" has to be rendered after background of some > other non-child element "B" is drawn? For the reason I stated above: visibility of overflowing line boxes. > If you have atomic rendering of element(its background and its > content as a whole) it is indistinguishable from the case > defined in the specification currently. No it isn't. The background of D2 would be drawn on top of the overflow from D1. > The only difference in rendering arises when you have negative > margins. And that is what I am talking about. > > That is also source of the problem mentioned by David Hyatt I think. The two issues are related but not equal in scope. David Hyatt was pondering whether elements with overflow should be made into (pseudo-)stacking contexts like floats and be painted on a higher painting layer than that for text. As far as I am aware, this was not because he felt that the non-overflow div should be atomic as concerns its background and text; rather, it was because there is some awkwardness in the particular case of overflow with (CSS- or JS-triggered) scrollbars. The change he proposed preserves the non-atomicity and focuses on changing the behaviour of an already rather special type of box. >>> That appears as a bug of the specification. Non-intuitive, >>> non-logical, etc. >> >> In no sense is this a bug in the specification. The behaviour is both >> intentional and desirable. In particular, if the specification were >> different on this point only, it would be impossible to reproduce the >> current behaviour (maximum text visibility) by other means, whereas it >> currently /is/ possible to obtain the results you seem to desire >> through the use of stacking contexts and z-index. > > "maximum text visibility" appears as a fuzzy logic statement. If it seems fuzzy, it was because I cannot have expressed myself well. Text should be as visible as possible subject to the desirable and currently-specified constraint of certain types of elements (currently positioned elements with non-negative z-index, and elements with opacity<1) being rendered on higher painting layers. Again, this is not /my/ principle; it is the current behaviour, and one which I think is good. > I would like to know why "overflow:hidden" complies to "maximum text > visibility" and say "margin-top:-10px;" is not. Because if overflow has explicitly been set to hidden, then the author has chosen to limit visibility. Whereas if the author has chosen to use negative margins, there may have been many varied reasons for doing so, unrelated to text visibility. More on this below. >>> Possible resolutions of the problem: >>> >>> A) To change [1] so drawing of the element background and its content >>> is atomic: >>> >>> For each static child draw its background and content(text) on top >>> of it. >>> (That is how it is made in all UI systems I know) >> >> This does not achieve the effect you desire, because the now-atomic >> background+text boxes of non-positioned in-flow elements will still be >> rendered in document tree order, and merely setting negative margins >> will not cause an earlier box to be painted on top of a later box. >> >> Moreover, the rendering which this approach does achieve is undesirable: >> >> A stacking context consists of several painting layers onto which >> different types of box are painted. Currently, in between the >> stacking layers for background and text of in-flow non-positioned >> dependants ("descendants") comes the stacking layer for non-positioned >> floated dependants. >> >> So proposal (A) effectively moves the painting layer for floats to >> after the now-unified painting layer for background and text of >> in-flow non-positioned dependants. Overflow from floats would now >> obscure the any adjacent text boxes belonging to their containing >> stacking context. Again, if you /want/ this behaviour you can >> currently have it through the use of stacking contexts; but if this >> behaviour were not the default you would not be able to reproduce it. > > I am not sure I understand you here. > > Static floating elements that overly the text they are "sitting in" > is a bug of implementation. The float does not overlay the text. However, your proposal (A) has the consequence that the /overflow/ from floats does overlay the text (instead of lying underneath the text that the floats "sit in", as per the current behaviour). This means that the surrounding text is obscured (and is possibly unselectable via traditional mouse interaction) if the float's overflowing content itself has a background. However, the argument against proposal (A) concerns not just overflow of floats but also overflow of static non-floated elements, as described earlier. >>> B) Treat all elements having negative margins as a separate layer >>> laying over >>> normal static children of the element thus rendering will happen >>> this way: >>> >>> step #1, for all static children *without* negative margins do: >>> step #1.1: draw backgrounds of elements; >>> step #1.2: draw content of elements (text in particular); >>> step #2, for all static children *with* negative margins do: >>> step #2.1: draw backgrounds of elements; >>> step #2.2: draw content of elements (text in particular); >> >> Whereas proposal (A) reorders the painting layers within a stacking >> context, proposal (B) introduces new painting layers in order to >> divide up the current behaviour in which boxes in the same painting >> layer are painted in document tree order. It is proposal (B) which >> was being discussed and formulated in your previous correspondence in >> a different thread. However, it does not seem well thought through. >> >> Your suggested change boils down to dividing the current painting step >> for text into two parts (text for non-negative- and text for >> negative-margined boxes) and inserting a new painting layer for the >> backgrounds of in-flow non-positioned dependants with negative margins >> in between. To avoid that this new painting layer for backgrounds sit >> on top of the painting layer for non-positioned floats (which would of >> course be unacceptable), the floats layer has to be moved to after the >> new text layer and we are more-or-less back to situation (A) and >> additionally we now have a different approach to honouring the >> document tree order. > > I think that A and B should be just combined together. > That would be most human comprehensible and intuitive :) solution. It would be less complex. It would also be less user-friendly because the current behaviour which ensures that text remains visible after increasing the text size or having strings which are too long for their container would be lost. Note that it is proposal A which is most destructive in this sense. >> Moreover, with this approach, if divs D1, D2 and D3 are in-flow and >> non-positioned in a common stacking context, and are such that both D1 >> and D2 have a negative margin-bottom, then D2 would overlap D1 and D3 >> would overlap D2. But this doesn't seem to solve the issue you were >> wishing to address, which was that by specifying a margin-bottom on D1 >> you wanted the bottom part of D1 to be rendered on /top/ of the top >> part of D2. To to address your issue you would actually have to >> differentiate between boxes with negative margin-top, boxes with >> negative margin-bottom and boxes with negative top and bottom margin >> in some way, and treat each case not only differently but also with >> respect to the margin values of the surrounding element; hence there >> are even more cases than these three, corresponding to the possible >> different combinations. The task of fleshing out the resulting >> algorithm - and expressing it in the specification - is not one which >> I would relish, nor one which has merit IMO. > > I do not think that it should be as such complex as you defined. Well, it does if we are to address the use case that you originally raised when you began this correspondence. I assume that you are no longer motivated by that use case. > It has to be just three layers: > 1) normal static elements drawn atomically; > 2) static elements with negative margins drawn atomically; > 3) static floated elements drawn atomically; > > This will solve problem mentioned by David Hyatt. > Thus it will allow in CSS to implement visual effects > without the need to redraw the whole tree from its root each time. This is true. However, aside from reducing the number of situations in which overflow remains visible, it is also (to me) less comprehensible and intuitive: would negative /horizontal/ margins trigger this effect (even though you use cases concerned negative vertical margins? It would be hard for authors to correlate negative horizontal margins with a painting layer promotion. >> Andrew Fedoniouk wrote: >> > <html> >> > <head> >> > <style> >> > .layer div { width:100px; height:100px; } >> > .layer div.principal { z-index:-1; position:relative; >> margin:-10px 0; >> > width:110px; } >> > .layer div:hover { background:gold !important; } >> > .aside { position:fixed; left:70px; top:0; bottom:0; width:10px; >> > background:magenta; z-index:-1;} >> > </style> </head> >> > <body> >> > >> > <div class="layer"> >> > <div class="static" style="background:red">Red</div> >> > <div class="principal" style="background:blue">Blue</div> >> > <div class="static" style="background:green">Green</div> >> > </div> >> > <div class="aside" >.</div> >> > </body> >> > </html> >> > >> > In this example I just wanted to put ".layer div.principal" element >> > behind its siblings. Intuitive solution for me is to use negative >> margins. >> >> Why? Negative margins are not for putting things behind other things >> in some specific order. That's what stacking contexts and z-index are >> for. > > What is the purpose of these "negative margins" then? For pulling the border area box past surrounding boxes (or pulling the surrounding boxes inwards past the border area box, depending upon your perspective). A typical example would be pulling left floats leftwards over their parent's left padding to create a less linear feel to a given design. The /stacking order/ that a negatively-margined box should have was not really even contemplated in CSS2.0 as far as I can tell -- it certainly didn't appear to be a primary consideration when introducing negative margins -- and this was part of the reason why stacking in CSS2.0 was so drastically underspecified. > Negative margins (NM) mean that one element is sitting on top of > another. I would like to see specification of what is sitting > on top of what. That is it. There is already specification of what is sitting on top of what. The issue is that you find this part of the specification disagreeable. > Current non-specified specification de-facto moves bottom of the NM > element underneath its next sibling and on top of its previous sibling. It is not "non-specified". You seem to be of the opinion that negative margins are somehow a forgotten aspect of the stacking mechanism. They're not. Boxes with the same painting layer in a stacking context are stacked back-to-front according to document tree order, irrespective of their margin values. > That creates stacking context by itself. But very strange one. I do not understand what you mean here. >> CSS3-COLOR[1] states: >> >> "If an element with opacity less than 1 is not positioned, >> implementations must paint the layer it creates, within its parent >> stacking context, at the same stacking order that would be used if it >> were a positioned element with ‘z-index: 0’ and ‘opacity: 1’. If an >> element with opacity less than 1 is positioned, the ‘z-index’ property >> applies as described in [CSS21], except that ‘auto’ is treated as ‘0’ >> since a new stacking context is always created." >> >> (Alas this paragraph introduces yet /another/ undefined term, >> "stacking order" to an already ambiguous description of the stacking >> model in CSS21. The phrase "at the same stacking order" is equivalent >> to the phrase "on the same painting layer" which I have been using.) > > Need of exceptions like this just highlights the flaw of > current way of rendering backgrounds of elements and their content > on separate layers. It doesn't; it merely highlights the exceptional nature of opacity. > In case of atomic rendering this particular > problem will not even exist. yes, but atomic rendering is a sledgehammer which "solves" some special cases such as David Hyatt's overflow issue and the exceptional behaviour of opacity, but at the significant cost of making overflow less useful. > This particular exception makes hacks like this: > > .static-nm > { > opacity:0.99; > border-bottom: 1px solid red; > margin-bottom: -1px; > } > > quite popular without doing any good for the rendering speed. I have never seen that in the wild, and I can think of no reason whatsoever why any author would choose to do that, given that exactly the same rendering can be achieved by .static-nm { position: relative; border-bottom: 1px solid red; margin-bottom: -1px; } without hurting rendering speed at all. >> CSS3-COLOR[1] states: >> >> "Since an element with opacity less than 1 is composited from a single >> offscreen image, content outside of it cannot be layered in z-order >> between pieces of content inside of it. For the same reason, >> implementations must create a new stacking context for any element >> with opacity less than 1." > > Atomic drawing assumes that *any* element establishes its own stacking > context for its children. I fully assume that you did not intend to mean that. Atomic drawing of an element's background and text is one thing. Forming a stacking context is an entirely different concept. > When elements interbreed background and content separately with > the rest of DOM tree you definitely will need more of those "but if"s. > ... wondering what will happen when we will start practically define > animations and the like effects on such flacky cake. Perhaps there will be more "but if"s. But each one (like opacity) is introduced due to some peculiar behaviour of the thing being introduced. Hence the right way to fix it is to special-case that thing. Your proposal (A) is less complex but also less powerful and less user-friendly than the current behaviour. I note that your last email almost entirely concerned proposal (A), whereas in my opinion proposal (B) is more interesting, although I dislike the resulting behaviour as concerns overflow, and I am unconvinced by the principle ("negative margins are special") which motivates it. Cheers, Anton Prowse http://dev.moonhenge.net
Received on Monday, 9 February 2009 19:40:22 UTC