Constructable Stylesheet Objects

A Collection of Interesting Ideas,

This version:
http://tabatkins.github.io/specs/construct-stylesheets/
Issue Tracking:
Inline In Spec
Editor:
Tab Atkins Jr. (Google)
This Document Is Obsolete and Has Been Replaced

This specification is obsolete and has been replaced by the document at https://w3c.github.io/csswg-drafts/cssom/#dom-cssstylesheet-cssstylesheet. Do not attempt to implement this specification. Do not refer to this specification except as a historical artifact.


Abstract

This draft defines additions to CSSOM to make StyleSheet objects directly constructable, along with methods and APIs to make it easier to deal with stylesheets in the context of custom elements and similar. It also defines constructors for CSSRule objects.

1. Adding Stylesheets In Script

[Constructor(DOMString text, optional CSSStyleSheetInit options)]
partial interface CSSStyleSheet {
};

dictionary CSSStyleSheetInit {
  (MediaList or DOMString) media = "";
  DOMString title = "";
  boolean alternate = false;
  boolean disabled = false;
};

interface TreeScope {
  attribute StyleSheetList moreStyleSheets;
};
Document implements TreeScope;
ShadowRoot implements TreeScope;
CSSStyleSheet(text, options)
When called, execute these steps:
  1. Construct a new CSSStyleSheet object sheet, with location set to null, no parent CSS style sheet, no owner node, no owner CSS rule, and a title set to the title attribute of options. Set sheet’s origin-clean flag.

  2. If the media attribute of options is a string, create a MediaList object from the string and assign it as sheet’s media. Otherwise, assign the value of the attribute as sheet’s media.

  3. If the alternate attribute of options is true, set sheet’s alternate flag.

  4. If the disabled attribute of options is true, set sheet’s disabled flag.

  5. Parse a stylesheet from text. If it returned a list of rules, assign the list as sheet’s CSS rules; otherwise, set sheet’s CSS rules to an empty list.

  6. Return sheet.

moreStyleSheets, of type StyleSheetList
Style sheets assigned to this attribute are part of the document CSS style sheets. They are ordered after the stylesheets in styleSheets.

Better name.

Or do we want to include manually-added sheets in document.styleSheets, similar to how document.fonts mixes OM-created and manually-created fonts? Big difference is that ordering matters here, which makes dealing with the invariants much more annoying. (What happens if you manually add a sheet between two <link> sheets, then insert another <link> in the document between them? Does it go before or after your manually-added one? Or do we just make it illegal to manually add a sheet before an automatic sheet?)

2. Applying Styles In All Contexts

One of the major "misuses" of the >>> combinator is to apply "default styles" to a component wherever it lives in the tree, no matter how deeply nested it is inside of components. The use-case for this is to provide the equivalent of the user-agent stylesheet, but for custom elements (thus, the styles by necessity must come from the author).

Unfortunately, this is extremely slow, and there’s not a whole lot that can be done about that—>>> combinators are slow by their nature. Note, though, that the UA and user stylesheets automatically apply in all shadows; it’s only the author stylesheet that is limited to the context it’s created in.

(At this point, one might point out that this is already handled by just setting up styles during element construction. This doesn’t help for cases where a component is purposely authored to be styled by the end-user; forcing users of components to go muck around in their components' source code is a non-starter.)

One possible solution here is to add another origin, the "author default" origin, which sits between "user" and "author", and applies in all shadow roots automatically. We can add a list for these stylesheets, akin to document.styleSheets, and allow you to insert constructed stylesheets into it. Or maybe add an .origin attribute to CSSStyleSheet, defaulting to "author"?

Maybe it only applies to the context you’re in and descendant contexts? Need to investigate; probably bad to let a component apply automatic styles to the outer page via this mechanism.

Conformance

Conformance requirements are expressed with a combination of descriptive assertions and RFC 2119 terminology. The key words “MUST”, “MUST NOT”, “REQUIRED”, “SHALL”, “SHALL NOT”, “SHOULD”, “SHOULD NOT”, “RECOMMENDED”, “MAY”, and “OPTIONAL” in the normative parts of this document are to be interpreted as described in RFC 2119. However, for readability, these words do not appear in all uppercase letters in this specification.

All of the text of this specification is normative except sections explicitly marked as non-normative, examples, and notes. [RFC2119]

Examples in this specification are introduced with the words “for example” or are set apart from the normative text with class="example", like this:

This is an example of an informative example.

Informative notes begin with the word “Note” and are set apart from the normative text with class="note", like this:

Note, this is an informative note.

Index

Terms defined by this specification

Terms defined by reference

References

Normative References

[CSS-SYNTAX-3]
Tab Atkins Jr.; Simon Sapin. CSS Syntax Module Level 3. URL: https://drafts.csswg.org/css-syntax/
[CSSOM-1]
Daniel Glazman; Emilio Cobos Álvarez. CSS Object Model (CSSOM). URL: https://drafts.csswg.org/cssom/
[DOM]
Anne van Kesteren. DOM Standard. Living Standard. URL: https://dom.spec.whatwg.org/
[FONT-METRICS-API-1]
Font Metrics API Level 1 URL: https://drafts.css-houdini.org/font-metrics-api-1/
[RFC2119]
S. Bradner. Key words for use in RFCs to Indicate Requirement Levels. March 1997. Best Current Practice. URL: https://datatracker.ietf.org/doc/html/rfc2119
[WEBIDL]
Edgar Chen; Timothy Gu. Web IDL Standard. Living Standard. URL: https://webidl.spec.whatwg.org/

IDL Index

[Constructor(DOMString text, optional CSSStyleSheetInit options)]
partial interface CSSStyleSheet {
};

dictionary CSSStyleSheetInit {
  (MediaList or DOMString) media = "";
  DOMString title = "";
  boolean alternate = false;
  boolean disabled = false;
};

interface TreeScope {
  attribute StyleSheetList moreStyleSheets;
};
Document implements TreeScope;
ShadowRoot implements TreeScope;

Issues Index

Better name.
Or do we want to include manually-added sheets in document.styleSheets, similar to how document.fonts mixes OM-created and manually-created fonts? Big difference is that ordering matters here, which makes dealing with the invariants much more annoying. (What happens if you manually add a sheet between two <link> sheets, then insert another <link> in the document between them? Does it go before or after your manually-added one? Or do we just make it illegal to manually add a sheet before an automatic sheet?)
One of the major "misuses" of the >>> combinator is to apply "default styles" to a component wherever it lives in the tree, no matter how deeply nested it is inside of components. The use-case for this is to provide the equivalent of the user-agent stylesheet, but for custom elements (thus, the styles by necessity must come from the author).

Unfortunately, this is extremely slow, and there’s not a whole lot that can be done about that—>>> combinators are slow by their nature. Note, though, that the UA and user stylesheets automatically apply in all shadows; it’s only the author stylesheet that is limited to the context it’s created in.

(At this point, one might point out that this is already handled by just setting up styles during element construction. This doesn’t help for cases where a component is purposely authored to be styled by the end-user; forcing users of components to go muck around in their components' source code is a non-starter.)

One possible solution here is to add another origin, the "author default" origin, which sits between "user" and "author", and applies in all shadow roots automatically. We can add a list for these stylesheets, akin to document.styleSheets, and allow you to insert constructed stylesheets into it. Or maybe add an .origin attribute to CSSStyleSheet, defaulting to "author"?

Maybe it only applies to the context you’re in and descendant contexts? Need to investigate; probably bad to let a component apply automatic styles to the outer page via this mechanism.