Fix Layout Shifts by managing priorities when enabling styles
EDIT: After this was merged at 2021-07-22 after some Back & Forth the solution proposed below was not the one that was merged. We found a way to fix the problem without any visual regressions (by not changing the order in which the utility classes are loaded) More details and Demos on this approach here
What does this MR do?
On first load (Clear Cache required) we are having layout shifts. With this MR we are refactoring our basic styles to get rid of this layout shift.
What's wrong here?
First of all, it's worth explaining what is the problem here. The reason we see the elements that are supposed to be hidden is indeed in the fact that application_utilities has not yet been applied to the page. However, the solution to this problem is not only in loading the application_utilities as soon as possible, but there are two conditions:
- import applicationBEFOREapplication_utilities. Otherwise, we will have the situation we're about to fix here permanently: the styles in theapplication.csswill override styles in theapplication_utilitiesthat ruins the whole point of the utility classes. This is one more reason why StartupCSS is not the right tool here.
- import both applicationandapplication_utilitiesBEFORE the content is rendered. Due toapplication_utiltiesbeing applied after the content has been output on the screen is the main cause of the issue here.
What's the solution?
After playing with the code and different approaches, we found a very simple yet effective solution. We do see that application.css gets applied before content is rendered on the screen. This is controlled by application.scss itself: by switching the content part of the page into display: block. This assures that application.css is parsed and before the content output.
So, the solution we came to is simply piggybacking this feature and import application_utilities right into application.scss as the very last import. This way we can be sure that both of the conditions above are met:
- since the import is the last one in the application.scss, the first condition is fulfilled
- since the application.scsscontrolls when the content is rendered and makes sure the styles are applied before that, we fulfill the second condition
Wouldn't it harm our LCP?
By reducing the parallelization in the downloading process, indeed we might have harmed the performance a bit. But, most probably by a very tiny margin. First of all, once the application.scss is fetched, it gets cached. So only the first hit on GitLab would be affected. By how much? Well, the result kB are pretty much the same and it's only the difference of 1 vs. 2 requests.
As a bonus 💪 
In the course of this change, we have also identified that application_utilities_dark isn't really needed. So the references to it have been removed and we might want to remove the stylesheet itself in the next iteration.
Demo
Note: Visual Changes in the video appear in the top section of the MR:
| Visual Changes area Example | 
|---|
|  | 
| before | after | 
|---|---|
| before-layout-shift | after-layout-shift | 
Expected Sideeffects
We'll most likely will be dealing with two different sideeffects once this MR will be merged.
pleasant sideeffect
It can be assumed since we are dealing with global stylesheets here, that the Demo Video above is not the only place behaviour like this occurs. Since the application_utilities stylesheet provides the basic foundation for styling our components, other parts of GitLab will benefit from this change and potentially more Layout shifts will be eliminated.
not so pleasant sideeffect
Just as @leipert explained perfectly in this comment !64806 (comment 621228857)
Initially:
The split of application and application utilities was on purpose, because the application utilities are supposed to overwrite the
page_specific_styles
Let's imagine you're going to have the following Markup
<div class="foo gl-display-block!">
The loaded styles were coming in as:
// page specific style
.foo {
  display: none !important
}
// utility
.gl-display-block! {
  display: block !important
}Which had the upside that utility styles were overwriting the page-specific styles, but under certain network conditions, the styles above will cause layout-shifts.
With the changes in this MR the styles will be like
// utility
.gl-display-block! {
  display: block !important
}
// page specific style
.foo {
  display: none !important
}
Which will remove any potential Layout Shifts, but since page-specific styles are no longer overwritten but utility styles, this has the potential to brake existing styles. This is probably a reasonable trade-off and the affected styles will be rather easy to fix, still something to be aware of.
Credit: Thanks to @pslaughter & @dmishunov for pairing up on this MR!
Does this MR meet the acceptance criteria?
Conformity
- 
I have included changelog trailers, or none are needed. (Does this MR need a changelog?) 
- 
I have added/updated documentation, or it's not needed. (Is documentation required?) 
- 
I have properly separated EE content from FOSS, or this MR is FOSS only. (Where should EE code go?) 
- 
I have added information for database reviewers in the MR description, or it's not needed. (Does this MR have database related changes?) 
- 
I have self-reviewed this MR per code review guidelines. 
- 
This MR does not harm performance, or I have asked a reviewer to help assess the performance impact. (Merge request performance guidelines) 
- 
I have followed the style guides. 
- 
This change is backwards compatible across updates, or this does not apply. 
Availability and Testing
- 
I have added/updated tests following the Testing Guide, or it's not needed. (Consider all test levels. See the Test Planning Process.) 
- 
I have tested this MR in all supported browsers, or it's not needed. 
- 
I have informed the Infrastructure department of a default or new setting change per definition of done, or it's not needed. 
Security
Does this MR contain changes to processing or storing of credentials or tokens, authorization and authentication methods or other items described in the security review guidelines? If not, then delete this Security section.
- 
Label as security and @ mention @gitlab-com/gl-security/appsec
- 
The MR includes necessary changes to maintain consistency between UI, API, email, or other methods 
- 
Security reports checked/validated by a reviewer from the AppSec team