[css3-box] Re: Splitting 'display'

Seems that Tab and I have both been thinking about this in recent days![0]

I haven't yet compared what's in [0] with what's below, but I will do 
shortly.  Bert and I are currently discussing what I've written below, 
which is why I hadn't posted it so far; but seeing as the topic is live 
right now I think it's worth posting straight away so that we can all 
compare the issues and figure out how best to proceed.

[0] http://lists.w3.org/Archives/Public/www-style/2012Oct/0552.html


On Wed, 21 Apr 2010 08:49:42 -0700 Tab Atkins Jr. wrote:
>> On Fri, 16 Apr 2010 12:23:18 -0700 Tab Atkins Jr. wrote:
>>> On Fri, 16 Apr 2010 11:10:25 -0700 Tab Atkins Jr. wrote:

>>>> In preparation for a mild rewrite of Flexbox to make the concepts
>>>> expressed in it map to a cleaner model, I've gone ahead and revived
>>>> the idea of "display" as a shorthand.  This is *long* overdue.  I took
>>>> some text from an older WD of the Box module.

A couple of years on, and I've been thinking about this afresh for 
css3-box.  To get another perspective and to re-evaluate assumptions, I 
decided to first approach the problem completely independently from the 
above thread and other past ones; however, when I compared my resulting 
model with Tab's (an update version of which is presented in 
http://www.xanthir.com/blog/b45F0 ), I found that our thinking is pretty 
much identical.

>>> It's probably good to explain why I did this.  [...]
>>>
>>> [...] our conflation of the inside
>>> and outside values has gotten somewhat ridiculous.  At the very least,
>>> any new display value has to be duplicated as a block and inline
>>> version.  This doesn't cover all useful cases, though.  For example,
>>> it's impossible to make a list-item act like a table, or a table-cell
>>> to format its children as a flexbox.

...or for a flexbox item to act like a grid, or for a table caption to 
format its children like a flexbox, ...


As we know, an old draft of the css3-box spec split 'display' into 
'display-role' and 'display-model'.

>>> [Treating 'display' as a shorthand for two longhand properties] makes
>>> this all work simply, like its supposed to.

This split continues to make sense to me, though like Tab I prefer the 
names 'display-outside' and 'display-inside'.

> There are three areas of difference [from] the older draft:
>
> 1. Some names have been changed
> 2. All corner cases are specified (the older draft forgot to address a
> few places)
> 3. A slightly different mapping between some of the single-keywords
> forms and the double-keyword forms.  I don't think the differences
> here are significant.

>>>> The `display` property now accepts one or two tokens.  If there are
>>>> two tokens, the first is taken as the value for `display-outside`, and
>>>> the second is taken as the value for `display-inside`.  If there is
>>>> one token, it is equivalent to a two-token expression as described
>>>> below:
>>>>
>>>> Mapping of existing `display` values to the full shorthand
>>>> ----------------------------------------------------------


     'display'    |'display-outside'|'display-inside'|
-----------------|-----------------|----------------|
block            |      block      |     block      |
inline           |      inline     |     inline     |
inline-block     |      inline     |     block      |
table            |      block      |     table      |
inline-table     |      inline     |     table      |
table-*          |     table-*     |     block      | <- for discussion
table-cell       |    table-cell   |     block      |
table-caption    |  table-caption  |     block      |
run-in           |      run-in     |     inline     |
compact          |     compact     |     block      |
ruby             |      inline     |      ruby      |
ruby-base        |    ruby-base    |     inline     |
ruby-text        |    ruby-text    |     inline     |
ruby-base-group  | ruby-base-group |     block      | <- for discussion
ruby-text-group  | ruby-text-group |     block      | <- for discussion
flex             |      block      |      flex      |
inline-flex      |      inline     |      flex      |
grid             |      block      |      grid      |
inline-grid      |      inline     |      grid      |
-----------------|-----------------|----------------|
none             |       none      |     block      | <- for discussion
-----------------|-----------------|----------------|
list-item        |    list-item    |     block      | <- anomalies
inline-list-item | inline-list-item|     block      |
-----------------|-----------------|-----------------
align-box        |      block      |   align-box    | <- possible future
flex-item        |    flex-item    |     block      |     values
-----------------------------------------------------

Note that 'inline-block', 'inline-flex', 'inline-grid' and
'inline-table' are neither valid values of 'display-inside' nor of 
'display-outside', but they are valid values of the 'display' shorthand 
property.


