Jump to content

User:EGardner (WMF)/drafts/Codex

From mediawiki.org

Codex is the design system for Wikimedia. As a system, Codex is made up of several distinct elements which may be used separately or together. These elements include: design tokens, icons, and UI components. Codex is bundled within MediaWiki, and is also available as a series of NPM packages.

The current version of Codex is 1.5.0.

Codex's source code is hosted on Gerrit, and its development is tracked in Phabricator.

Basic Usage

[edit]

Codex provides a variety of components which skin, extension, and userscript authors can embed in their own user interfaces: buttons, checkboxes, toggle switches, dialogs, etc. Many of these components can be extensively customized.

A full list of current Codex components (along with documentation and interactive demos) can be found here.

CodexExample MediaWiki extension

[edit]

The Design System Team maintains the CodexExample MediaWiki extension for demonstration purposes. This extension can be installed (it sets up a dedicated special page called `Special:CodexExample` with live demos) or you can study its source code for inspiration. See the project README page for more information.

Usage With JavaScript

[edit]

Codex components are built using the Vue.js JavaScript framework. If you are developing a Vue application in MediaWiki, then it's easy to load Codex components from ResourceLoader using require(). You can load all of Codex at once, or just a limited subset of components.

Loading the entire library (recommended for use in userscripts)

[edit]
"ext.myExtension.foo": {
	"dependencies": [ "@wikimedia/codex" ]
	"packageFiles": [
		"init.js",
		"MyComponent.vue"
	]
}
<!-- MyComponent.vue -->
<template>
    <cdx-button @click="doSomething">Click me!</cdx-button>
</template>

<script>
const { CdxButton } = require( '@wikimedia/codex' );

module.exports = exports = {
    name: "MyComponent"
    components: {
        CdxButton
    },
    methods: {
        doSomething() {
            //...
        }
    }
}
</script>
Loading a subset of Codex components (recommended for skins and extensions)
[edit]

To only load a limited set of components, you can declare your dependencies in the following way below:

"ext.myExtension.foo": {
    "class": "MediaWiki\\ResourceLoader\\CodexModule",
	"packageFiles": [
		"init.js",
		"MyComponent.vue"
	],
    "codexComponents": [
        "CdxButton",
        "CdxCard",
        "CdxDialog"
    ]
}

This will generate a virtual file, codex.js, in your resources directory with the exports you need. You can then require the components you requested from that virtual file:

// In resources/ext.myExtension.foo/MyComponent.vue
const { CdxButton, CdxTextInput } = require( '../codex.js' );

See the CodexExample repository for more in-depth example of how to use Codex in a MediaWiki extension.

Usage Without JavaScript (CSS-only Codex components)

[edit]

Many Codex components also support "css-only" usage. These components should appear visually identical to their JS-enabled counterparts, but they will offer more limited behavior.

Loading component styles
[edit]

You can load the styles of a limited subset of Codex CSS components in the same way as you would for JS components, above. If you only need the styles, you can add the "codexStyleOnly": "true" option when you define your module.

"ext.myExtension.foo": {
	"class": "MediaWiki\\ResourceLoader\\CodexModule",
	"styles": "ext.myExtension.foo/styles.less",
	"codexStyleOnly": "true",
		"codexComponents": [
			"CdxButton",
			"CdxCard",
			"CdxCheckbox",
			"CdxProgressBar"
	]
}
Providing component markup
[edit]

To use CSS-only Codex components, just ensure that the appropriate styles are loaded and then add the necessary markup to your page. For now, you'll have to do this by hand. You can find example markup in the "CSS-only usage" section on a component's documentation page (here is the markup for the Button component).

<div>
  <button class="cdx-button cdx-button--action-default">Default button</button>
</div>

<div>
  <button class="cdx-button cdx-button--action-progressive">
    Progressive button
  </button>
</div>

<div>
  <button class="cdx-button cdx-button--action-destructive">
    Destructive button
  </button>
</div>

Advanced Usage

[edit]

Using a limited subset of components

[edit]

The @wikimedia/codex ResourceLoader module provides the entire Codex library – all components, styles, etc. If you are developing a skin or an extension and you care about performance, you should consider using Codex's code-splitting feature. ResourceLoader allows you to specify a list of Codex components and load only the JS/CSS for those components plus their dependencies.

To use this feature, define a custom ResourceLoader module (this is typically done in skin.json or extension.json) and specify a list of codexComponents:

