Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cssom] Add toString method on CSSStyleSheet #7171

Open
lucacasonato opened this issue Mar 24, 2022 · 12 comments
Open

[cssom] Add toString method on CSSStyleSheet #7171

lucacasonato opened this issue Mar 24, 2022 · 12 comments
Labels

Comments

@lucacasonato
Copy link

CSSOM allows programmatic construction and modification of CSSStyleSheets. These created sheets can be attached to a HTML document on the client so users can directly use them. It is however not currently trivial to "export" programmatic style sheets back into a text representation.

This is unfortunate, because the CSSOM API would be great for the generation of style sheets server side, if the server side runtime were to support the API. This is something that we would like to do in Deno (denoland/deno#13898). In Deno, there is no DOM, so the style sheet could never directly be used. Instead you would build a style sheet with the CSSOM API, and then export it to a string and send it to a client.

It is possible to turn a CSSStyleSheet into a string my manually concatenating the string representations of each rule. This is what the stringify-css-stylesheet npm package does for example. A simplified example:

function stringifyStyleSheet(stylesheet) {
  return Array.from(stylesheet.cssRules)
    .map(rule => rule.cssText || "")
    .join("\n")
}

It would be awesome if CSSStyleSheet.prototype.toString() would exist to do this natively, without requiring any helper code.

@tabatkins
Copy link
Member

Yes, this seems like a lack purely due to apathy, not due to any actual problems, since authors can trivially serialize an entire sheet anyway, as demonstrated. I think this would be completely fine to add.

I couldn't find any discussions about this in the past with a quick search thru my archives, but that doesn't guarantee there wasn't any. Is anyone aware of us discussing this in the past?

@timotius02
Copy link

timotius02 commented May 3, 2022

Hello, I'm currently taking this issue after some guidance from @tabatkins (thanks a lot for your guidance btw).

I wanted to know whether the stringifier should be the toString method of CSSStyleSheet like this issue suggests or if it should be exposed as a string property just like in CSSRule. Would love some opinions on this.

@tabatkins
Copy link
Member

For consistency, we probably should expose it as cssText, but we can make it better by making it a stringifier attribute.

@Loirooriol
Copy link
Contributor

CSSStyleRule doesn't have a stringifier, so I would keep CSSStyleSheet consistent.

@lucacasonato
Copy link
Author

Or we add a stringifier to both?

@timotius02
Copy link

I would be open to changing both, but what are the chances that there's something out there that relies on the current stringified behavior of CSSStyleRule?

Also is there a difference spec-wise other than adding stringifier to the attribute to make something a stringifier?

@tabatkins
Copy link
Member

Nope, that keyword is all you need.

(If you don't have an attribute already containing the text you have to write the definition slightly different to have an independent stringifier, but it's literally just a few words difference in invocation.)

And yeah, adding a stringifier to both would be ideal (but it increases the testing cost slightly). Relying on the current stringification seems not too likely. (In theory someone could be using it for dirty type-testing since it currently stringifies to "[Object CSSRule]", but doing that generically requires you to specifically use Object.prototype.toString.call() precisely because a lot of objects have stringifiers, so it's likely such uses are relying on that and won't be affected.)

@Loirooriol
Copy link
Contributor

It should also be consistent with other interfaces like CSSStyleDeclaration. And given that most things don't have stringifiers, I wouldn't add them here.

@timotius02
Copy link

timotius02 commented May 4, 2022

On the other side however, it seems like MediaList interface has a stringifier, though it seems like it's the only one in cssom and I can't seem to track the reasoning behind that.

We can choose add a stringifier to CSSStyleDeclaration as well but if we do want to move in the path of least resistance, just forgoing stringifiers is best. Can anyone provide context on the state of stringifiers as a whole in w3c spec, like how common are they currently and any specs that heavily uses stringifiers as major features?

@trusktr
Copy link

trusktr commented Nov 4, 2023

Here's a use case for this:

In the second snippet there, the #shared-stylesheet could have a CSS OM and no textContent, so to-stringing it would be handy.

But iterating the rules is also not that difficult.

@TheJaredWilcurt
Copy link

Use case: Visual Regression (screenshot) testing

There is a problem with visual regression tests where they aren't cross-browser or cross-platform compatible due to subtle differences in rendering, especially fonts. So taking a screenshot of the app in a specific state, and then comparing the pixels against a previous known-good screenshot will fail just because the screenshot was done on a different OS or slightly different browser version.

If you could serialize the entire DOM and CSSOM and get hashes of each, then store those hashes next to the screenshot, you'd be able to inform a CI tool to update the snapshots when it re-runs if the hashes do not match. Allowing you to locally update the hashes when you want a new known good, without having to commit a screenshot from your machine that will only work for your machine. Then when the CI runs, it validates the screenshots, or replaces them if the hashes don't match.

This would work best if you could serialize just the part of the DOM and CSSOM that applies to a specific selector, like [data-test="myWidget"]. So the screenshot does not need to be of the entire page, but just the part related to that specific test.

@mayank99
Copy link

mayank99 commented Jul 8, 2024

Ran into a super weird issue today when using the cssText approach shown in the original description. Apparently it can produce invalid CSS when using custom properties (https://issues.chromium.org/issues/40804066). Obviously this bug should be fixed, but a .toString() function could help avoid the problem altogether.

Edit: Actually .toString() would have the same issue since that's how it's specced to be serialized. I think the spec should probably be fixed if we're wanting to serialize entire stylesheets?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

7 participants