The 'display-outside' property:

Initial value: inline
Values:
 none             -> generates no boxes
 inline           -> generates an inline-level principal box
 block            -> generates a block-level principal box
 list-item        -> generates a block-level principal box and
                               a marker box
 inline-list-item -> generates an inline-level principal box
                               and a marker box
 table-*          -> generates a principal table-* box
 table-cell       -> generates a cell-level principal box
 table-caption    -> generates a caption-level principal box
 run-in           -> generates either a block-level principal
                               box or an inline-level principal box
                               depending on context
 compact          -> generates either a block-level principal
                               box or a principal "margin box"
                               depending on context
 ruby-*           -> generates a principal ruby-* box
 flex-item        -> generates a flex-level principal box


The 'display-inside' property :

Initial value: inline
Values:

 inline     -> the principal box is an inline box
 block      -> the principal box is a block container box
 table      -> the principal box is a table wrapper box and
                         the element also generates a child table box
 ruby       -> the principal box is a ruby wrapper box
 flex       -> the principal box is a flex container box
 grid       -> the principal box generates grid slot boxes
 align-box  -> the principal box is an align box

For discussion of the 'display-inside' value of elements whose 
'display-outside' is 'table-*' and 'ruby-base-group' and 
'ruby-text-group', see below.

Note that values of 'display-inside' have no effect on replaced elements.


To prevent unworkable combinations of 'display-outside' and 
'display-inside', we need just three rules:

(1) If 'display-outside' doesn't compute to 'inline', 'run-in', or 
'ruby-*' then a specified value of 'inline' for 'display-inside' 
computes to 'block' (but see (3) below for 'ruby-base-group' and 
'ruby-text-group').

(2) If 'display-outside' computes to 'ruby-base' or 'ruby-text' then the 
computed value of 'display-inside' is 'inline'.  (This differs from 
Tab's proposal in which the computed value is 'block'; I'm not too 
familiar with the Ruby spec but 'inline' looks more correct.)

(3) If 'display-outside' computes to one of the 'ruby-base-group', 
'ruby-text-group' or 'table-*' values as per the list of values given 
above, the "inside behaviour" is glued to the "outside behaviour", so 
the 'display-inside' value is not important but needs to be "normalized" 
to something.  It's cleaner not to introduce all the 'table-*' etc 
values to 'display-inside' corresponding to the same values for 
'display-outside', so I suggest we choose some single other value.  Tab 
created new values 'table-inside' and 'ruby-inside' for this purpose... 
but perhaps we can just choose 'block'?

Note that one of the axioms of the solution design is that 
'display-outside' can influence the computed value of 'display-inside' 
but not vice versa.


Run-ins and compact boxes

In theory there need be no limitation on the 'display-inside' value of 
run-ins and compact boxes.  A table element, flex container elements or 
grid element that runs becomes an inline-table, inline-flex or 
inline-grid, for example.


display:none

If we are to handle display:none using one or other of the 
'display-outside' and 'display-inside' properties then 'display-outside' 
feels more natural.  When 'display-outside' is 'none' then it's then an 
open question which value the 'display-inside' property should compute 
to.  We could introduce a 'none' value of that property, but just as in 
rule 3 above it doesn't seem necessary; like Tab, I think that 'block' 
is as good a choice as any.  With this approach, we need another rule:

(4) If 'display-outside' computes to 'none' then the computed value of 
'display-inside' is 'block'.

However, the second of the two key issues raised on the original 
proposal to split the 'display' property concerned the ability of 
authors to toggle display (ie box generation) between none and not-none 
via script etc.  When 'none' is handled by one of 'display-outside' or 
'display-inside', it's necessary to first query and remember the current 
used value in order to know how to toggle between none and not-none.  An 
alternative approach is to introduce a third longhand value for 
'display'.  For want of a better name, let's assume this property is 
called 'display-role' (thus reusing a previously-proposed term for a 
different purpose).  The values would be defined as follows.

The 'display-role' property:

Initial value: normal
Values:
 none             -> boxes are generated as specified according
                             to the values of 'display-outside' and
                             'display-inside'
 normal           -> no boxes are generated for the element,
                             irrespective of the values of
                             'display-outside' and 'display-inside'

There would no longer be a need for a 'none' value of 'display-outside', 
"display:none" would be equivalent to
"display: inline inline none", and box generation would be toggled using 
'display-role' alone.  The initial value of 'display' would be "inline 
inline normal".