"ext.myExtension.blockform": {
    "class": "MediaWiki\\ResourceLoader\\CodexModule",
    "codexComponents": [
        "CdxButton",
        "CdxCard",
        "CdxDialog",
        "CdxIcon",
        "CdxRadio",
        "CdxTextInput",
        "useModelWrapper"
    ],
    "packageFiles": [
        "init.js",
        "BlockForm.vue"
    ],
    "messages": [
		"block-target",
		"ipb-submit"
	]
}

This will generate a virtual file, codex.js, in your resources directory with the exports you need. You can then require the components and composables you requested from that virtual file:

// In resources/ext.myExtension/BlockForm.vue
const { CdxButton, CdxTextInput } = require( '../codex.js' );

If you only need CSS-only components and don't wish to load the component JavaScript, you can add "codexStyleOnly": true to the module definition.

Similarly, if you only need the JavaScript files and not styles, you can add "codexScriptOnly": "true". You should only do this if you're putting the styles in another, style-only module as described above.

Using Codex Icons

[edit]

For performance reasons, there is no codex-icons ResourceLoader module containing all the icons from Codex. Such a module would be large and wasteful, since most users of Codex only need a handful of the 200+ icons. Instead, ResourceLoader provides a way for modules to embed the icons they need, similar to the code-splitting approach described above.

{
    "name": "icons.json",
    "callback": "MediaWiki\\ResourceLoader\\CodexModule::getIcons",
    "callbackParam": [
        // List the icons your module needs here, e.g.:
        "cdxIconArrowNext",
        "cdxIconBold",
        "cdxIconTrash"
    ]
}

Using design tokens directly

[edit]

Design tokens can be imported into LESS stylesheets as variables. This may be useful if you are developing your own components or styles and want them to integrate with Codex.

Codex design tokens should be imported from the mediawiki.skin.variables.less file.

@import 'mediawiki.skin.variables.less';

.my-feature {
    color: @color-base;
    background-color: @background-color-base;
}

For a full list of Codex's design tokens, broken down by category, see here.

Codex LESS mixins

[edit]

Some Codex functionality is implemented using Less mixins. For example, the Link component is a Less mixin rather than a Vue component, and using icons in CSS-only components requires using a Less mixin (see also the documentation for using CSS-only components below).

Using Codex Less mixins in MediaWiki and extensions works very similarly to using design tokens: simply import 'mediawiki.skin.variables.less', which makes all Codex mixins available, as well as the design tokens.

@import 'mediawiki.skin.variables.less';

.my-feature {
    a {
        .cdx-mixin-link-base();
    }
}

Using Codex in Userscripts

[edit]

It is possible to use Codex in userscripts. However, there are some limitations that will require certain workarounds. Here are a few considerations to keep in mind when using Vue and Codex in userscripts:

  • No .vue single-file component support; you must define components in plain JS files
  • Everything needs to live in one file; userscripts don't provide a good way to load custom modules
  • Define component templates using ES6 template literals
  • Prefer global component registration for Codex components

Loading Vue/Codex

[edit]

You'll need to load Vue and Codex from ResourceLoader. The best way to do this is via mw.loader.using; the rest of your userscript code should live in a callback or promise chain.

mw.loader.using( '@wikimedia/codex' ).then( function( require ) {
    const Vue = require( 'vue' );
    const Codex = require ( '@wikimedia/codex' );
} );

Use Vue.createMwApp

[edit]

Once you've loaded Vue and Codex, you must define a Vue app and mount it somewhere on the page. The exact location will vary depending on what you are trying to do. You can use MediaWiki's custom createMwApp method for this.

mw.loader.using( '@wikimedia/codex' ).then( function( require ) {
    //... require Vue and Codex as above
    // create an element to mount the Vue app
    const mountPoint = document.body.appendChild( document.createElement( 'div' ) );
    // create a Vue app and mount it to the target element
    Vue.createMwApp( {
        // data, computed props, methods, etc. go here
    } ).mount( mountPoint );
} );

A complete example

[edit]

The link below shows a complete example of a userscript which adds a portlet link to all Wiki pages that triggers a custom Codex Dialog component to launch when clicked. Feel free to copy this script to your own user page to use as a starting point.

https://en.wikipedia.org/wiki/User:EGardner_(WMF)/codex-hello-world.js

Release cycle

[edit]

A new version of Codex is released every other Tuesday. When a new release is created, a patch is also submitted to MediaWiki core to use that new release. Since this is done on Tuesdays, the update to core will be deployed the following week (the next time the deployment train runs).