What is it?
CSS-only components would be a group of Codex components that are implemented solely by creating standard, shared styles for each component, meaning JavaScript would not be required to use them. For example, to use a CSS-only Message component, a developer would either write or generate the markup for the message, then apply the shared styles to their markup, resulting in a no-JS message that has visual consistency with the Vue version of the Message component.
Currently, nearly all Codex components are implemented as Vue.js components, meaning they require JavaScript running on the client to work. Eventually, we hope to have a solution for rendering Vue components on the server (server-side rendering, or SSR), but this will take time, and there still may be use cases for no-JS versions of Codex components even if we have full SSR.
What does it block?
Some WMF teams may be able to start using Codex (and, therefore, the current and canonical design system) if they have access to these components while they're waiting on SSR. Existing uses of deprecated libraries (like mediawiki.ui) cannot be removed until there's something else to migrate to.
Impact
End users:
- Users without JavaScript can see and use UIs that have a consistent design that matches our canonical design system, across wikis
- Users with slow connections can see and start to use visually consistent UI components while they await the fully interactive JS version of the UI to load
- All users benefit from simple UIs that can be rendered on the server, which load quickly and provide a consistent experience
- Non-WMF wikis have a way to provide consistent server-rendered UIs without having to set up SSR
- Easily available API could mean misuse on-wiki, e.g. applying interface styles to content, or more potential for breaking visual changes due to high usage of CSS classes
Engineers:
- While they await an SSR solution, or in situations where SSR cannot be used, engineers can use CSS-only Codex components to style server-rendered elements, enabling them to use the current design system and reducing the need for custom code.
- Engineers with no JavaScript or Vue experience can build UIs with Codex components
- We can eventually deprecate old, inconsistent parts of our systems in favor of a single, centralized set of styles, leading to lower maintenance costs, less confusion, and easier onboarding of new developers
- Abstracting styles out of Vue components will mean having a set of implementation-agnostic base styles that could be applied to a new implementation in the future (e.g. React or a new framework)
- Development and maintenance costs: building this set of components will require time from DST and other teams to develop and test
Designers:
- Since styles are shared between CSS-only and Vue components in Codex, designers will have less to review when adding or changing components
- Once we deprecate old systems, designers will have fewer things to keep track of and work on, leading to greater velocity on the current system
- Designers who know HTML and CSS can use CSS-only components for rapid prototyping or to make code changes for production UIs
Proposed implementation strategy
Proposal
- Use cdx- classes for styling ("Bootstrap-style" approach). We are already writing BEM-style CSS inside our Vue components, so we don't need to make any changes here.
- Continue writing styles within Vue single-file components (SFCs). Where necessary, components will make distinctions between two sets of styles. The first is styles that only appear in the CSS-only version, and styles that only appear in the Vue version (both SSR-ed and mounted). We can distinguish these styles by adding -vue to the relevant classes to target Vue-only styles. The second is styles that only appear in a no-JS environment, and styles that only appear in a JS-enabled environment. We can distinguish these styles by adding a --js-on or --mounted class suffix when the component is mounted. Finally, we will add a ref to most components that will be set to true on (or right before) mount, via a composition function, which can be used to conditionally show different markup depending on whether the SSR-ed component has been hydrated or not (this last part could be done in the future during SSR implementation).
- Define a series of new Codex sub-packages which are intended for use within MediaWiki. Each new package will group together a set of related components (maybe corresponding to the component groups we were considering adding to the documentation site). This will work similar to how we are handling codex-search currently. Each new package will get built in Vite’s library-mode, so a single JS and CSS file gets built with only the data for the relevant subset of components. An example of this can be seen in this patch, which creates a demo codex-buttons package: https://gerrit.wikimedia.org/r/c/design/codex/+/865840
- Each Codex sub-package gets its own ResourceLoader module containing only its CSS and JS files. Users only need to load the packages corresponding to the components they need in their feature.
- Load CSS and JS separately: There are two ways we can do this.
- We could update ResourceLoader so that a user can get only the styles, or only the scripts, or both, when using a module that contains both CSS and JS. Currently this is only possible through an annoying work-around.
- Alternatively, we will have to do this work manually. Each Codex sub-package would need to have two modules in MW – one containing only the CSS, i.e. codex-buttons-styles, and one containing only the JS files, i.e. codex-buttons. The latter package would depend on the former, but the former can be used without the latter.
- Interaction with SSR: In the future, we expect that a more robust SSR system will be available in at least some situations. SSR-based features would basically create their own bundle instead of relying on the pre-split ones that we’d be providing here. Such builds will depend on the full Codex package (which contains all CSS and JS code). The SSR tooling will be able to tree-shake out all unnecessary code for these features, so the regular Codex modules will not be needed at all.
Acceptance criteria
- Most Codex components should have a CSS implementation provided alongside the Vue implementation (unless there is a good reason not to provide one – Dialog will likely be JS only for example)
- CSS and Vue versions of the same component would share styles and class names; the Vue version of the component would expect for the appropriate CSS styles to already be present on the page
- Visual parity between CSS / JS implementations of a component should be the goal in most cases
- Seamless progressive enhancement (where CSS components get replaced with JS-based ones shortly after the page loads) is another goal; Ideally user would not notice until they interact
- CSS and JS versions of the same component should be able to coexist on the same page without any issues;
- Component styles should never be double-loaded; If a CSS Button is being used on a page which is progressively enhanced, the JS code should not duplicate the styles which are already present
- Users should not need to load all of Codex when they only need one or two components. This applies equally for CSS and JS use-cases.
- It should be possible to load CSS components independently of their JS counterparts
- JS component module(s) should have the CSS equivalent as a dependency; this ensures that in non-progressive-enhancement scenarios (i.e. feature requires JS to work), developers who load the Vue components will also load all relevant styles
Future work
In the system described here, users of CSS-only components would still be responsible for authoring the proper markup for their UI. This will likely be somewhat tedious for all but the simplest features. We could make this experience a lot better by providing one or both of the following:
- Better templating language support on the server; it would be great if template authors could just include a {% cdx-button %} partial that can take arguments corresponding to component properties
- Alternatively, a PHP interface could be provided similar to how OOUI-PHP works currently
Feasibility of this approach
Aside from the potential (small-ish) changes to ResourceLoader (see point 4 above), we can do all of this now. And actually most of the styles we need already exist in the Vue components themselves. Most of the work would just be a matter of reorganizing some of our current code so that it can be used in multiple ways going forward.
Risks/Trade-offs
- This implementation strategy will allow us to keep writing our component styles within the Vue SFCs, which is nice from a familiarity and developer productivity approach.
- We would need to create some additional packages inside the Codex repo, but this will mostly be a one-time task. As we add new components to Codex we'll need to include them in one of the existing sub-packages or (more rarely) create a new one. But little additional code is required here and once a sub-package has been set up it will rarely change. These will be included in each release just like codex-search is (though we may not need to publish them on NPM since they are intended only for use in MW). See https://gerrit.wikimedia.org/r/c/design/codex/+/866509/ for an example of what this would look like.
- CSS-only implementations of components will need to be tested; VueTest is a good place to do this (maybe a new sub-page of that extension can be created for the CSS-only components)
- We'll need to create several additional ResourceLoader modules (especially if we cannot come up with a way to get just the CSS or JS from a module that contains both). But like in the case of the additional Codex packages, most of the work here is a one-time effort.
- We might want to include some easily copy-pastable markup snippets for users in the absence of a good way to package up CSS-only components in mustache templates or PHP.
Overall we think this approach is pretty low-risk, and doesn't rely on any speculative or complex new technical work.
Phases of work and owners
- Strategy
- Owner: Anne/Eric.
- To do:
- Choose an implementation path (done, proposed above)
- Decide on an MVP implementation and set of components (done, proposed above and in T325105 )
- Determine how to document components (done, see T325105)
- Determine testing strategy
- Get feedback from major stakeholders on implementation plan
- Consider documenting any standards that come out of this research, like prioritizing native browser elements/styles when possible, or building the CSS-only version of a new component first before the Vue verison
- Build CSS-only versions of components (we are here).
- Owner: Anne.
- To do:
- Build MVP set of components. Start with high priority, then move through medium and low (covered by T325105)
- Work with QTE to test components as they're developed
- Work with other teams to test/validate specific components
- Enable use of components within MediaWIki.
- Owner: Eric.
- Validate proposal with performance team
- Define sub-packages and create RL modules
- Enable loading of CSS and JS separately
- Application of CSS-only components.
- Owner: DST + Web + Others?
- To do:
- Strategize with teams and volunteers on how they can use CSS-only components and migrate from deprecated libraries
- Document a migration plan for legacy libraries?
Cross-team dependencies
- Web, Growth: These teams will be a major consumers of these components and should be involved in strategy and testing at least.
- Performance: We may need to get the Performance team's feedback on any changes that may impact performance.