-
-
Notifications
You must be signed in to change notification settings - Fork 75
css cascade layers polyfill #244
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
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
@romainmenke thank you! |
…-slow-worm-8ac9f7ce8c cascade layers tests
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
Whatever works best for you :)
I had something like this in mind :
It might not be needed to walk twice, but maybe it's easier to start with two walks so that the analysis and transforms are separated in code? The main challenge I think is walking the AST and building the correct data model. The actual transform is much simpler : this bit was not tested and is oversimplified, it won't handle nested @layer correctly const atRuleClone = atRule.clone();
const atRuleClone.nodes.forEach((node) => {
const modifiedSelectors = node.selectors.map((selector) => {
return `${selector}:not(<N times #foo>)`;
});
atRule.parent.insertBefore(atRule, node.clone({ selectors: modifiedSelectors }));
});
|
@romainmenke would you be able to explain more about what |
Hi @oluoluoxenfree! @oluoluoxenfree said :
@romainmenke said :
This function and callback will go over each In my mind it would be clearest (not most efficient) to have something like this : const state = ....;
root.walkAtRules((atRule) => {
// analyse the source CSS and store in "state"
//
// you want to have a structure or mechanic that enables this :
// give it a layer name : "foo" or "foo.bar"
// get a number back that indicates how much specificity needs to be added
});
root.walkRules((rule)) => {
// modify unlayered styles.
//
// these need to get the highest possible specificity compared to layered styles
// rule.selector = ...
});
root.walkAtRules((atRule) => {
// modify the layered CSS based on what is stored in "state"
//
// move all CSS out of the "atRule" and remove the now empty "atRule"
// https://postcss.org/api/#container-insertbefore
//
// modify the selectors so that they have the correct specificity
}); At the moment there is no other plugin doing something like this. @oluoluoxenfree said :
Very simplistic it could be something like this : const state = {
"foo": 1,
"bar": 2
} Corresponding to this : @layer foo {
a {
order: 1;
}
}
@layer bar {
a {
order: 2;
}
}
a {
order: 3;
} a:not(#foo) { /* 1 -> #foo */
order: 1;
}
a:not(#foo#foo) { /* 2 -> #foo#foo */
order: 2;
}
a:not(#foo#foo#foo) { /* unlayered -> 3 -> #foo#foo#foo */
order: 3;
} Building this state separately should make it easier to handle nested I hope this makes it a bit clearer :) |
First walkthrough, initializing data model
Hi @romainmenke and @oluoluoxenfree - I have an approach to handle when layers have both nested layers and unlayered styles and I'd love to get your feedback on it. Here is a draft PR I just opened and also a forked AST explorer to help visualize my thinking. In a scenario like this:
The rule inside layer A but outside of layer Z (i.e. When we do our first walkthrough and build out the data model, I wanted to ensure that our state reflects this priority correctly when there is layer nesting. Here is how I am thinking to handle it:
This means that after the first walkthrough our example above would now look like:
And since
❓The issue I am having with this approach is that after the first walk, when I am doing the second walk to build out the data model, I don't see the new layer. The AST explorer and postcss-tape tests show the insertion is working correctly but I don't think the root is showing the new insertions. Any advice on how to tackle this? 🤔 Also, please feel free to share any thoughts on this approach or feedback. I am happy to answer any questions, too 🙏🏼 |
@sanajaved7 Can you try with this code? : if (hasNestedLayers && hasUnlayeredStyles) {
const implicitLayer = atRule.clone({ params: `${atRule.params}.implicit` });
implicitLayer.each((node) => {
node.remove();
});
//create final layer
atRule.append(implicitLayer);
// go through the unlayered rules, clone, and delete from top level atRule
atRule.each((node) => {
if (node.type == 'rule') {
implicitLayer.append(node.clone());
node.remove();
}
});
} Want to give a slightly longer reply later, but hoping this gets you unstuck :) |
@romainmenke Awesome, that worked! Thanks for such a quick reply! I'll keep an eye out for your longer reply as well. Thanks again! |
Handling layer nesting
@romainmenke @oluoluoxenfree I started on handling |
@sanajaved7 Do you maybe have an example? If possible we prefer not to have a client side component to this polyfill as this will cause a flash of incorrectly styled content and make presentation dependant on javascript. At the moment the only part of the specification that we can not handle is re-ordering of layers in I am however curious if we can not handle |
Have read up a bit on My best idea at the moment :
This has many issues :)
Basic example (click to expand)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
@layer base, special;
@layer special {
.item {
--color-special: red;
color: red;
}
.feature {
color: var(--color-base);
color: revert-layer;
}
}
@layer base {
.item {
--color-base: blue;
color: blue;
}
.feature {
--color-base: green;
color: green;
}
}
</style>
</head>
<body>
<p>This example contains a list.</p>
<ul>
<li class="item feature">Item one</li>
<li class="item">Item two</li>
<li class="item">Item three</li>
</ul>
</body>
</html> Is this something that can be handled better client side with Javascript? Although I understand the usefulness of Does this plugin work well enough without any support for |
Hi all! Yeah, I think there a few features here that would need to be handled client-side, and We'll have a similar issue if people want to use layers inside media queries. If you put
We might just want to throw a warning if people do it anyway. (I don't think either of these should be blocking - we should just make sure the limitations are well-documented) |
Hi @mirisuzanne!
I think this is the right way to go at the moment. We can adjust later if there is a good client-side polyfill. I did still have some questions about Also opened an issue about this on WPT : web-platform-tests/wpt#33767 Do you have any guidance on this aspect? if this is resolved I think we can do a last review on this plugin and finalise documentation and examples |
@romainmenke I can answer the question about proper behavior, but not the question about what other tests to look at. I replied on the WPT thread with the proper behavior (layer ordering is reversed in important layers). I also pinged some of the Chrome devs who wrote those tests, so hopefully they can speak to the other tests that exist. |
@romainmenke @oluoluoxenfree I was looking at layer importing and realized there is a comment from earlier that |
@romainmenke are we good to merge everything in without the wpt tests or are they blocking? |
You would need to use the postcss import plugin if you want to use
I do not think we need to wait on the wpt tests. Would be nice to have a few final touches :
|
cascade layers : important and warnings
@romainmenke I've got a draft PR where I'm adding a warning and info the README for using the
|
This is not needed. In practice people who use If any
This is expected but maybe we need to make the sorting of root nodes a bit more strict 🤔. We can improve this later, after an initial release.
This looks great! |
@romainmenke thanks so much! I've updated that test and think the PR should be ready to review whenever you have a chance. Thanks in advance! |
@oluoluoxenfree @sanajaved7 I think the whole feature is now ready to be merged? |
@romainmenke yes, I think so! Should we just flip it to ready for review? Are there any other things needed as well? |
@sanajaved7 There seems to be a merge conflict, most likely That should be the last step 🎉 |
@sanajaved7 @oluoluoxenfree and everyone at oddbird thank you for all this! We will merge this in and perform a few last housekeeping steps before releasing this. |
Even if I haven't been personally involved I've been keeping an eye and all I can say is great work you all! It's been awesome to see it take shape and can't wait to get it out. Kudos! @sanajaved7 @oluoluoxenfree (and oddbird! 🐥) 🎉 |
Thank you @romainmenke and @Antonio-Laguna, this has been super fun to work on with you and we appreciate all of your help along the way! 🚀 🎉 |
@sanajaved7 @oluoluoxenfree This has just been released : https://www.npmjs.com/package/@csstools/postcss-cascade-layers by @Antonio-Laguna We always wait a little bit before adding it to postcss-preset-env but plan to do so next week. Did you have anything in mind for an announcement for this? I also keep forgetting to mention that we are on Discord : https://discord.gg/bUadyRwkJS |
#105
Writing a polyfill for cascade layers using
:is()
and:not()
.