List-valued properties

A way to solve the lack of control of properties taking lists as value

1.    Introduction

A large amount of CSS properties take a list of arguments as value. However, when multiple rules match an element, it’s not possible to isolate these rules from each other, creating conflicts.

Sample 1.

This sample shows how the lack of independence of list-valued properties hinders web authors.

CSS CODE

a { color: blue; transition: all 0.2s; }
a:hover { color: red; }

.zoom-on-hover {
    transition: transform 0.5s;
    transform: scale(1.0);
}

.zoom-on-hover:hover {
    transform: scale(1.1);
}

.upside-down {
    transform: rotate(180deg);
}

HTML CODE

<a class="upside-down zoom-on-hover">...</a>

It’s likely that the behavior of this code is not the one the author expects. The transition from the foreground color from blue to red will be instantaneous (not 0.2 seconds), and the element will perform a 180° rotation on hover because the scale transformation replaces the rotation one.

This module provides a way for CSS authors to create independent style rules by enable the addition ad modification of elements in a list in a non-destructive way.

Sample 2.

This sample shows how this module solves the previous problem.

CSS CODE

a { color: blue; transition: all 0.2s; }
a:hover { color: red; }

.zoom-on-hover {           
    transition[hz]: transform[hz] 0.5s;
    transform[hz]: scale(1.0);
}

.zoom-on-hover:hover {
    transform[hz]: scale(1.1);
}

.upside-down {
    transform[]: rotate(180deg);
}

HTML CODE

<a class="upside-down zoom-on-hover">...</a>

Now, the transition from the foreground color from blue to red will correctly take 0.2 seconds, and the element will be kept upside-down while zooming-in on hover.

2.    Definitions

2.1.           The list-valued properties

A list-valued property is a CSS property whose value consist in a list of items of similar syntax, eventually separated by separators like commas or spaces.

Sample 3.                                 

The “transition” property is a list-valued property because it can contain an infinite list of items whose accepted syntax is identical.

CSS CODE

#element {
    transition:
        background-color 4s,
        border-color 4.5s,
        color 5s;
}

2.2.           The list-item type

The <list-item> type of a list-valued property is an automatically-generated type which matches any <value> production that’s accepted as a list item of the corresponding list-valued property.

Sample 4.

The “transition-duration” property’s list item type is: <time>

2.3.           The list separator

The list separator of a list-valued property is a list of token used to separate two items of the corresponding list-valued property.

Sample 5.

The “transition-duration” property’s list separator is: the comma glyph (“,”).

2.4.           The list-push declarations

A list-push declaration is a property declaration whose name is composed of an identifier directly followed by a list of tokens starting with an opening bracket “[” and ending with a closing bracket “]”, called the follow-up token list.

The declaration index of a list-push declaration is defined as a view of the follow-up token list from which the opening and closing brackets are eliminated. This token list can possibly be empty (which means it doesn’t contains any token).

Sample 6.

The “transition[…]: scale(1.1)” property declaration is a list-push declaration, as soon as we replace the “…” token by a valid list of token.

An identified list-push declaration is a list-push declaration whose declaration index is composed of a single token matching the IDENT production.

Sample 7.

The “transition[hz]: scale(1.1)” property declaration is a list-push declaration.

An indexed list-push declaration is a list-push declaration whose declaration index is composed of a single token matching the NUMBER production.

Sample 8.

The “transition[0]: scale(1.1)” property declaration is an indexed list-push declaration.

An anonymous list-push declaration is a list-push declaration whose declaration index is empty.

Sample 9.

The “transition[]: scale(1.1)” property declaration is an anonymous list-push declaration.

2.5.           Changes to the CSS Syntax

2.5.1.      Changes to the syntax of a property declaration

A property is an identifier, optionally followed by an identifier enclosed into brackets or a pair of bracket.

This specification therefore updates the grammar of a CSS property:

property
  : IDENT [ '[' (IDENT|NUMBER)? ']' ]? S*
  ;

When the brackets enclose no identifier, the user agent must generate a unique identifier and behave as if such an identifier had been found there instead. The exact mechanism used to create such a unique identifier is left at the user agent’s discretion. Automatically generated identifier should be available through the CSS OM as if it was specified by the user.

2.5.2.      Changes to the set of valid property declarations

This specification defines as valid any property declaration whose name is an identifier representing a known list-valued property followed by an identifier or a number enclosed into brackets, and whose value is valid if it matches the [ <list-item> [SEPARATOR <list-item>]* ] type.

The properties defined in the previous paragraph are not inherited and do not have any particular effect on the page, except entering into consideration in the algorithm defined in 2.6.1.

2.6.           Changes to CSS 2.1

2.6.1.      The computed value of a list-valued property

The steps required to compute the value of a list-valued property is modified by this specification in order to take in consideration the specified value of list-push declarations whose target property is the list-valued property itself.

Anywhere a reference is made to the specified value of list-valued property, the reference must be replaced to the value resulting from this algorithm:

1.       Let “items” be an array containing the list items contained in the specified value of the property (in case the specified value is none, let “items” be an empty array).

2.       For each push-declaration whose target property is the current property, append the value of the push-declaration property to the “items” array. The order in which the push-declarations should be sorted in this algorithm is defined in section 2.7 (the items with higher priority are added in the last positions of the array).

3.       Join all the elements of the “items” array using the list separator as separator between items and return that value (in case “items” in an empty, return “none” instead).

Sample 10.

Given the code the code of the second sample of this specification, the computed value of the transform property on the A element would be ‘scale(1.0) rotate(180deg)’.

2.7.           Priority order of push-declarations

Push-declarations order is defined as follow:

An indexed push-declaration has a higher priority than an identified push-declaration.

An indexed push-declaration has a higher priority than another one if its index is an higher number than the index of the second push-declaration.

An identified push-declaration has a higher priority than another one if the lowest-priority CSS rule applicable to the current element and where it’s defined has a lower priority than the one of the second push-declaration.

Sample 11.

This sample clarify the order of CSS declarations. Given the following code:

CSS CODE

* { property[0]: index0; property[1]: index1; }

my-tag { property[a]: tag; }

.my-class { property[b]: class; property: base; }

#my-id { property[c]: id; property[a]: tag-id; }

HTML CODE

<my-tag id="my-id" class="my-class">...</a>

The computed value of the property would be “base, tag-id, class, id, index0, index1” if it uses a comma as an item separator.

3.    Conclusion

This specification aims to solve one of the most important issues of CSS in a relatively simple way. It certainly does introduce new concepts but the newly introduced concepts should not be very difficult to implement.

Meanwhile, the benefits for CSS authors is clearly visible.