Page MenuHomePhabricator

[EPIC] Address themeability needs for Codex components
Open, Needs TriagePublic

Description

Goal

Codex is set out to be themeable in order to

  1. be used outside of Wikimedia context and
  2. be adaptable within Wikimedia for different use cases like MediaWiki skins.

Clarify the use cases and find technical abilities and limitations.


Existing theme documentation (design/development)
  • Wikimedia Design Style Guide: Theming has not yet been made a part of the Design Style Guide.

Only existing documentation in Wikimedia context is around

  • MediaWiki skins (a historic mess) and
  • OOUI themes
    • (three out of box, template Default, WikimediaUI – the DSG derived and Apex – used in MW Monobook skin)
Existing implementations
Wikimedia

MediaWiki Core
Additionally to skins, different folks including myself, @Volker_E have worked in the past on a low-level framework to bring central style decisions to skins:

  • mediawiki.skin.defaults.less – definition of all agreed on stylistic pieces to provide to skins. If a var isn't defined here, it's not supported to be replaced by RL in the skin
    • mediawiki.skin.variables (empty fallback pointing at mediawiki.skin.defaults.less for skins that don't define variables file themselves)

MediaWiki Skins

  • mediawiki.skin.variables (currently used by Vector and MinervaNeue, copies of each other with Wikimedia Design Style Guide/Codex equivalent values). This path could provide us with a low-level way to have a big number of Codex design tokens used in various (Less) places. It is a Less variables carrying file, where we should inject our Codex design tokens. It needs to exist per skin, otherwise the fallback value is featured.

OOUI:

  • OOUI provides the two themes, which a skin can choose from. Monobook uses Apex theme, Vector, MinervaNeue and all others WikimediaUI.

The themeability of OOUI is technically burdensome and outside of the default theme provided, no other themes have been worked on in the years of its existence.

Open questions

1. How should our theming approach be? rigid or flexible?

Design System talked about what theming approach to follow. We've agreed that it won't be goal-oriented to provide an everything goes path in Codex. We don't want theme to support major UX issues, like fully broken minimum interaction sizes/touch areas.

2. Is the technical path of JSON tokens and Style Dictionary provided sufficient?

Currently we end up with different CSS/stylesheet files that need to be set/replaced when using/changing themes. Or chained together in case of theme overrides.
Alternatives:

  • Generated styles via JS, which Vue 3 supports, but it would be difficult with a server-rendered UI
  • Include themes as Vue plugins

Implementation notes and challenges

Developer/themer experience
Default theme with other theme featuring overrides
As theming is a burdensome and error-prone activity, we assume that providing a default theme as fallback and theme overrides might be the designer/developer friendliest solution.
We don't want to end up in a situation, where a themer needs to keep 5000 attributes/tokens in sync with the main theme and has to provide decisions for each and everyone.
Premise, that most needs are covered by changing colors (or gradients) and possibly other visual attributes like border size/border radius
Possible way out is to work with an inheritance model, having theme modifications only be applied on top of a default theme.
Pro

  • Themers are freed up from caring about a large number of visual attributes

Con

  • As long as you don't define all properties you can't achieve a clean slate, meaning there will be default visual decisions lurk into override theme. That on the backside would mean we should consider a default (orienting on CSS and HTML defaults) looking theme to start with.

Related Objects

Event Timeline

Catrope added a subscriber: Catrope.

Reviving this old task with some thoughts about what it would take to have more than one theme in Codex:

In the tokens package, we would have to:

  • Add a theme-foo.json file in codex-design-tokens/src/, whose tokens have the same names as the ones in theme-wikimedia-ui.json but different values
  • Configure Style Dictionary to build dist/theme-foo.{css,less,scss} files using the tokens from theme-foo.json instead of the ones from theme-wikimedia-ui.json.
  • Rename dist/index.json to dist/theme-wikimedia-ui.json, and generate dist/theme-foo.json for each theme

In the components (Codex) package, we would have to:

  • Build different versions of dist/codex.style.css (and codex.style-rtl.css), one for each theme, e.g. codex.theme-foo.css
    • One way to make this work could be to run Vite multiple times, once for each theme, with the component files importing the tokens from a virtual path that is aliased to the theme being built
  • Alternatively, or in addition, we could build a dist/codex.style.css that doesn't have CSS variables substituted (so it contains CSS like color: var( --color-base );), and have library consumers use that file along with one of the theme-foo.css files that provides the variables
    • We could build this by running Vite only once, with the component files importing a dummy tokens file that really just maps LESS variables to CSS variables, like @color-base: var( --color-base );
    • We could also use this approach to build CSS files for each theme without having to run Vite multiple times: run Vite once to produce a CSS files that uses CSS variables, then repeatedly run postcss-custom-properties to substitute the variables and generate CSS files for each theme. I think that would be better than the naive approach of running multiple Vite builds, and it would hopefully also make switching themes on the demo site easier.

In the docs package, we would have to:

  • Design a theme selector that changes the theme of the component demos (similar to how the LTR/RTL switcher changes the directionality of the component demos)
  • Find a way to make the styling change to the right theme when the user selects a different theme using the theme selector
    • The best way I can think of would be to have the demo site load the component styles with unsubstituted CSS variables, and then apply different values for those variables based on a CSS class on the demo wrapper that indicates the theme, e.g. .cdx-demo-wrapper-theme-wikimedia-ui { --color-base: #202122; } .cdx-demo-wrapper-theme-foo { --color-base: #333; }.
    • This is inspired by an example of a theme selector in IBM's Carbon design system that Bárbara found, which does essentially that but with a data attribute (they use body[data-live-preview-theme="foo"], we would probably use .cdx-demo-wrapper[data-theme="foo"]).

There's an implementation relation to T297000: Define the design system's size and spacing scale on legacy token font size implementation, which are an anchor point for themability in current environment.

ldelench_wmf moved this task from Inbox to Needs Refinement on the Design-System-Team board.

Change 856706 had a related patch set uploaded (by Catrope; author: Catrope):

[design/codex@main] [WIP] tokens: Add basic theme system

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

Developer/themer experience
Default theme with other theme featuring overrides
As theming is a burdensome and error-prone activity, we assume that providing a default theme as fallback and theme overrides might be the designer/developer friendliest solution.
We don't want to end up in a situation, where a themer needs to keep 5000 attributes/tokens in sync with the main theme and has to provide decisions for each and everyone.
Premise, that most needs are covered by changing colors (or gradients) and possibly other visual attributes like border size/border radius
Possible way out is to work with an inheritance model, having theme modifications only be applied on top of a default theme.

I think that these are all important points to keep in mind. Ideally, I think we should aim for our theming experience to be similar to what Bootstrap or Bulma provides.

I think that in some theming scenarios, a theme author might just want to swap out some choices from within the existing set of pre-defined options. Like say a dark-mode theme: such a theme might replace the base background color value with something from another part of the already-defined grayscale spectrum. A legacy Vector or Minerva theme might work this way as well.

It would be great if a theme author could just provide a small JSON file of overrides in such situations, without having to copy over every single token in the theme-wikimedia-ui.json file.

On a related note, should the theme-wikimedia-ui.json file be renamed to something like theme-default?

ldelench_wmf renamed this task from Clarify themeability needs and technical questions for Codex to [EPIC] Address themeability needs Codex.Dec 19 2022, 10:52 PM
Volker_E renamed this task from [EPIC] Address themeability needs Codex to [EPIC] Address themeability needs for Codex components.Feb 22 2023, 1:57 PM

Reviving this old task with some thoughts about what it would take to have more than one theme in Codex:

In the tokens package, we would have to:

  • Add a theme-foo.json file in codex-design-tokens/src/, whose tokens have the same names as the ones in theme-wikimedia-ui.json but different values
  • Configure Style Dictionary to build dist/theme-foo.{css,less,scss} files using the tokens from theme-foo.json instead of the ones from theme-wikimedia-ui.json.

To preserve the historical record, I don't want to edit this comment from almost a year ago, but I should add here that I've since realized that it's not this simple. T325237#8700501 goes into a little more detail, but briefly: it's not the case that to make a different theme, you would only have to change the values in theme-wikimedia-ui.json but not those in codex-base.json. The filenames imply that, but it's not true. We would need to do more to figure out how this would work, and how we should organize our files and directories differently.

  • Rename dist/index.json to dist/theme-wikimedia-ui.json, and generate dist/theme-foo.json for each theme

We did this rename in https://gerrit.wikimedia.org/r/c/design/codex/+/864866 . Also, in https://gerrit.wikimedia.org/r/c/design/codex/+/869274 we started building two versions of every output file, one set named theme-wikimedia-ui.{less,css,scss,json} and one set named theme-wikimedia-ui-legacy.{less,css,scss,json}. These aren't really different themes, they're different variants of the same theme with a different basePxFontSize setting, but for this purpose they function as different themes.

In the components (Codex) package, we would have to:

  • Build different versions of dist/codex.style.css (and codex.style-rtl.css), one for each theme, e.g. codex.theme-foo.css
    • One way to make this work could be to run Vite multiple times, once for each theme, with the component files importing the tokens from a virtual path that is aliased to the theme being built

We did this in https://gerrit.wikimedia.org/r/c/design/codex/+/869808: we now build separate versions of these files for wikimedia-ui and wikimedia-ui-legacy, using exactly this approach.

  • Alternatively, or in addition, we could build a dist/codex.style.css that doesn't have CSS variables substituted (so it contains CSS like color: var( --color-base );), and have library consumers use that file along with one of the theme-foo.css files that provides the variables
    • We could build this by running Vite only once, with the component files importing a dummy tokens file that really just maps LESS variables to CSS variables, like @color-base: var( --color-base );
    • We could also use this approach to build CSS files for each theme without having to run Vite multiple times: run Vite once to produce a CSS files that uses CSS variables, then repeatedly run postcss-custom-properties to substitute the variables and generate CSS files for each theme. I think that would be better than the naive approach of running multiple Vite builds, and it would hopefully also make switching themes on the demo site easier.

In the long term, I'd like to go in a direction similar to this. I think we should build what's described above (a version of the Codex styles with the token unsubstituted), but in addition to a CSS version, we should also build Less and SASS versions. These files would have the Codex Less rules compiled to CSS, but with the variables still there as CSS/Less/SASS variables (e.g. .cdx-button:enabled { color: @color-base; } or .cdx-button:enabled { color: var( --color-base ); }). This would then enable MediaWiki (and other Codex consumers) to change the values of individual tokens. In MediaWiki, we'd load the Codex tokens as Less variables, allow them to be overridden by skins through the skin variables system, then load the Less build of the Codex styles. We would also still build a regular CSS version of the Codex styles for each theme, for library consumers who would rather avoid this complexity.

However, we'll have to do some exploration to see how we would generate builds with unsubstituted variables like this, and how feasible that is. No off-the-shelf tools appear to exist for this. We could try to hack it with things like @color-base: ~'var( --color-base )'; or @color-base: ~'@color-base';, but that may not work in some situations, or may impose constraints on how we can write our Less code that may be difficult to work with.

CCiufo-WMF lowered the priority of this task from Medium to Low.Sep 8 2023, 11:18 PM
CCiufo-WMF raised the priority of this task from Low to Needs Triage.Jan 16 2024, 3:26 PM