Consider a different name for 'adoptedStyleSheets'
The name adoptedStyleSheets is a bit awkward, for a few reasons:
It's long
Unwieldy names aren't uncommon in the DOM, but it's particularly problematic when the most common example usage...
document.adoptedStyleSheets = [...document.adoptedStyleSheets, newSheet];
...involves repetition. (I'm hopeful that this pattern will be abandoned in favour of explicit methods, due to the fact that it will result in nasty race condition bugs, but it's still a lot to type.)
'Adopted' is confusing
I'm not aware of any other places in the DOM where this verb is used. The most commonly understood meaning of 'adopted' is in the family context, where there's a parent-child relationship. Multiple families cannot adopt the same child. Used here, it could easily be understood to mean that a stylesheet can belong to a single element.
The casing is hard to remember
Although CSS stands for Cascading Style Sheets, people informally talk about 'stylesheets' rather than 'style sheets' — one word. You only ever see <link rel="stylesheet">, never <link rel="styleSheet">. This makes it harder to remember whether it's adoptedStylesheets or adoptedStyleSheets, particularly if you're not writing it frequently.
In fact, this is part of a spec called Constructable Stylesheets!
Of course, there is an easy way to remember it, but...
...The acronym is unfortunate
Not that I'm opposed to levity in web development, but I guarantee that this will be universally shortened to 'document dot aSS' or 'this dot aSS'. I'm not sure if that's what everyone had in mind.
Is there a reason this couldn't simply be document.css? An API like this would be a lot more palatable to most developers:
document.css.add(sheet);
(I realise there are concerns about being able to control the order of the collection, so add and remove may be too simplistic an API, but I'd expect appending to account for 99% of cases. Regardless, that's a separate issue.)
I like it.
It mimics the KISS paradigm of :
e.classList.add('...')
... which I love to use.
In webcomponents, I probably would like to do :
this.css.add(sheet);
To add global stylesheets, I might do :
globalThis.css.add(sheet);
'Adopted' is confusing I'm not aware of any other places in the DOM where this verb is used.
It's defined here and can be seen in a few APIs such as document.adoptNode and the CE callback adoptedCallback.
Step 2 from that spec page:
If node’s parent is not null, remove node from its parent.
In other words, a node cannot be adopted by multiple parents. It feels like it's being used incorrectly here.
There is a namespace object named CSS, though I think it's more for utility functions like CSS.escape(s) rather than managing stylesheets. Just something to keep in mind to not end up with a confusing API, like if there's globalThis.css and globalThis.CSS.
https://drafts.csswg.org/cssom/#utility-apis
Wouldn't globalThis.css not work since the adoptedStyleSheets currently sits on DocumentOrShadowRoot? I agree we should be weary of collisions, but this fact might mitigate your concern.
One thing about "css", is that it specifies the exact technology used today for style sheets. If there were to ever be another styling type, other than CSS (that could be mixed into this array along with css), at that point "css" would become a bad name.
I like "styles" (plural) for the name because it reminds me that this is an array of styles even if [onlyOneStyleIsUsed] and the list could potential hold any future technology-type for styling if that ever happens. "styles" is also pretty short (to type) compared to "adoptedStyleSheets". "styleSheets" also comes to mind too as a better choice. "sheets" is another option. "adopted" needs to be removed at the very least.
We definitely need some array-like structure for the API, since the underlying semantics are array based. I see the need for these operations:
- Append one or more sheets to the end of the list
- Prepend one or more sheets to the start of the list
- Get length
- Insert one or more sheets at arbitrary indices
- Access by index
- Iterate through the list
- Remove from arbitrary indices
- Replace the entire list
For what it's worth, I think the initial idea was to use moreStyleSheets to draw a parallel with DocumentOrShadowRoot.prototype.styleSheets. I just want to make sure this fact wasn't overlooked, the initial goal was to maintain an association between the two property names. I definitely see value in that, but also realize it isn't strictly necessary, but wanted to make sure that point isn't overlooked moving forward.
The casing is hard to remember
It's the exact same casing already used in document.styleSheets. I agree it's confusing and I don't like it, but it would be much worse to adopt something different.
'Adopted' is confusing
Legit. Our usage of 'adopted' elsewhere in the platform does indeed imply ownership that's not present here. My original suggestion was, iirc, .moreStyleSheets, which wasn't great either. ^_^
...The acronym is unfortunate
lol, good point
It's long
I get you, but I really think it's important to keep this name close to the existing document.styleSheets, since it's doing essentially the exact same thing. If we're still using "styleSheets", we can't get too much shorter than "adopted".
One thing about "css", is that it specifies the exact technology used today for style sheets.
That's not really a big deal here; we're long, long past the point where a new styling language is a particular concern.
We definitely need some array-like structure for the API,
Yup, need basically all the functionality of an Array here. If we could convincingly fake an Array on the platform, that would be something else, but we can't. :/
I see the need for these operations
As an app developer I would file the following under YAGNI:
- Get length
- Insert one or more sheets at arbitrary indices
- Access by index
- Iterate through the list
- Remove from arbitrary indices
I'm also sceptical that I would need to prepend, in practice. I appreciate that there may be some contrived situations in which those operations are necessary, but it doesn't seem to me that the API needs to optimise for them.
I really think it's important to keep this name close to the existing
document.styleSheets
I'm not sure I've ever seen anyone use document.styleSheets in the wild. Again, I'm sure it has its uses, but it's not part of most web developers' lived experience, whereas document.adoptedStyleSheets would be. And most usage examples of adoptedStyleSheets I've seen focus on the custom element case, rather than document. So I'm not sure I agree that that's a priority.
Moreover, document.styleSheets is a StyleSheetList rather than a frozen array, so if consistency is a goal then it still falls short.
As an app developer I would file the following under YAGNI:
As someone who maintains code that already uses adoptedStyleSheets I have to disagree.
Managing the list directly is extremely important for app and component-set theming mechanisms and polyfills that might rely on adoptedStyleSheets. Given that multiple actors may be managing the list, being able to tell if the sheets you care about are already in the list, and what precedence they're at is important.
Hum, so you want to be able to test a key, then decide if you insert before of after this key.
If the word adopted is confusing as per multiple parents adopting a child. Maybe .appliedStyleSheets would be a better name.
If the acronym is really considered regrettable, .extraStyleSheets is another option.
Why not simply .constructedStyleSheets?
The name is a bit longer than .adoptedStyleSheets, but it is clear about exactly what this is for: the structure that holds the style sheets that were created using the constructor.
The only thing I dislike about .appliedStyleSheets is that any of the sheets can be disabled, in which case, they are not applicable.
.extraStyleSheets would probably be fine, but it doesn't communicate any inherent restrictions, especially in the case of getting a NotAllowedError when you try to add a non-constructed sheet.
If I were a developer exploring a new feature called Constructable StyleSheets, I think I would find it intuitive that there is a new attribute called .constructedStyleSheets.
Ostensibly this API will be used for CSS modules as well, so .constructedStyleSheets doesn't quite fit.
Just putting some thoughts here, the purpose of this API is to share stylesheets across multiple DocumentOrShadowRoots and to assume styles from CSS modules. If we want to keep the -StyleSheets suffix, perhaps something along the lines of sharedStyleSheets.
connectedStyleSheets could also work (the acronym might be confusing), but the language relates to Node.isConnected which is a property on the Node object that illustrates whether or not the node in question is connected to a context object. In this instance the adjective connected is relating to which style sheets are currently connected to the relevant DocumentOrShadowRoot with an understanding that each sheet that can be connected can also be connected elsewhere (where the analogy fails alongside the analogy for adopted).
I think the name should describe what the author intends to do with the stylesheets, not how they were made. One could imagine future ways to create detached stylesheets that don't involve a constructor, but which might still be desirable to attach to a document at some point. So we should not lock in how they are made IMO.
I am not sure why adding non-constructed stylesheets is not allowed currently. Is it because all other current ways of making stylesheets already start out attached to a document? In which case the error isn't that it's not constructed, it's that it is already attached.
I think it's reasonable to not tie the name to how the sheets are created in case that were to change in the future.
I am just referring to the spec's description of adoptedStyleSheets:
If any entry of adopted has its constructed flag not set, or its constructor document is not equal to this
DocumentOrShadowRoot's node document, throw a "NotAllowedError"DOMException.
Alright, I've tired to summarize the list of suggestions made so far with the corresponding concerns.
-
cssConcerns: Confusing with CSS. Does not sound related todocument.styleSheets. -
moreStyleSheets -
extraStyleSheetsConcerns: It doesn't communicate any inherent restrictions, especially in the case of getting a NotAllowedError when you try to add a non-constructed sheet. -
appliedStyleSheetsConcerns: any of the sheets can be disabled, in which case, they are not applicable. -
constructedStyleSheetsConcerns: Naming how they were created is strange; we may want to allow CSS moduels or StyleSheet from style element in the future. -
connectedStyleSheets -
sharedStyleSheetsConcerns: These style sheets are not always shared.
What about something like cssList? Like a classList, but for style sheets.
What about something like
cssList? Like aclassList, but for style sheets.
I think that has the same concern mentioned earlier that it sounds not related at all to document.styleSheets or shadowRoot.styleSheets.
To me the essence of these things are that "each style sheet listed" is in the form of a javascript variable that points directly to the style in memory and NOT some URL that has to be downloaded and cached from a web server.
This makes me think of names like:
- referencedStyleSheets
- linkedStyleSheets
- styleSheetPointers
- styleSheetReferences
- styleReferenceList
By now, isn't just going to stay adoptedStyleSheets anyway? Verbosity!
I'd be cool with styles, styleSheets, or shadowStyleSheets.
So long as it's not misleading as to the relationship between the stylesheets and the document/shadow root in a problematic way, I think it's nice that a web component's shadow root connectedStyleSheets and a web component's custom element connectedCallback would both use "connected."
...despite them being slightly different senses of connected.
Those are different senses of the word connected, I think.
@othermaciej I disagree. isConnected reports if a particular node is attached to some point in a DOM tree. The true analog would be to let stylesheets (constructed or modules) have an isConnected feature as well, let's pretend for a minute they do:
const sheet = new CSSStyleSheet();
someShadowRoot.adoptedStyleSheets = [ sheet ]; // using the old naming convention on purpose here
console.log(sheet.isConnected); // if the feature exists, I would presume this to be true
Therefore we can ask to what is sheet connected? In this instance it is connected to someShadowRoot. Therefore I do think it makes sense to call this API DocumentOrShadowRoot.prototype.connectedStyleSheets and perhaps introduce the CSSStyleSheet.prototype.isConnected (which I can see being useful, but don't have a hard use case for so I won't push it) or even CSSStyleSheet.prototype.connectedTo which could be a NodeList of every tree the sheet is connected to.
I don’t think the analogy with node’s connectedness quite works because a single node can be associated with at most one document whereas a style sheet can be associated with multiple shadow trees / documents; this is actually problematic though... We probably want to limit it to the same document somehow. Otherwise we’d end up with global object leaks.
@rniwa, That makes sense, but unless I've missed something (which is possible, you are certainly better prepared to make that sort of statement than I am), there's nothing about connected that necessarily implies a one-to-one relationship; a Node instance, however, does have that implication, but a CSSStyleSheet and a Node are different beasts, but both can feasibly be connected to some context. That's all I'll say about it, thanks for all the hard work on making this API work for everyone.
@rniwa the suggestion in https://github.com/WICG/construct-stylesheets/issues/97#issuecomment-521299640 was not in your list: sheets.
Though I think the concerns against moreStyleSheets and extraStyleSheets also apply to sheets, but, if a short name is deemed more important, this one seems better than css.
There is some precedent: https://drafts.csswg.org/cssom/#the-linkstyle-interface
@zcorpan That actually makes a lot of sense. If you do let style = document.createElement('style'), the style object will have a property called sheet that is the CSSStyleSheet (or StyleSheet in Firefox) object that would be created here. Calling the API sheets literally makes it a list of sheet objects.