Page MenuHomePhabricator

[SPIKE] Determine how to mitigate performance concerns for CSS-only components
Closed, ResolvedPublic3 Estimated Story PointsSpike

Description

Open question

Is there any way we can limit the CSS that is shipped to what is *actually* needed on a given page (both for server-rendered and client-rendered components)? Having to load ALL of “codex-css” and then Vue, and then Codex JS means a lot of code

Proposed Solution

See https://phabricator.wikimedia.org/T323179#8455644 for what the current proposed implementation path for how to ship CSS Codex components alongside the JS ones we are already building. That proposal is informed by the performance considerations explored here, but it is balanced with some practical concerns (minimizing risk, not needing changes to existing infrastructure, etc).

Here is a short summary of the performance implications of the approach under consideration:

  1. Codex components will be organized into a few different modules for use inside of MediaWiki, similar to how we currently do things with OOUI. To do this we'll need to create additional bundles using Vite that contain the JS and CSS for a sub-set of our components. For example a bundle that just contained Codex buttons and related components would have codex-buttons.js and codex-buttons.css. This is similar to how we are currently building the codex-search package as a separate bundle.
  2. These bundles will get included in MediaWiki as separate ResourceLoader modules. Each new codex "sub-package" will likely require two RL bundles: one for the styles (which can be used by itself if only CSS-based components are needed), and one with the JS. The latter module will have the former as a dependency.

This will allow for users of Codex inside of MediaWiki to gain the following benefits:

  • They won't need to load ALL of Codex just to use a small number of components. Without more sophisticated front-end build tools (which all require a Node.js runtime) we can't do perfect tree-shaking, but at least this will allow us to avoid the worst inefficiencies (where all of Codex needs to be loaded when only one component is needed). We believe this will be acceptable for now from a performance standpoint (we do the same thing for OOUI). If more optimization is needed then we need to explore introducing a true front-end build step in MediaWiki.
  • CSS can be loaded independently of JS – and subsequent loading of JS (to progressively enhance the page) will not require re-loading the CSS that has already been delivered. Simple features that don't need JS at all can just use the CSS style module for the components they need and be done with it.

Acceptance criteria

  • DST discusses potential problems and solutions
  • DST determines a path forward based in tandem with choosing implementation path (see T323179) and documents work via subtasks of the CSS-only components epic

Event Timeline

Restricted Application changed the subtype of this task from "Task" to "Spike". · View Herald TranscriptNov 15 2022, 11:22 PM
Restricted Application added a project: Design-Systems-Team. · View Herald Transcript
Restricted Application added a subscriber: Aklapper. · View Herald Transcript

Initial thoughts for how we could enable separating these two CSS bundles in Codex:

  • Move the styles needed for the CSS-only version (the "basic styles") of a component out of the .vue file, into a separate .less file (one for each component)
  • Keep the styles that are only needed for the JS version (the "interactive styles") in the .vue file
  • In the build process, build two(*) separate CSS files: one with the basic styles for all components, and one with the interactive styles. (Optionally, we could build another CSS file that combines both, for convenience). Publish all these files as part of the Codex package.
  • If any CSS-only components are present on the page, load the basic styles as render-blocking styles
  • If any JS-powered components are present, load the interactive styles as part of the JS bundle for those components (plus the basic styles if they haven't already been loaded)
  • Optionally, we could express this relationship between the .vue file with the component code and the .less file with the basic styles by having the style block of the .vue file @import the .less file. However, we'd then have to map these imports to an empty string when building the interactive-only bundle, which would also not be super clear

(*) Really four, because we would build an LTR version and an RTL version of each

This would not solve for all components being bundled together, but that's a pre-existing issue independent of CSS-only components. We could explore solving that by splitting Codex into smaller bundles, kind of like what we did with codex-search (but ideally I'd like to avoid creating dozens of NPM packages; support for a build step in MW could solve this too)

@Catrope do you see any benefit in moving the styles into a completely separate package (Codex vue components would still contain their own supplemental styles as needed)? I guess we can try to do this in the main Codex package first and then see if we need a separate package for codex-styles / codex-css down the road.

What would all of this mean for users who are just using Codex Vue components with a build tool outside MediaWiki? Ideally they should be able to import a Vue component and get both the base and interactive styles without having to do anything else.

What would all of this mean for users who are just using Codex Vue components with a build tool outside MediaWiki? Ideally they should be able to import a Vue component and get both the base and interactive styles without having to do anything else.

Users of Codex components outside of MediaWiki already need to load the Codex CSS file themselves (styles are output as a separate file in Vite library mode) from the /dist folder in the NPM package.

I was originally imaging a system like this:

  • Separate codex-css package with its own build config (could use Vite or something else) – all the various LESS files would get compiled into a single stylesheet.
  • Continue to build component override styles alongside the Codex JS in the main Codex workspace. These styles would need to be loaded in addition to the above stylesheet if the JS-enabled components were being used.

But maybe we can do this without needing a separate sub-package if we provide the appropriate Vite config.

@Catrope do you see any benefit in moving the styles into a completely separate package (Codex vue components would still contain their own supplemental styles as needed)? I guess we can try to do this in the main Codex package first and then see if we need a separate package for codex-styles / codex-css down the road.

I don't think there are major benefits to either approach. To me it's more about "is this conceptually a separate thing, and should we publish it to NPM as such". When CSS-only component mature we may well decide that they are, but maybe for now we can start out without a separate package?

OOUI splits its styles into a few groupings so that developers can ship a smaller style payload. We could consider doing something similar for Codex styles as well. However, I'm wondering if there is another way we could do this, similar to how we handle Codex Icons in MediaWiki.
In MW we have a ResourceLoader PHP method called getIcons which can take arguments that specify which icons are needed. Only data for those icons is delivered to the browser. Usage looks like this:

{
	"name": "resources/icons.json",
	"callback": "MediaWiki\\ResourceLoader\\CodexModule::getIcons",
	"callbackParam": [
		"cdxIconAlert",
		"cdxIconCheck",
		"cdxIconClose",
		"cdxIconError"
	]
}

Could we do something similar for Codex styles, with some kind of getStyles method?

{
	"name": "resources/codex.css",
	"callback": "MediaWiki\\ResourceLoader\\CodexModule::getStyles",
	"callbackParam": [
		"cdxButton",
		"cdxIcon",
		"cdxSelect"
	]
}

Styles for the desired components would then get concatenated into a single CSS file via PHP.

Change 865840 had a related patch set uploaded (by Eric Gardner; author: Eric Gardner):

[design/codex@main] [PoC, Do not merge] Example codex-buttons package

https://gerrit.wikimedia.org/r/865840

egardner changed the task status from Open to In Progress.Dec 9 2022, 6:07 PM
egardner claimed this task.
egardner triaged this task as High priority.
egardner set the point value for this task to 3.

Our proposed implementation strategy is now documented in the parent epic (T321351), so I'm closing this spike.