list items

It has been argued that the 'list-item' value of 'display' is anomalous. 
  For example:

>>>> There are an additional set of concepts
>>>> conflated into `display`, exemplified by the `list-item` value.
>>>> Should this be defined as some sort of magic that translates into
>>>> another set of properties that actually trigger ::marker generation,
>>>> or perhaps as a shorthand for a third `display-extras` property?

> On Wed, 21 Apr 2010 10:12:34 +0300 Mikko Rantalainen wrote:
>> I agree with your reasoning about list-item and the I think that it
>> requires something pretty special. I'm afraid that for backwards
>> compatibility and special side-effects required for current definition
>> of 'display: list-item', the only logical choice is a third display
>> property. I don't know if it should be called 'display-extras' or
>> 'display-side-effects' or 'display-magic' or 'display-compatibility' but
>> it would make following
>>  display: list-item;
>> a short hand for
>>  display-outside: block;
>>  display-inside: inline;
>>  display-extras: list-item; /* ::marker and counter magic */
>
> Well, display-inside is 'block' for list-item, but otherwise yes.

The argument is that a list item is really a block-level box with an 
extra marker box, and so 'display-outside' really ought to be block, and 
the list item nature ought to be captured in some other way.  Originally 
I thought the same thing, especially since it would seem natural to make 
a list item inline-level just by changing 'display-outside' from block 
to inline.

However, I've recently moved away from that position.  I don't think 
being a list item is orthogonal to 'display-outside'; I don't perceive a 
need for flex items or table captions that are also list items.  I think 
it's reasonable to have 'list-item' and 'inline-list-item' values of 
'display-outside' since list items are only really appropriate to block 
and inline formatting contexts.  In contrast, tables, block containers 
and flex containers are appropriate to many formatting contexts and so 
having inline-* versions of them is peculiar.  However, there is an 
editorial disadvantage to using 'display-outside', described below.

Still, if we pursue the argument that list items are orthogonal to 
'display-outside', we need to decide how to induce list item behaviour.
It's also been argued that it doesn't make sense to use 'display-inside' 
for this, since it's reasonable to want list items which are flex 
containers, grids or tables.  I'm on the fence about this.  I'm not 
convinced we do want such things, in which case 'display-inline' is a 
viable possibility.  (We'd only need one value, namely 'list-item'. This 
approach has the advantage of reusing a keyword that's already used for 
'display'.)

Another option is to introduce another longhand property for the 
'display' shorthand, as discussed by others above.  The name and values 
don't particularly interest me at this point, although I rather like 
'display-model':'list-item' which again reuses a property name that was 
proposed for a rather different purpose and which reuses an existing 
keyword.  (Presumably the other value would be 'normal' or suchlike.) We 
would need to introduce another rule:

(5) If 'display-outside' doesn't compute to 'inline' or 'block' then a 
specified value of 'list-item' for 'display-model' (or 'marker' for 
'display-extras' or whatever) computes to 'normal'.

Yet another option is to not use a display longhand property at all, and 
instead use the existence of a non-empty ::marker pseudo-element.  (Then 
"list-style-type:none" would create a marker with the equivalent of 
visibility:hidden rather than "display:none"; there's no rendering 
difference between the two at the moment, but there might be in future: 
eg see [1].)  Whilst this seems rather natural, it's a rather more 
complex model (and the 'display' shorthand would be influenced by things 
other than its own longhand properties, which is possibly unacceptable). 
  It also sounds harder to implement, and there is an editorial 
disadvantage, described below.


Consequences

One editorial thing to bear in mind when thinking about all this is that 
in various places in the spec we require that boxes be "blockified". For 
example, when boxes are floated or absolutely positioned, inline-* boxes 
become * boxes as per the table in CSS21 9.7 (extended in the flexbox 
spec to handle inline-flex, and tentatively in the lists spec to handle 
the proposed inline-list-item value).  It would be great to be able to 
model that as a simple switch of 'display-inside' from 'inline' to 
'block', so that we didn't need that table any more.  Regarding list 
items, this switch is compatible with using 'display-inline' or 
'display-model' etc to handle list items, but is incompatible with using 
'display-outside' or an approach involving sniffing ::marker.


[1] http://lists.w3.org/Archives/Public/www-style/2012Oct/0419.html

Cheers,
Anton Prowse
http://dev.moonhenge.net

Received on Saturday, 20 October 2012 00:06:41 UTC