Proposal for "Cascading Attribute Sheets" - like CSS, but for attributes!

I recently participated in an internal thread at Google where it was
proposed to move a (webkit-specific) feature from an attribute to a
CSS property, because applying it via a property is *much* more
convenient.

Similarly, some of the a11y folks have recently been talking about
applying aria-* attributes via CSS, again, because it's just so much
more convenient.

I think there are probably a lot of examples of this, where something
definitely belongs in an attribute, but it would just be so *nice* to
set it with something like CSS, where you declare it once and
everything just works.  For example, inline event handlers!

Given all this, I have a proposal for a method of doing that.  It's
very similar to CSS and gives most of the benefits, but should avoid
some pitfalls that have sunk similar proposals in the past.

Cascading Attribute Sheets
=======================

CAS is a language using the same basic syntax of CSS, meant for easily
applying attributes to an HTML page.

To use it, include a CAS file using <script type="text/cas">.  (I
chose <script> over <style>, even though it resembles CSS, because it
acts more like a script - it's run once, no dynamic mutations to the
file, etc.)  CAS scripts are automatically async.

The basic grammar of CAS is identical to CSS.  Your attribute sheet
contains rules, composed of selectors, curly braces, and declarations,
like so:

video {
  preload: metadata;
}
#content video {
  preload: auto;
}

In the place where CSS normally has a property name, CAS allows any
attribute name.  In the value, CAS allows any single token, such as a
number, identifier, or string.  If the value is a string, it's used as
the attribute's value.  Otherwise, the value is serialized using CSS
serialization rules, and that is used as the attribute's value.

There are three special values you may use instead to set the value:
!on sets an attribute to its name (shorthand for boolean attributes),
!off removes an attribute (boolean or not), and !initial does nothing
(it's used to cancel any changes that other, less specific, rules may
be attempting to make, and is the initial value of all properties).

CAS files are *not* as dynamic as CSS.  They do not respond to
arbitrary document changes.  (They *can't*, otherwise you have
dependency cycles with an attribute selector rule removing the
attribute, etc.)  My thought right now is that your CAS is only
applied to elements when they are inserted into the DOM (this also
applies to any parser-created elements in the page).  This allows us
to keep information tracking to a bare minimum - we don't need to
track what attributes came from CAS vs the markup or setAttribute()
calls, we don't need to define precedence between CAS and other
sources, and we don't need to do any of the fancy coalescing and
whatnot that CSS changes require.  Semantically, a CAS file should be
exactly equivalent to a <script> that does
"document.querySelectorAll(<selector>).forEach(<do attribute
mutations>);", plus a mutation observer that reruns the mutations on
any nodes added to the document.  It's just a much more convenient way
to express these.

(Slight weirdness here - a CAS file can reset its own @src attribute
to load *another* CAS file.  A <script> can do the same, though.
Acceptable or not?)

I think we should allow the CSS Conditional rules as well
<http://dev.w3.org/csswg/css3-conditional/> - at least Media Queries,
but @document seems useful as well, and @supports may even by
justifiable (it would need some definition work to make it usable for
CAS, though).  Again, these aren't responsive to live changes, like MQ
are in CSS, but they let you respond to the initial document condition
and apply attributes accordingly.



Thoughts?  I tried to make this as simple as possible while still
being useful, so that it's easy to implement and to understand.
Hopefully I succeeded!

~TJ

Received on Tuesday, 21 August 2012 18:17:58 UTC