Describe the bug; what happened?
Currently there are composed components in the fast-components-react-msft package that add classes to other components. This creates a specificity issue because we are relying on the rendering order of a components style in the DOM for style overrides. If a component used by another component is rendered on a higher level, this will cause the order to be incorrect.
What are the steps to reproduce the issue?
Example:
What behavior did you expect?
I expect no matter where the components are used and in what order they render that the styling should be consistent.
Is there any additional context?
A possible solution for the exported packages is to duplicate the stylings and only rely on the base components possibly by extending the class name contract.
We've encountered this issue on MSN and I think it needs to be marked as high priority
From my investigation the style precedence is based on render order of styled components, this would work fine except that styles are also be de-duped by the sheet registry.
This causes an issue when a fast-dna styled component is passed a CSS class which overrides its default style class from its parent component. If the styled component is re-used before the parented one then the default style class will have precedence over the passed in CSS class.
Some of options I can see for dealing with this :
1) Remove the de-duping of styles
- +Simplest implementation
- -Potentially large performance hit, getting worse with size of the site
2) Give fast-dna component styles the lowest precedence
- +Fairly simple implementation
- -Only fixes the problem for fast-dna components
3) Create a set dependency graphs to de-dupe based on
- +Most robust solution
- -Complex solution
- -Complex implementation
- ~Performance implications are unclear
- ~Unclear how much de-duping could be determined
4) Expose setting of the style index
- +Simple implementation
- -Moves the difficulty to the users of the library
- -Maintaining the current impl would mean this is also non-deterministic
5) Remove injectable styling from styled components
- +Simple implementation
- -Breaking Change
- -Feature regression
Ultimately the problem is that render order is not a deterministic way to set precedence so either it needs to move to something that will always be deterministic, or handle the complexity, or remove the handling from the library
Thanks @shauntc, this is a great summary.
Regarding your first point, that was the original implementation of the library and we did see large performance impacts that were ultimately unacceptable. To fix that, de-duping was introduced though that is obviously causing it's own issues.
I'm intrigued by using a dependency graph to determine style element order, but I agree there are unknowns there.
Aside from a dependency graph, I'm starting to think manual indexing may be the most viable solution. CSS specificity has always been tedious to manage, so providing a clear mechanism for authors to apply and preserve component style element order addresses the issue in a manner consistent with managing CSS itself. We would still need to determine initial order though, which I believe is reverse call order in JSS (not 100% deterministic).
I'm not sure I'm following your fifth point - can you clarify that one?
@nicholasrice the fifth one is removing the capability of setting an extra class name on already styled components thus there is only one class per component and no precedence issue, it is another simple but incomplete solution, only for the styled fast components
I figured that was the case with the first solution, and I agree that the dependency graph and exposing the index are the two most long-term solutions. Personally I would favor the dependency graph, being a library of reusable components, handling the complexity of style ordering would be a much better experience for the user and I think it could be built without changes to the Api of the library.
React's context api could be used to generate the tree from inside the JSSManager. I created a super rough basic proof of concept of generating depth/id paths for the styles which could be handed to the sheet manager to create the dependency tree.
Whichever solution is decided on I think temporarily implementing 2 would be a good quick solution. Forcing lower precedence on the fast component styles as they shouldn't need to override other styles
Gotcha - yea I don't think we would want to remove that ability because an author may need to add any arbitrary class to a managed component for purposes we can't know, and it prevents overriding where the selector may have enough specificity regardless of style-element order.
Thanks for the proof of concept - I'll take a peek at that today.
@nicholasrice Any updates on this?
@shauntc No - I haven't had a chance to make progress on this.
My hope was that component dependency tracking and resolution of a sheet order based on that data would prove viable, but I was unable to get a working prototype during my exploration. While I still feel this could be the most developer friendly solution, the following issues are preventing me from recommending it at this time:
Another solution proposed by @shauntc is to enable memoization based on incoming class-names. Because the issue only repros when attempting to override a component's styles with classes, I think this solution is viable. It would, however, increase the number of style elements being created, which can negatively impact performance.
A third solution is to enumerate style order manually. This is surely the most deterministic method and gives authors complete control over sheet order, but does require manual management of indexes.
To address the issue, I'd like to propose a three-part solution.
This seems like a good compromise.
The most troublesome part of this issue, in my mind, is the lack of documentation on why it happens, which if known can be largely mitigated.
I also think the approach towards granular control on ordering the styles is an acceptable requirement since that is something that those authoring CSS should already familiar with.
This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.
closed by #3517
Most helpful comment
My hope was that component dependency tracking and resolution of a sheet order based on that data would prove viable, but I was unable to get a working prototype during my exploration. While I still feel this could be the most developer friendly solution, the following issues are preventing me from recommending it at this time:
Another solution proposed by @shauntc is to enable memoization based on incoming class-names. Because the issue only repros when attempting to override a component's styles with classes, I think this solution is viable. It would, however, increase the number of style elements being created, which can negatively impact performance.
A third solution is to enumerate style order manually. This is surely the most deterministic method and gives authors complete control over sheet order, but does require manual management of indexes.
To address the issue, I'd like to propose a three-part solution.