- From: Anne van Kesteren <annevk@opera.com>
- Date: Tue, 06 Apr 2010 12:28:29 +0200
- To: "www-style@w3.org" <www-style@w3.org>
On Thu, 25 Mar 2010 16:10:58 +0100, Anne van Kesteren <annevk@opera.com> wrote: > [...] > > Thoughts on this are very much welcome. Some brainstorming during the > F2F might also be a good use of time if we limit the amount of time :-) This is a fairly long email describing a value API proposal for CSS. None of this made it into CSSOM yet. Hopefully people can make the time to review this and point out mistakes or ideas for improvement. Feel free to ask any questions. I'll do my best to reply timely. TERMINOLOGY The terminology is abbreviated here to make the proposal more easily to skim through. value -- the value of a property margin: 20px 20px the value is 20px 20px component value -- one of the components of a value margin: 20px 20px the component values are 20px and 20px color: <color> the component value is <color> map value -- component values are independent but grouped in a single value border:2px solid red the value of this property is a map value list value -- component values are comma-separated cursor:url(big), pointer; the value of this property is a list value space-list value -- component values are space-separated, possibly with a maximum length counter-increment: foo 1 bar 2 the value of this property is a space-list value of map values (or a single ident) margin:20px 20px the value of this property is a space-list value of map values (but limited in length) INTERFACES All values implement a single base interface, CSSValue. This interface has all the various value types exposed as string constants and the value it currently exposes can be read by getting its type attribute. It uses string constants to make extensibility easier and uses constants to make optimizations easier. It also has a cssText attribute for parsing from string and serializing purposes. Map values implement an interface (CSSMapValue) with a single attribute, named m for now, to access a map object (CSSMap). The map object has a named getter and a way to iterate over the items in a map. (I.e. there's length, getter key(int), and getter item(string).) The map object is not directly implemented on the value object to prevent potential future clashes between new members of the value object and existing keys on the map object. (An alternative design for CSSMap might be to just expose string[] keys and getter item(string) as to not reveal ordering and allow more optimizations.) List values and space-list values behave similarly to map values (CSSListValue, CSSList), except their attribute is named l, for now. Interface-wise they are identical but to distinguish them the type attribute will return a different value. List and space-list values can also be mutated. I.e. new items can be added and existing items can be removed. Components have specific interfaces depending on the type of component. A value implements all the various component interfaces it supports. E.g. 'width' takes both <length> and <percentage> so value objects for it will support both the length and percentage component interfaces. VALUE ATTRIBUTE I also want to give CSSValue a value attribute. This attribute is of type any. I.e. it can return anything and be set to anything. What you can set it to and what you get out of it depends on the type attribute of CSSValue. In some cases this will be the same as mutating a specific member, such as px on <length>, but for some types this makes sense as the only accessor, e.g. <string> and <ident>. ADDING LIST ITEMS Currently everything with appending in the CSSOM is done from a string. I'm inclined to follow that model for now. We could construct a way to create all these various kind of objects, but that would be tricky and not very convenient for authors. We could also have a method that appends an object of the right type at a certain position and then let authors modify it after it is appended. I'm open to suggestions. EXAMPLES 'color': Returns an object that implements CSSValue and the interface for <color>. ele.style.color.red++ 'margin': Returns an object that implements CSSValue and CSSListValue. Its associated CSSList has up to four items, each of which implements CSSValue and the interfaces for <length>, <percentage>, and <ident>. ele.style.margin.l[0].px++ // increases the px value of the first item 'border-top': Returns an object that implements CSSValue and CSSMapValue. Its associated CSSMap has three items, named "width", "style", and "color". Each of the items implement CSSValue and the appropriate component value interface. ele.style.borderTop.m.style.value = "double" 'border-image': Returns an object that implements CSSValue and CSSMapValue. Its associated CSSMap has five items, named "source", "slice", "width", "outset", and "repeat". The "source" item implements CSSValue, <image>, and <ident>. "slice" implements CSSValue and CSSMapValue. Its associated CSSMap consists of two items, one a CSSListValue and one an <ident>... 'border-image' is definitely doable with generics (just follow the patterns outlined so far), but for complex properties like these we might actually want to simplify matters and offer a more high-level API. E.g. for "slice" a CSSValue/CSSListValue that also has the ability to easily set the 'fill' keyword rather than having to use a CSSMapValue/CSSMap around them. EXTENSIBILITY Or, what we should do when property values change. Property values, for better or worse, change. E.g. 'background-image' used to be a simple keyword or a <uri>, now it is a comma-separated list of keywords or <uri> (let alone <uri> -> <image>). If we imagine that this value API was introduced before this property value change the 'background-image' value would be represented by an object that implements CSSValue, <uri>, and <ident>. (Likely <ident> is nothing apart from a constant on CSSValue.) Now the change is introduced so we need to modify the API. Basically the idea is that besides CSSValue, <uri>, and <ident>, it also implements CSSListValue. To not break usage of the old API modifying <uri>/<ident> would modify the first item in the list. (Alternatively they could empty the list on setting and return nothing if more than one item is in the list, I suppose.) CHANGES SINCE INITIAL DISCUSSION Initially we discussed 'margin' and properties like it to be represented by a map. However, given the way one or more component values work for 'margin' it behaves very much like a list with a maximum length. It seems better therefore to use the list interface for it. -- Anne van Kesteren http://annevankesteren.nl/
Received on Tuesday, 6 April 2010 10:29:19 UTC