Re: [css3-images] Linear gradients feedback

On Aug 29, 2010, at 9:56 am, Tab Atkins Jr. wrote:

> tl;dr: I've added instructions for serializing gradients.  I want to
> add an ending-point argument to linear-gradient(), and change the
> color transitions to happen in pre-multiplied rgba space.
> 
> Simon and I recently had a twitter conversation about issues keeping
> him from wanting to implement the draft gradient syntax in Webkit.
> 
> One of the issues was that gradients didn't have a defined
> serialization.  I've now defined this in the draft.
> 
> Another issue was that linear-gradient() had too much "magic".  There
> is explicitly different behavior for all four combinations of
> specified and omitted values in the first argument.  He'd prefer that
> the syntax have more regular behavior, such that a missing value is
> just filled in with a default value.  He sent an email to this effect
> back during the original syntax discussions:
> http://lists.w3.org/Archives/Public/www-style/2009Nov/0050.html
> 
> After thinking it through, I believe his complaint is valid, though I
> strongly disagree with the fix he proposes in that email.  Instead,
> I'd like to add an additional argument to the linear-gradient() syntax
> - another <bg-position> given as the second argument to the function,
> for specifying an ending-point for the gradient.

Here's the current draft, for those following:

<http://dev.w3.org/csswg/css3-images/#linear-gradients>

It currently has:

  linear-gradient([<bg-position> || <angle>,]? <color-stop>, <color-stop>[, <color-stop>]*);

and you're suggesting (I think; my grammar is weak):

  linear-gradient([<bg-position> <bg-position>? || <angle>,]? <color-stop>, <color-stop>[, <color-stop>]*);


> Let me explain why I think this addresses his complaint adequately.
> 
> Right now, there are four cases that can be expressed by the first
> argument to linear-gradient() - the <bg-position> can be present or
> absent, and the <angle> can be present or absent.  These are all
> handled somewhat specially, in what appears to be an ad hoc manner.
> Really, though, the cases are very consistent and in-line with the
> "unspecified values just take a default" ethos that Simon wants;
> however, this isn't apparent with only four cases to look at.  Adding
> an ending-point argument makes this much more apparent, and also
> addresses several use-cases.
> 
> In his email, Simon notes that it appears that linear-gradient() is
> actually expressing two or three different syntaxes.  He's right -
> there are two substantially different behaviors here, keyed by the
> presence or absence of the angle.
> 
> If the angle is absent, the gradient-line simply goes from one point
> to another point.  If both points are specified, you're done.  If the
> ending-point is missing, it defaults to rotating the starting-point
> around the center of the box.  If the starting-point is missing, it
> defaults to "top center".
> 
> If the angle is present, the gradient-line starts at the
> starting-point, extends outward at the angle specified, and ends when
> a line drawn perpendicular to the gradient-line would intersect with
> the ending-point.  If both points are specified, you're done.

You'd have to describe what happens when the angle is perpendicular to the line
between the endpoints.

> If the
> ending-point is missing, it defaults to the corner of the box in the
> direction of the angle.  If the starting-point is missing, it default
> to the corner of the box in the opposite direction of the angle.

That seems inconsistent (aka "magic") to me, and a surprising difference
from the no-angle case. In the no-angle case, the end point is obtained by mirroring
around the center of the box. In this case, a "extend to the edge of the box"
method is used.

I think there are several reasons to try to avoid the special behaviors
that the current spec has.

With most compound or shorthand CSS properties, you can omit values
that have default values without change in behavior. This is not
the case with gradients. For example:

  linear-gradient(top left, ...)

is not equivalent to:

  linear-gradient(top left, 0, ...)

Similarly 

  linear-gradient(200deg, ...)

is not equivalent to

  linear-gradient(top 200deg, ...)

This violates the principle of least surprise for authors.

A second reason to try to avoid these branches in the gradient algorithm is because
it makes interpolating between them for animation harder. Without a canonical
form, you can't map all inputs to a common representation for interpolation.

> In either case, missing points have consistent, well-defined default
> values.  The defaults are just completely different between the two
> cases.  I believe this is necessary to make the two cases actually be
> useful.  Simon's proposal for always defaulting the starting-point to
> the top-left is insufficient for either case.  If the angle is absent,
> it makes the default behavior a diagonal gradient, when the most
> common gradients on the web and elsewhere are vertical and horizontal.
> If the angle is present, it makes the only useful angle values be
> between 0deg and -90deg - 0deg to 90deg and 180deg to 270deg have
> unintuitive results, while 90deg to 180deg appears to have no effect
> at all in most cases (it draws the gradient completely outside of the
> box unless there are color-stops with positions lower than 0%).

I was trying to think along the lines of considering the two
<bg-positions> not as endpoints of the gradient specifically, but
as the corners of a virtual box that bounds the gradient (following
the "extend to the edges" logic that exists in the spec). Unfortunately
this doesn't solve the problem of having sensible defaults. The gradient

    linear-gradient(top 45deg, ...)

would no longer extend from the top-left to bottom-right corners.

    linear-gradient(top left 45deg, ...)

would, however.

> I believe that the current level of "smarts" used in differentiating
> the defaults of the two cases is the minimum necessary to make the
> syntax useful.  The benefits of simplifying it further would be
> strongly outweighed by the downsides of the bad defaults, in my
> opinion.

Fundamentally, there are two ways to describe a linear gradient; either as
two end points, or a start point (or center point), angle, and length.

That argues for two forms:

  linear-gradient(<bg-position> <bg-position>, ...);

and

  linear-gradient(<bg-position> <angle> <length>, ...);

This latter form would be more useful if <bg-position> specifies the center-point,
because then any angle works. A length of 100% could be specified to that which
has the gradient extend to fill the box. The defaults are a little tricky; you'd
want the <bg-position> to default to center, and <angle> to 90deg for a vertical gradient.

Simon

Received on Sunday, 29 August 2010 22:15:25 UTC