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