W3C home > Mailing lists > Public > public-houdini@w3.org > August 2015

Re: Typed CSSOM

From: Tab Atkins Jr. <jackalmage@gmail.com>
Date: Mon, 10 Aug 2015 16:28:22 -0700
Message-ID: <CAAWBYDCt1dX6nC2zY3vUMKMPNS6btjALd77EzY4RaSdpz+y2Dg@mail.gmail.com>
To: Shane Stephens <shans@google.com>
Cc: "public-houdini@w3.org" <public-houdini@w3.org>
Note to others who are familiar with my proposal for a CSSOM based on
Value Objects <http://www.xanthir.com/b4UD0> - these two efforts are
not hostile to each other.  Value Objects, while still on the roadmap
for ES, are still some ways away, at least a few years.  CSSOM's
stringly-typed OM, tho, continues to be terrible, and is increasingly
a performance bottleneck.  Creating a reasonably simple stop-gap
object-based solution lets off some of the pressure and allows us to
wait for TC39 to do Value Objects properly.  We just need to make sure
we don't squat on good names we'll want to use in the future.

On Sun, Aug 9, 2015 at 6:05 PM, Shane Stephens <shans@google.com> wrote:
> Hi houdiños,
>
> Being able to describe the type of CSS values has been an important part of
> the custom properties proposals that we've talked about in this group.
>
> I'd like to explore whether we can extend this notion to JavaScript - i.e.
> provide typed JS objects rather than strings when accessing custom CSS
> values. I'd also like to see whether we can retrofit the existing CSSOM with
> this idea. These ideas were discussed in the January f2f but I don't think
> we've talked about it much on list since.
>
> My reasons are basically those provided in Sydney: converting CSS strings to
> values is tedious, error-prone, and a source of performance problems.
>
> So to get the ball rolling - here's a partial proposal that only covers
> numbers and lengths. Obviously we'll eventually want to expose the rest of
> the types too, but we need to start somewhere :) Bikeshed away!
>
> (1) Access to typed CSSOM
> A new OM can't just start to replace existing CSSOM functionality with typed
> versions because this will break lots of things. Instead we should create a
> new interface, StylePropertyMap, that is accessible alongside the old
> methods:
>
> interface StyleValue {
>   boolean isInitial();
>   boolean isInherit();
>   boolean isDefault();
>   boolean isUnset();
>   attribute DOMString cssString;
> }

Is there any particular reason these are method, rather than
attributes?  Or perhaps a single enum containing the global keyword
that it has the value of (or null if it's not one of the global
keywords)?

> interface StylePropertyMap {
>   StyleValue get(DOMString property);
>   sequence<StyleValue> getAll(DOMString property);
>   void set(DOMString property, StyleValue or sequence<StyleValue> or
> DOMString value);
>   void append(DOMString property, StyleValue or sequence<StyleValue> or
> DOMString value);
>   iterable<DOMString, StyleValue or DOMString>;
>   stringifier;
> };

+1 to a Map-like interface, as it means we can use custom properties
alongside normal ones.

This should presumably be an actual MapLike<DOMString, StyleValue>, right?

(For others, note the MultiMap interface - get/getAll and set/append.
This is for list-valued properties, so you can interact with it as a
list or as a single value.  This MultiMap interface is already used by
the URL spec for URLSearchParams.)

> interface ComputedStylePropertyMap : StylePropertyMap {
> };
>
> interface SpecifiedStylePropertyMap : StylePropertyMap {
>   boolean has(DOMString property);
>   sequence<DOMString> getProperties();
> }

Why does SpecifiedStyle have has() but not ComputedStyle?  Yes,
elements have computed styles for all properties, but that doesn't
seem sufficiently worthwhile to break the MapLike interface.

> partial interface Element {
>   readonly attribute SpecifiedStylePropertyMap styleMap;
> };
>
> partial interface Document {
>   ComputedStylePropertyMap getComputedStyleMap(Element element);
> };
>
> partial interface CSSStyleRule {
>   readonly attribute SpecifiedStylePropertyMap styleMap;
> };
>
> (2) Numbers
>
> Number properties like z-index or opacity should have a very simple
> wrapping:
>
> interface NumberValue : StyleValue {
>   double value;
> }
>
> An open question: where and when does validation happen? What happens if I
> try to set an out-of-range number to opacity? Will this be consistent across
> all properties?
>
> (3) Lengths
>
> Usually, lengths are simple single-unit values (e.g. 50px or 30em). However,
> it is possible for calc values to be used instead. The following API tries
> to capture common use-cases without allowing web devs to accidentally write
> code that will break when calc values are fed in:
>
> enum LengthType {
>   "px", "percent", "em", // ...
> }
>
> interface Length : StyleValue {
>   Length add(Length value); // can throw
>   Length subtract(Length value); // can throw
>   Length multiply(double value); // can throw
>   Length divide(double value); // can throw
>   static Length parse(DOMString cssString);
>   static Length fromValue(double value, LengthType type);
>   static Length fromDictionary({...});
> }
>
> [Constructor(DOMString cssString),
>  Constructor(Length),
>  Constructor({
>   // … all the props.
> })]
> interface CalcLength : Length {
>   attribute double? px;
>   attribute double? percent;
>   attribute double? em;
>   attribute double? ex;
>   attribute double? ch;
>   attribute double? rem;
>   attribute double? vw;
>   attribute double? vh;
>   attribute double? vmin;
>   attribute double? vmax;
>   attribute double? cm;
>   attribute double? mm;
>   attribute double? q;
>   attribute double? in;
>   attribute double? pc;
>   attribute double? pt;
> }
>
> // lengths that are *just* keywords don't become SimpleLengths or
> CalcLengths.
> interface SimpleLength : Length {
>   attribute double value;
>   readonly attribute LengthType type;
> }
>
> In particular, lengths can be manipulated using the Length API without
> knowing whether they're simple or calc expressions. One downside is that
> developers need to check whether a Length is a SimpleLength before
> extracting the Length value - but then again, in practice they should be
> doing this anyway.
>
> (4) Everything else
>
> I realize that there are lots of details missing! Hopefully there's enough
> here to kick off a conversation, though - is this a direction we want to go
> in, and does the API sketched out above look like it could work?

Yes!

~TJ
Received on Monday, 10 August 2015 23:29:09 UTC

This archive was generated by hypermail 2.3.1 : Monday, 10 August 2015 23:29:09 UTC