Readers/Web/Dev notes/Zebra update

Challenges

edit

We set out to update the visual appearance of Vector 2022. Changes were entirely cosmetic and meant to mainly address complaints about white-space.

Question: How do we build this new visual appearance while maintaining the old one?

Feature flagging is necessary, but not sufficient.

Approaches

edit

Current strategy

edit
  1. Create a new resourceLoader module called skins.vector.zebra.styles
  2. Copy over files that would be affected by the zebra update into the new module
  3. Scope the affected files in both the skins.vector.styles and skins.vector.zebra.styles module to the feature flag.

i.e. in skins.vector.styles/skin.less

 @media screen {
     .vector-feature-zebra-design-disabled {
 	@import './layouts/screen.less';
     }
     @import './components/PageTools.less';
 }

i.e. in skins.vector.zebra.styles/skin.less

 @media screen {
     .vector-feature-zebra-design-enabled {
 	@import './layouts/screen.less';
     }
 }
  1. In SkinVector2022.php, create a function called getDefaultModules() which conditionally loads the zebra module if the feature flag is enabled. NOTE: When the feature flag is enabled, both the default style module AND the zebra module are loaded. The default style module contains files that are scoped AND NOT scoped to the feature flag. The zebra module contains ONLY files that are scoped to the feature flag.
  2. At this point, we have created two semi-divergent codepaths. Files in zebra will diverge from file in the default module, but files that are unaffected by zebra will remain in the default style module and be loaded along-side Zebra.

Other strategies considered

edit

The current approach partially separates the feature flag code. i.e, the new module still load all styles from the default style module. A different approach could be to split the new feature module entirely, and load one or the other depending on the feature flag. This has been described as feature branch, but without the version control. This was not chosen due to the potential difficulty of merging non-feature work into the feature module.

Past strategies

edit

During the Page Tools project, new styles were also feature-flagged, but differently. The feature-flag class was applied on a per-rule basis, spread throughout existing files.

e.g.

.vector-page-tools-landmark { 
    .vector-feature-page-tools-pinned-enabled .vector-page-toolbar-container &, 
    .vector-feature-page-tools-pinned-disabled .vector-column-end & { 
        display: none; 
    }
}


Observations

edit

πŸ‘Ž Wrapping the file @import in a class is unintuitive 🀒

edit

We often rely on classes on the HTML element throughout our styles. These classes target different features or UI states, e.g. pinned/unpinned/full/narrow-width etc. By wrapping the imports in a feature flag class, i.e. .vector-feature-zebra-design-enabled or .vector-feature-zebra-design-disabled all of the existing HTML selector in those files have to be modified.

e.g.

@media ( max-width: @max-width-tablet ) {
	.client-js,
	.client-nojs {
		.vector-pinned-container {
			display: none;
		}
	}
}

Has to become:

@media ( max-width: @max-width-tablet ) {
	&.client-js,
	&.client-nojs {
		.vector-pinned-container {
			display: none;
		}
	}
}

Note the & at the beginning. That ampersand has to be applied wherever a selector targeting the HTML element is used.

This is very unintuitive because the value of that ampersand comes from a different file (the skin.less file that wraps the import in a feature-flag class).

πŸ‘Ž Wrapping the file @import in a class causes specificity issues πŸ™ƒ

edit

The following UBN was caused by the change in specificity when wrapping the import in a class selector:

#siteSub { display:none } became .vector-feature-zebra-enabled #siteSub { display:none }

which ended up overriding on-wiki common.css styles that show/hide the tagline. Other user-land styles could be affected by this change in specificity.

πŸ‘ Isolating modules has allowed for bigger refactors πŸ—οΈ

edit

By moving entire files into a new module, we were able to refactor the entire contents of those files under the protection of a feature flag. If we had developed this feature alongside the fully-deployed code, refactoring tangentially related code would be too risky.