手册:如何制作MediaWiki皮肤

This page is a translated version of the page Manual:How to make a MediaWiki skin and the translation is 29% complete.
Outdated translations are marked like this.

制作皮肤是了解并熟悉MediaWiki内部机制,并开始为维基媒体运动贡献自己力量的绝佳方式!

如果你熟悉CSS、JavaScript、JSON等前端技术,你就可以制作MediaWiki皮肤! 制作Mediawiki皮肤不需要了解PHP,但如果需要做更高级的功能,PHP可以起到帮助作用。

虽然并不需要精通以下知识,但如果您熟悉LESS CSS,这将帮助您制作皮肤。 本教程假定您有一份已安装且可正常工作的MediaWiki副本,并且正在运行当前最新开发版本,若尚不满足,建议先去准备一下。

本指南不适合使用PHP BaseTemplate制作皮肤的用户。 请参阅手册:如何将基于SkinTemplate的skin/Migrating迁移到SkinMustache

准备

熟悉Mustache模板后,皮肤的开发会简单很多。

至少,您应该熟悉以下示例中描述的Mustache基础知识:

{{ str }} <!-- renders escaped string -->
{{{ html-str }}} <!-- renders RAW HTML -->

<!-- accesses data inside an associative array of the form { 'key' => 'hello' } -->
{{#array-data}} 
{{ key }} <!-- renders hello -->
{{/array-data}}

<!-- for boolean values -->
{{#is-talk-page}}
<!-- conditional rendering if is-talk-page is true -->
{{/is-talk-page}}

皮肤负责呈现用户可见内容,即BODY标签内的HTML。 皮肤被限制编辑HEAD内的任何内容。 这部分的代码将自动生成给皮肤,这样皮肤可以专注于呈现内容的功能。 如果您需要修改HEAD,请使用Hooks、扩展或配置来完成,因为这被视为皮肤的职能之外。请参阅常见问题解答,了解最佳实践是如何做的。

入门

要制作你的第一款皮肤,我们推荐以下两个选项。

选项1:分叉一份示例皮肤

示例皮肤 https://github.com/wikimedia/mediawiki-skins-Example 提供了一款皮肤需要实现的基本骨架。 克隆仓库到您的皮肤文件夹,确保文件夹名称为“Example”,并添加以下代码到您的LocalSettings.php中:

wfLoadSkin( 'Example' );

若一切顺利,您的皮肤应该会在您wiki的Special:Preferences上显示。

选项2:使用皮肤实验室

皮肤实验室允许您使用基础的CSS和模板建立皮肤。 在调整完毕后,你可以点击“下载为ZIP(download as ZIP)”,网站会自动为您编译所需的皮肤模板。 希望生成的皮肤仓库对你来说还算简单。 在你下载完ZIP后,将其放入你的MediaWiki皮肤文件夹,并用以下代码更新您的LocalSettings.php:

wfLoadSkin( 'FolderName' );

若一切顺利,您的皮肤应该会在您wiki的Special:Preferences上显示。

选项3:从命令行

cd mediawiki/skins

npm install mediawiki-skins-cli

npx create-mw-skin SkinName

cd SkinName

在你下载完ZIP后,将其放入你的MediaWiki皮肤文件夹,并用以下代码更新你的LocalSettings.php:

wfLoadSkin( 'SkinName' );

After installing navigate to MediaWiki with ?useskin=skinname on the URL to see your skin.

To explore the data available to the Mustache template add ?useskin=json (example).

广而告之!

和更多的人制作你的皮肤会更有趣也更容易!在你的皮肤有了一个可以用的原型之后,可以考虑发布到GitHub或Gerrit 。 一旦把代码公开,你应该 创建一个皮肤页面(确保修改标题!)来让人们知道你愿意合作!

创建一个wiki页面也有很多其他好处。 您可以在Phabricator或GitHub issue中处理错误报告,并从MediaWiki社区的其他志愿者那里接收补丁。 也会有人可以帮助你设置好翻译。

Mustache

在1.35版本中我们为皮肤加入了Mustache的支持。 我们发现在开发Skin:MinervaSkin:Vector皮肤时使用Mustache非常有帮助,因为它提供了数据和呈现分离的功能。 Mustache partial(插值模板/子模块)可用作皮肤的可重用部分。 如果要在MediaWiki中使用部分Partial.mustache,那么只需将它们添加到你正在工作的文件夹,并在主模板skin.mustache中使用{{>Partial}}引用。

发送至Mustache模板的数据相对比较灵活。 如果有些内容缺失了,你可以使用PHP通过扩展SkinMustache ::getTemplateData函数来添加这些数据。

你可以把SkinJson皮肤添加到MediaWiki的开发实例中,以检查皮肤可用的数据。 请注意,为了帮助您理解正在处理的数据,在这里,数组以 "array-" 为前缀,布尔值以 "is-" 为前缀,对象以 "data-" 为前缀。

The data available, and in which MediaWiki versions it is available is documented on SkinMustache.php.

让皮肤文本可被翻译(国际化)

在ValidSkinNames下的skin.json中,你可以使用messages选项来定义可翻译的消息键。 These keys should correspond to entries inside the i18n/en.json folder. Once registered for the skin, these will be available in your template with a prefix msg-.

"example": {
	"class": "SkinMustache",
	"args": [ {
			"name": "example",
			"messages": [
				"sitetitle",
				"search",
				"tagline",
				"navigation-heading"
			]
	} ]
}

For example in the example below, the message "sitetitle" can be rendered in a Mustache template using:

<p>{{msg-sitetitle}}</p>

See Localisation for more information on why this is important.

Rendering template partials

Template partials can be used to render different parts of the skin and to avoid the problem with having a large unmaintainable skin.mustache file. In the following example, the skin renders the contents of the templates in the 3 files with the filenames Logo.mustache, Content.mustache and Footer.mustache. These files must exist in the same folder as the skin.mustache file or a subfolder of the directory containing skin.mustache.

{{>subfolder/Logo}}
{{>Content}}
{{>Footer}}

Template partials are a great way to break up your skin to make it more readable. No template partials are predefined and available by default, however you can look at existing skins using SkinMustache for inspiration.

Read more about Mustache template partials.

Logo.mustache

Following code block is being used to show the site logo in the Example skin and you will also see the same if you create the skin from the SkinLabs.

<!-- Logo.mustache -->
<div id="p-logo" class="mw-portlet" role="banner">
    <a href="{{link-mainpage}}">
    {{#data-logos}}
        {{#icon}}<img src="{{.}}" width="40" height="40">{{/icon}}
        {{#wordmark}}<img src="{{src}}" width="{{width}}" height="{{height}}">{{/wordmark}}
        {{^wordmark}}<h1>{{msg-sitetitle}}</h1>{{/wordmark}}
    {{/data-logos}}
    </a>
</div>

From the code block mentioned above, the following line is responsible to show the logo `icon`:

{{#icon}}<img src="{{.}}" width="40" height="40">{{/icon}}

This line assumes that, there is a key icon in the array $wgLogos . So in your LocalSettings.php file, if there is a line similar as $wgLogos = [ 'icon' => "$wgResourceBasePath/resources/assets/wiki.png" ];, then the logo/icon image will be displayed. The default MediaWiki LocalSettings.php exports a 1x key in the $wgLogos array.

So to show the logo you need to update the LocalSettings.php and add a key icon.

If you want to change the logo do not just replace the default logo with a new one in the resources/assets/wiki.png path. Because it will be changed to default, when you update the MediaWiki. The recommended way to change the logo is to add a new logo image file and add that path to the LocalSettings.php.

Rendering menus with Mustache

All menus are structured in the same format (PortletData). A generic Menu.mustache template partial, added to the same folder as skin.mustache can therefore be used to generate a menu.

<nav id="{{id}}" class="{{class}}" title="{{html-tooltip}}"
    aria-labelledby="{{id}}-label">
    <input type="checkbox" aria-labelledby="{{id}}-label" />
    <h3 id="{{id}}-label" {{{html-user-language-attributes}}}>{{label}}</h3>
    <div class="mw-portlet-body">
        <ul {{{html-user-language-attributes}}}>
            {{{html-items}}}
        </ul>
        {{{html-after-portal}}}
    </div>
</nav>

However, when using this partial you'll need to tell it which menu to generate HTML for.

Inside skin.mustache the following would render the languages and views menu.

{{! Switch template context to data for all menus }}
{{#data-portlets}}
    {{! Access the menu called "languages" }}
    {{#data-languages}}
    {{>Menu}}
    {{/data-languages}}
    {{! Access the menu called "views" }}
    {{#data-views}}
    {{>Menu}}
    {{/data-views}}
{{/data-portlets}}

Rendering dropdown or sub-menus

Skin designers can also use the Mustache syntax to create dropdown menus from the elements previously found in the sidebar of the Vector and MonoBook skins. This is a little trickier, however, but understanding the way the elements are stored can help.

The first sidebar element — typically containing the main page link and other MediaWiki links, is rendered via the {{#data-portlets-sidebar.data-portlets-first}} call. Subsequent menus, however, are stored in the {{#data-portlets-sidebar.array-portlets-rest}} array, and can be rendered by calling this.

For example, one may use the following syntax:

	{{#data-portlets-sidebar.array-portlets-rest}}
    <div class="mw-dropdown-title"><span>{{label}}</span>
    <div class="dropdown-content">{{>Portlet}}</div></div>  
    {{/data-portlets-sidebar.array-portlets-rest}}

Which, when CSS is applied to hide "dropdown-content" until "mw-dropdown-title" is hovered over, thus creating a dropdown menu.

Disabling the table of contents

In MW 1.38, you can remove the table of contents from the article body and position it outside the content area. To disable the table of contents generation, add the following to skin.json:

{
    "ValidSkinNames": {
        "notocskin": {
            "class": "SkinMustache",
            "args": [
                {
                   "name": "notocskin",
                   "toc": false
                }
            ]
        }
    }
}

The array-sections template key can be used for rendering the table of contents.

More examples

To see examples of template partials that can be used in your project, you can look through the examples in the Wikimedia skins labs.

脚本与样式

默认

A skin at minimum requires a single style ResourceLoader module defined in your skin's skin.json file. It will look a bit like this:

 "ResourceModules": {
     "skins.example": {
         "class": "ResourceLoaderSkinModule",
         "features": { "normalize": true },
          "styles": [ "resources/skins.example.less" ]
      }
 },

The features key allows you to use useful boiler plate defaults for a variety of things including i18n and normalize which are documented in the MediaWiki core php documentation. Features can be an array of keys (opt-in policy) or in MW 1.36 an associative array (opt-out policy, recommended). If you are not sure, please omit the features key to use the recommended defaults.

CSS / LESS

The skin.json is a manifest of all the assets your skin uses. Under the `ResourceModules` key you should find the style module for your skin listed under `skins.example`. Here, under the "styles" key you can add LESS or CSS files. They will be loaded in the order they are listed. With LESS you can use @import statements in the same folder. More information about what's possible can be found in Developing with ResourceLoader.

When using images you should be able to use relative URIs to access the image assets.

MediaWiki Skin Variables

MediaWiki skin variables originally introduced in MW 1.35, were enabled for wide use since MW 1.41.
The list of values available is in sync with latest Codex design tokens (demo site).

 

  • Quickly implement designFor skin designers skin variables offer a way to quickly implement design choices by setting values in a flat list. Through them designers can change fundamental properties like typography (font family, font size, etc.), colors, breakpoints, borders, sizing, spacing or z-indexes. This easy to use list is grouped by CSS properties. It must be located in folder and file 'resources/mediawiki.less/mediawiki.skin.variables.less' for ResourceLoader to pick it up. The naming scheme follows the MediaWiki variables naming convention .
  • Neutral defaultsIf a skin doesn't specify certain values, the system will fall back to use the defaults from MediaWiki's own 'mediawiki.skin.defaults.less'. Those values are representing a basic HTML look.
  • CustomizeWhile you can't modify the variable names, you could define additional ones in separate skin specific files. In every Less file, you can import the skin's values by only @import 'mediawiki.skin.variables.less';
  • Centralization benefitsAll the variables definition for the Wikimedia default theme and all the naming is centralized in order to
    • Establish a consistent naming convention for clarity and uniformity
    • Ensure compatibility across various skins and extensions
    • Provide insights into potential gaps or areas of improvement based on common usage patterns

Skin authors are encouraged to familiarize themselves with these updates to maximize the potential of their skins.

Use in core, skins and extensions Less files

In order to use mediawiki.skin.variables.less variables aka design tokens you must include an import statement on top of your Less file.

Example usage:

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

/* Links */
a {
	color: @color-link;
}

a:visited {
	color: @color-link--visited;
}

a:active {
	color: @color-link--active;
}

An important detail is, that only variables specified in mediawiki.skin.defaults.less can be reassigned with skin specific values in the skin's mediawiki.skin.variables.less file.

Note that, Vector 2022, Vector legacy and MinervaNeue skins are using the two default Codex themes for Wikimedia user-interfaces, which are also represented on the Codex documentation site.

Over 45 different skins and extensions use the skin variables in MW 1.41 already.

Using CSS variables for supporting different themes e.g. dark mode

MediaWiki skin variables support both fixed and CSS variable based output. You can opt into CSS variables by adding the following line to your mediawiki.skin.variables.less file.

@import 'mediawiki.skin.codex-design-tokens/theme-wikimedia-ui.less';
Using the default Wikimedia dark mode palette

When using CSS variables, you can make use of the mixin-night-mode-palette mixin to define a night mode palette as in this example:

@import 'mediawiki.skin.variables.less';
@import 'mediawiki.skin.codex-design-tokens/theme-wikimedia-ui-mixin-dark.less';
@import 'mediawiki.skin.codex-design-tokens/theme-wikimedia-ui-reset.less';

@media screen and ( prefers-color-scheme: dark ) {
    html {
        color-scheme: light dark;
        .mixin-night-mode-palette();
    }
}

Responsive skins / adding a meta viewport

If you are building a responsive skin, make sure to use the responsive skin option when declaring your skin in skin.json.

{
    "ValidSkinNames": {
        "my-responsive-skin": {
            "class": "SkinMustache",
            "args": [
                {
                   "name": "my-responsive-skin",
                   "responsive": true
                }
            ]
        }
    }
}

Making skins compatible with languages that are right-to-left (RTL)

The scripts of certain languages e.g. Hebrew are in right to left rather than left to right. This presents a problem for skin developers, where interfaces are flipped e.g. in Vector the sidebar is on the left rather than the right.

In MediaWiki it's also possible to use a different language for the skin and the content. For example, in Special:Preferences you can set your language to Hebrew while retaining the content in English.

Writing skins that work well in RTL is a large topic out of scope for this document, but if you need to test your skin in RTL you should update LocalSettings.php to change your content language:

$wgLanguageCode = "he";

As a skin developer you should keep in mind two things:

  • Any CSS written for your skin will be flipped automatically via the CSSJanus tool without any work required from you, however you may need to disable some of those transformations (see Flipping).
  • Any HTML you render that can be translated should be marked up with the dir HTML attribute. For your convenience SkinMustache provides the html-user-language-attributes template variable which can be used like so:
<div
  {{{html-user-language-attributes}}}
>
</div>

for a user who has set their language to Hebrew in preferences, produces the following HTML:

<div
  dir="rtl" lang="he"
>
</div>

图像

You can extend 手册:$wgLogos with any data you choose to. This will allow site admins to configure images as they choose, but you must always conditionally render them.

In cases where images must be hardcoded for some reason, and cannot use a CSS background-image or wgLogos, you will need to extend the data sent to the template

JavaScript

JavaScript code in skins, runs in an environment where you can rely on the `mw` and `jQuery` objects having been loaded. We recommend using ResourceLoader/Package_files which will allow you to require file assets.

For information on the available API and libraries see core JS documentation.

高级信息

More advanced information will provided on an as requested basis. Please ask a question on the talk page to accelerate the addition of documentation!

i18n

Messages defined in i18n/en.json can be passed directly to your Mustache template by inclusion in skin.json. Note, that you can use any messages defined inside MediaWiki core.

skin.json i18n/en.json skin.mustache
{
    "ValidSkinNames": {
        "mymustacheskin": {
            "class": "SkinMustache",
            "args": [
                {
                   "name": "mymustacheskin",
                   "messages": [
                        "createaccount"
                    ]
                }
            ]
        }
    }
}
{
        "@metadata": {
                "authors": []
        },
        "skinname-mymustacheskin": "My Mustache Skin",
        "mymustacheskin-hello": "Hello"
}
<div>
    {{msg-mymustacheskin-hello}} <!-- prints hello in the current language -->
</div>

Extending data

The data available is documented on SkinMustache.php .

If you need to add additional data for rendering inside your skin's template that cannot be served by messages (as in the i18n section) e.g. raw HTML or complex data structures you must use a dedicated PHP class and extend the SkinMustache::getTemplateData method.

<?php

class MySkin extends SkinMustache {
    
    /**
     * Extends the getTemplateData function to add a template key 'html-myskin-hello-world'
     * which can be rendered in skin.mustache using {{{html-myskin-hello-world}}}
     */
    public function getTemplateData() {
        $data = parent::getTemplateData();
        $data['html-myskin-hello-world'] = '<strong>Hello world!</strong>'; // or $this->msg('msg-key')->parse();
        return $data;
    }
}

使用SkinModule类载入默认样式

All skins should define a single style module with the class SkinModule . The module defines various default styles to take care of MediaWiki internals. If you want, you can disable these features and provide your own styles. Define features as an empty object to tie yourself into the recommended MediaWiki defaults. A list of support features is provided in our docs.

Example MediaWiki\\ResourceLoader\\SkinModule that disables the logo feature but enables several others:

{
    "skins.vector.styles": {
		"class": "MediaWiki\\ResourceLoader\\SkinModule",
		"features": {
				"normalize": true,
				"elements": true,
				"content": true,
				"logo": false,
				"interface": true,
				"legacy": true
		},
		"targets": [
				"desktop",
				"mobile"
		],
		"styles": [ "resources/skins.vector.styles/skin.less" ]
	}
}


整合其他擴充功能

Extensions should integrate with you, not the other way round! Try to forget about extensions when writing your skin. Skins for extension developers is provided for extension developers to ensure they get the best compatibility. The starter templates in Getting_started will render all possible UI elements. If you omit certain pieces of data you may break support with extensions.

For certain extensions you may want to tweak the styles of the default UI elements, notably Extension:Echo . To do this you will need to read Manual:$wgResourceModuleSkinStyles .

更改菜单内容

The composition of menus can be changed by using hooks. For example in Vector, the SkinTemplateNavigation hook is used to relocate the watch star icon. When doing this, remember to check the skin being operated on, to avoid side effects in other skins.

I want to change elements in the head of the HTML page

Skin developers should not concern themselves with modifying anything in the HEAD of a document. Modifications of the HEAD are better served via extensions and configuration inside LocalSettings.php.

The following links may be helpful:

I am writing an extension that needs to style itself differently depending on the skin

Extensions can make use of skin styles to ship skin-specific styles using the skinStyles key. 参见Manual:$wgResourceModuleSkinStyles

VisualEditor compatibility

To ensure compatibility with VisualEditor please see these guidelines.

为1.35版本构建皮肤

In 1.35 support for building skins was not as straightforward as in 1.36. If you wish to build a skin for 1.35, using template data provided in 1.36, you will need to extend the SkinMustache PHP class. A polyfill for the Example skin is provided.

你的意见很重要

If something is not easy, we'd like to make it easier, so your feedback is important. If you run into problems, then please file a bug report in the MediaWiki core skin architecture project in Phabricator, and we'll try and find an elegant solution.

Please feel free to ask a question on the talk page. There is no such thing as a stupid question.