[css-flexbox] Fixing a mistake with flex-grow betwen 0 and 1

I've been thinking about this lately, and I think I made a mistake in
how I specified the behavior of 'flex-grow' values greater than 0 but
less than 1.  The current behavior isn't compatible with animation,
and a small fix would also enable a useful behavior for authors.  I
believe this mistake can be safely corrected with insignificant, if
any, compat problems.

# Problem

Right now, the spec treats flex-grow values between 0 and 1 the same
as values 1+ - they get normalized into a share of the free space, and
then space gets distributed.  This means that *any* non-zero value,
even something as tiny as .0001, will cause the element to flex and
fill its container.

This is troublesome when you try to animate between 0 and a non-zero
value, because a lone flexible element stays container-filling until
the final moment when its flex-grow reaches 0, at which point it's
suddenly completely inflexible and flips to its normal size.  Because
this isn't really "transitioning", I even explicitly disallow
animating between 0 and non-zero.

This is annoying, because if there are *two* flexible items on the
line, then transitioning one of them to 0 *does* work properly - the
item gradually shrinks to its hypothetical size, as expected.  This
problem only occurs when there's only a single flexible item on the
line, which is annoying and confusing to authors.

As well, this is discontinuous behavior that exposes UA rounding
behavior, which is something we try to avoid whenever possible.

# Suggested Solution

Lightly reinterpret flex to be literally a fraction of the free space,
with the usual "1 = 100%" conversion.  If the sum of the flexes on a
flex line is greater than 1, normalize appropriately, but if not,
leave the numbers alone.

In specific edits, this would just be a change in the
<http://dev.w3.org/csswg/css-flexbox/#resolve-flexible-lengths>
section - insert a step 2.5 that checks if the free space is positive
and the sum of the flex-grows are <1, and if so, sets all the flex
items to their hypothetical main size + the corresponding fraction of
the free space, then exits the algorithm.

# Benefits

This change would allow 'flex' to be animated between 0 and non-zero
values in all cases.  If there's a single flexible item, as its
flex-grow drops below 1, it'll start using less than all of the free
space, gradually shrinking to its hypothetical main size, exactly what
you get from an inflexible item.  This gives us precisely what we
want, and eliminates the discontinuity.

This has real worth.  Without this, you can't easily do flexible
accordions, where when everything is closed they pack toward
flex-start, but when a section is expanded it fills the available
space.

Another unexpected benefit of this is that it allows a reasonable,
though not perfect, solution to the "items on the last flex-line are
way too big" problem, where the last line in a multi-line flexbox
sometimes has 1 or 2 items which stretch to fill the entire line,
ending up ridiculously larger than all the other items in the flexbox.
 This can be fixed by, rather than specifying everything with a flex
of "1", instead specifying a flex of the maximum fraction of the line
you want something to fill, approximately.  For example, if your
flexbox will typically have 4-6 items per line, just set each to
"flex: .25", and the ones on the final line will be reasonably sized,
while the rest of the lines are unaffected (the sum of flexes will be
1 to 1.5, which means they get normalized as usual and soak up all the
free space).  This requires you to guess how many items will fit on a
line, which isn't ideal, but is often good enough.  I plan to address
this more thoroughly in Flexbox 2.

# Compat Risk

I expect the compat risk for this change to be insignificant, if not
nonexistent.

This only affects items with a flex value >0 and <1, which should be
rare in the first place - the spec only uses integer values, every
tutorial I've seen avoids values in that range, and I've never seen
real code try to use values in that range either.

Further, it only affects these items if the sum of the flexes on the
line is less than 1.  Two items both with "flex: .5;" will continue to
work as normal, for example.  I suspect anything hitting this case to
be *very* rare.

I could use a use-counter to try and track this stuff, but I fear that
a 12- to 18-week delay for full results might result in a growth of
the usage of this case, as unprefixed Flexbox impls move into the
public web.  I believe it's safe to make this change as long as we do
so quickly.

Thoughts?

~TJ

Received on Monday, 7 October 2013 23:11:24 UTC