By <daniel@redwerks.org>

This page has been copied to a wiki page located at https://www.mediawiki.org/wiki/Manual:Skinning/Tutorial which may be more up to date.
wiki-skin-builder

This tutorial details the creation of a custom skin for MediaWiki. The skin is packaged up into a directory you can drop into a MediaWiki installation and require_once() to install just as any other extension.

Keep in mind that this tutorial is aimed at MediaWiki 1.18, features are used here which will not work before 1.18.

Creating the design

The actual creation of the html, css, images, any mockups, or even the idea of what a skin should look like is out of the scope of this tutorial. You’ll have to either have the markup, styles, and resources ready, or build up the empty boilerplate and start building the skin from there.

However before I go into that boilerplate and how-to I will go over some things you will have to think about while creating the design.

Body area

When mocking up the body area and layout of your skin remember there are many special types of pages in MediaWiki: You can view a diff between two versions of an article, there’s the edit page, and there are some special pages like recent changes to think of. Many of these can be very heavy pages, so much so that you may want to consider giving them a modified wider page in your design if you are doing a fixed width design. Consider including a mock up using recent changes or an edit page in the body area.

Subtitles

When placing the title in your design remember that MediaWiki has some subtitles that usually go below the title. These are usually in the form of a subpage hierarchy, redirected from line, diff/permalink navigation, or undelete line. You don’t need to use the same type of attached title+subtitle+body typically used in MonoBook/Vector but do keep in mind where such subtitles will go and how they will look.

Site notices and user notifications

MediaWiki has three built-in types of notifications. The site notice, user notice for new talkpage messages, and a js message for things like when you (un)watch a page. When you mock up your design remember to plan where these notices will go.

MediaWiki has a built in search functionality, the search bar is a key part of skins. When designing your skin remember to include a search bar in your design, including a style for the search bar that fits in with the rest of the skin.

Personal tools

MediaWiki includes a bar with a number of personal tools. These include things like login/logout, user and talk page links, and other things like watchlist and preferences. You don’t have to keep this bar the exact same in your design, you can decide to move the userpage link to something a little more styled, make login, logout, and register buttons, etc… Just remember that other links can be added to this bar and you should have some location for the rest of the functionality you didn’t add custom stuff for in your skin.

Actions

MediaWiki includes a number of page specific actions, these are typically displayed as tabs on a page. You have two formats you can work with this in, a single flat list (see MonoBook for reference) and a set of lists categorized into namespaces, variants, views, and actions (see how Vector separates them into separate areas and menus). Whether you use a flat list, grouped lists, or even pull some of them out and make buttons be sure you have a place for these. They are an important part of MediaWiki and easy to forget about if you are used to doing things for other CMS like WordPress.

MediaWiki includes a built-in sidebar for navigation. It isn’t the only type of navigation you can add to a skin, however it’s the only type currently with a built-in way of generating it. You can use whatever type of navigation you want in a design, but remember that if you don’t use a sidebar then you’ll either have to hardcode pieces of it or have someone program something like the sidebar’s parser for your navigation for now. However there are plans to implement support for more types of navigation and custom navigation in MediaWiki in the future.

That said, try not to hesitate coming up with a design that uses whatever type of navigation fits your skin and site best. Too many of the few existing skins are nothing but MonoBook slightly modified in look, sticking to the norm at the cost of what’s best for the design is one of the reason so many existing MediaWiki skins are bad.

Toolbox

MediaWiki skins usually also include the functionality that MonoBook and Vecor group into a toolbox. These tools include generic things like a special pages link and other page specific things like permalinks, feed links, block links, and printable links. Like the personal tools you can move pieces of these wherever you want, but since more links can be added to these tools be sure to have a place for the rest of the links you have not moved.

MediaWiki has support for interlanguage links from one page to the same page in a wiki of another language. One of the skin’s responsibilities is to define a place for this list of language lines to go. Be sure to consider a place for it, even if you just throw it in a sidebar lke MonoBook and Vector do.

Skns typically include a series of links and icons. These include things like the license / copyright of the wiki, a powered by icon, links to about, disclaimer, and privacy policy. When designing your skin consider if you want these or have a reason to leave them out of your design. When including them also consider how you are going to style them.

Wrapping up

Be sure to keep these areas in mind when mocking up your design. You don’t need to restrict yourself to the norm, feel free to relocate portions of areas into other spots, like taking the Username/talk links out of personal urls. The important thing is to make sure you don’t leave out the things MediaWiki needs to output when you think up the design. It’s very easy to create a beautiful design for your site but later realize you forgot the edit link, history, protect, delete and all the other actions, created a page that doesn’t fit editing, diffing history, and looking at recent changes well, doesn’t have a place for any user or site notices, doesn’t have a toolbox with key links, and doesn’t even have account for a link to the user’s watchlist.

Creating the skin

Lets start with the skin name and the blank files to start with. For our tutorial let’s ignore the existence of a skin called ‘MySkin’ in MediaWiki and call ours “My Skin” for tutorial purposes. You should come up with a name of your own for your skin.

When building a skin you’ll be working with your skin name in three forms, a lower-cased key, a camel cased identifier, and a localized skin name. For our skin the key would typically be ‘myskin’, the identifier ‘MySkin’, and the localized name ‘My Skin’. That’s the normal convention to follow, but you can use a different convention on your own, just remember that the key and identifier should only contain characters from the latin alphabet, you may have problems if the very start of the identifier is a number, neither may contain spaces, and the key must be entirely lower-case. The localized name should be the name you want people to see when they actually pick your skin and it may end up translated into another language.

Wherever we use ‘myskin’, ‘MySkin’ or ‘My Skin’ in examples remember to replace those with the key, identifier, and name of your own skin.

To start building your skin create a new folder in the skins/ folder using the key for your skin, in our case skins/myskin/. Inside this folder we want to start with three files, a .php file using your skin’s key as the entry point, a .i18n.php file using your skin’s identifier, and a file for the skin classes which you can use .skin.php with your skin’s identifier for.

For this tutorial you should have the following blank files to start with:

  • skins/myskin/myskin.php
  • skins/myskin/MySkin.skin.php
  • skins/myskin/MySkin.i18n.php

For skins/myskin/myskin.php you should probably start with a documentation header:

<?php
/**
 * My Skin skin
 *
 * @file
 * @ingroup Skins
 * @author Daniel Friesen (http://www.mediawiki.org/wiki/User:Dantman)
 * @license http://www.gnu.org/copyleft/gpl.html GNU General Public License 2.0 or later
 */

The practice of including this metadata can help with documentation generation. The @file and @ingroup are organizational and should be left as-is, while you’ll want to customize the @author and @license to what’s correct for your skin. You can note the typical practice for each is @author Name (url) or @author Name (email@address.tld) and `@license url name.

Like extensions you may want to include a quick bit of boilerplate to prevent running standalone and credits so when installed people know who authored your skin:

if( !defined( 'MEDIAWIKI' ) ) die( "This is an extension to the MediaWiki package and cannot be run standalone." );

$wgExtensionCredits['skin'][] = array(
	'path' => __FILE__,
	'name' => 'My Skin',
	'url' => "[...]",
	'author' => '[http://mediawiki.org/wiki/User:Dantman Daniel Friesen]',
	'descriptionmsg' => 'myskin-desc',
);

Be sure to customize things to what’s correct for you too.

Next we want the boilerplate to add our skin, autoload classes, add i18n, and add a resourceloader module:

$wgValidSkinNames['myskin'] = 'MySkin';
$wgAutoloadClasses['SkinMySkin'] = dirname(__FILE__).'/MySkin.skin.php';
$wgExtensionMessagesFiles['MySkin'] = dirname(__FILE__).'/MySkin.i18n.php';

$wgResourceModules['skins.myskin'] = array(
	'styles' => array(
		'myskin/css/screen.css' => array( 'media' => 'screen' ),
	),
	'remoteBasePath' => &$GLOBALS['wgStylePath'],
	'localBasePath' => &$GLOBALS['wgStyleDirectory'],
);

I should explain two key points here about the boilerplate.

Firstly when creating the class for the skin MediaWiki uses the value of the $wgValidSkinNames for the key of the skin and prepends it with Skin. So the name of the class for your skin will be “Skin{Your Identifier}” keep that in mind since that means that you do want to replace the “MySkin” in the “SkinMySkin” above with the identifier for your skin, and likewise you will want to do so in the class names in the .skin.php file.

Secondly I’ve included a ‘myskin/css/screen.css’ stylesheet in the list of styles here. The only thing important there is the ‘myskin/’ since the stylesheet should be somewhere within the same folder as the rest of your skin. Whether you want to put the stylesheet right next to the skin’s .php files, in a css/ subfolder, a styles/ subfolder, subfolder of some other name, or even something like styles/css/ along with styles/images/ is completely up to you there’s no restriction as long as you can address it from the array of styles.

The ‘styles’ array itself also does not have to be a single stylesheet, ResourceLoader combines all the stylesheets into a single stylesheet so you can separate your stylesheets into as many separate files as you want. You’ll probably want to read up on the ResourceLoader because it has other features like @embed and rtl flipping to consider when writing the .css for your skin, MediaWiki 1.19 also has 3 stylesheets you can import to get some common content styling without having to copy it into your own css., I’ll try to touch on all those later on in another section.

The next file you’ll want to deal with is the i18n file, at minimum you are going to need two i18n messages in English, one for the skin name so it can be displayed right in the lists of skins in preferences where the user selects their skin, and another for the credits that will go in Special:Version.

<?php
/**
 * Internationalization file for skin My Skin.
 *
 * @file
 * @ingroup Skins
 */

$messages = array();

/** English
 * @author Daniel Friesen
 */
$messages['en'] = array(
	'skinname-myskin' => "My Skin",
	'myskin-desc' => "Some skin of mine.",
);

You’ll want to customize the key and content of the -desc message with the description that should go in Special:Version as well as the “My Skin” and @author in the doc comments and of course the skinname- key and value.

The last php file to deal with now is the MySkin.skin.php file where the bulk of our actual skin is.

For good practice we’ll want to open this up with the usual header:

<?php
/**
* Skin file for skin My Skin.
*
* @file
* @ingroup Skins
*/

// [...]

And now we want to open up a SkinTemplate class:

/**
 * SkinTemplate class for My Skin skin
 * @ingroup Skins
 */
class SkinMySkin extends SkinTemplate {

	var $skinname = 'myskin', $stylename = 'myskin',
		$template = 'MySkinTemplate', $useHeadElement = true;

	/**
	 * @param $out OutputPage object
	 */
	function setupSkinUserCss( OutputPage $out ){
		parent::setupSkinUserCss( $out );
		$out->addModuleStyles( "skins.myskin" );
	}

}

// [...]

You should remember the SkinMySkin from earlier that you want to update, you’ll also want to update MySkinTemplate. A skin is built up of a SkinTemplate subclass and a template, the SkinTemplate inherits a lot of heavy functionality from the skin system itself, and the template handles the creation of the markup for the skin. You’ll also want to update the $skinname and $stylename. They’ve got different purposes, but generally these should be the same. The setupSkinUserCss code imports that resource loader module from before, remember to update the key there.

/**
 * BaseTemplate class for My Skin skin
 * @ingroup Skins
 */
class MySkinTemplate extends BaseTemplate {

	/**
	 * Outputs the entire contents of the page
	 */
	public function execute() {
		// Suppress warnings to prevent notices about missing indexes in $this->data
		wfSuppressWarnings();

		$this->html( 'headelement' ); ?>

// [...]

<?php $this->printTrail(); ?>
</body>
</html><?php
		wfRestoreWarnings();
	}

}

This is the outermost part of the boilerplate, we’ll be continuing in the “[...]” and adding the markup for your skin. Keep in mind that headelement defines the <body> tag itself you shouldn’t include one, if you want to add some attributes or classes to the body tag you’ll have to override the addToBodyAttributes function in your SkinTemplate.

Now we get into your actual skin specific markup here. Because that’s entirely dependent on the skin you are building the rest of the boilerplate will be small boilerplate focused around how to turn parts of your markup into functional parts of the skin.

To start with you should take your markup and insert it into the “// [...]“. You may want to consider taking this time to also setup the css for your skin. Doing so should give you a skin that doesn’t function but displays all the dummy markup you have inserted. It’s a good way to test your skin with just dummy markup and then make pieces of it functional and test them piece by piece.

makeListItem

In the following sections I am going to be using a helper method $this->makeListItem( $key, $item ); a lot to generate items from lists of data. This helper intelligently knows how to properly format a list item and all the attributes it needs, as well as create the correct links and other contents that belong in the list item.

By default this method outputs a <li>. If you wish to output something other than a an unordered or ordered list of items makeListItem can accept an array of options as a third argument. You can use that to change the tag from a ‘li’ to something else with something like array( 'tag' => 'span' ). Naturally when doing this you would also change the boilerplate I give to output something other than a <ul> to surround the list.

JS Message

Some parts of MediaWiki functionality use a special location for dynamically inserted messages. Most notably this is used for the message that shows up when a user watches or unwatches a page.

This is the boilerplate to mark the spot where the message will go.

<div id="mw-js-message" style="display:none;"></div>

Note that the entire thing is important. The mw-js-message id is used by MediaWiki’s js to find this block and insert the message into it. The display:none; style makes sure that the message block is not visible since this is just a placeholder until some js in the page needs a place to put a message.

User Message (newtalk)

MediaWiki can output a message to a user about new messages on their talkpage. Historically MonoBook and Vector have outputted the newtalk message in the content area and for now they are still there for compatibility with some out-of-content area hacks used on Wikipedia. However practically the newtalk message may actually make more sense alongside the js message inside your skin.

The boilerplate to output it is:

<?php $this->html( 'newtalk' ); ?>

The newtalk message has no wrapping or style on it’s own so you should wrap this in a block to style. The message doesn’t always show up so be sure to wrap it in a conditional:

<?php if ( $this->data['newtalk'] ) { ?>...<?php } ?>

The MonoBook/Vector line of skins uses a div with a usermessage class for the block around the newtalk message. To output the newtalk message in a MonoBook/Vector like way you’ll use:

<?php if ( $this->data['newtalk'] ) { ?><div class="usermessage"><?php $this->html( 'newtalk' ); ?></div><?php } ?>

Site Notice

The boilerplate to output a wiki’s sitenotice is:

<?php $this->html( 'sitenotice' ); ?>

Because you’ll likely be wrapping the sitenotice in a block you’ll probably want to test if the sitenotice is present, to do that you’ll use this boilerplate

<?php if ( $this->data['sitenotice'] ) { ?>...<?php } ?>

The MonoBook/Vector line of skins uses a div with a siteNotice id for the block around the sitenotice. To output the sitenotice in a MonoBook/Vector like way you would use:

<?php if ( $this->data['sitenotice'] ) { ?><div id="siteNotice"><?php $this->html( 'sitenotice' ); ?></div><?php } ?>

Site Name

Historically MediaWiki has never really had a need to access the site name from a skin. The built in skins generally outputted a logo instead of the site’s name, and most locations where the site’s name is used is a i18n message using the {{SITENAME}} magic word. As a result there’s no good skin key to access the sitename. I added one to SkinTemplate in 1.19, but we’re stuck in 1.18 for now. To make things easier for yourself, if you need to use the sitename in your skin this piece of boilerplate can be used to add a sitename key to the template’s keys:

if ( !isset($this->data['sitename']) ) {
	global $wgSitename;
	$this->set( 'sitename', $wgSitename );
}

Include this in your code after the public function execute() { before the comment and wfSuppressWarnings(); (please do indent it as relevant for sanity’s sake). After adding this (or if you only need 1.19 compatibility and already have the sitename key as a result) you can use this boilerplate to insert the sitename wherever you need in your skin:

<?php $this->text( 'sitename' ); ?>

The logo is comprised primarily of the key for the logo, the key for the main page’s url, and a call to insert a tooltip and accesskey for the logo.

The way to build this differs between MonoBook and Vector, with MonoBook now using the Html:: methods to build the markup and Vector expanding attributes into html using an Xml:: method.

This is the method that MonoBook uses to create a link to the mainpage with the logo as

<?php
	echo Html::element( 'a', array(
		'href' => $this->data['nav_urls']['mainpage']['href'],
		'style' => "background-image: url({$this->data['logopath']});" )
		+ Linker::tooltipAndAccesskeyAttribs('p-logo') ); ?>
  • The $this->data['nav_urls']['mainpage']['href'] key references the main page’s url.
  • The $this->data['logopath']key references the path to the logo’s image.
    • If you build something else like an img tag instead of a ‘style’ you’ll use $this->data['logopath'] as a value to the tag’s ‘src’ attribute.
  • Linker::tooltipAndAccesskeyAttribs('p-logo') creates an array containing a tooltip and associated accesskey. ‘p-logo’ is the id typically used for the logo so that’s the key that is used by MW to define the accesskey and tooltip.
  • If you output a second logo in the page you should avoid re-using the accesskey. To do that instead of merging the array from Linker::tooltipAndAccessKeyAttribs use 'title' => Linker::titleAttrib('p-logo') as another key => value pair to the attributes along with the href and style.
  • If you don’t use the Html:: interface to build your link you’ll have to use echo Xml::expandAttributes( ... ); to expand the attributes returned by tooltipAndAccesskeyAttribs.
  • If you don’t use the Html:: interface be sure to remember to use htmlspecialchars to escape the href, logopath, and Linker::titleAttrib. Alternatively for logopath you can use $this->text( 'logopath' ).

Keep in mind a something about the logo itself. We currently only have one config for logopath. As a result the same logo will be used in every skin, that includes the built-in skins. MediaWiki’s built-in skins generally have a logo area that restricts logo sizes to around 155x155px. If your skin has a logo area intended for a logo of a different size you may want to consider including a separate config variable to let users of your skin define a skin-specific logo.

Title

The title of the page is included into the skin’s keys as html. Generally a title is plain text but a limited set of html formatting may be permitted inside of a DISPLAYTITLE and some extensions may add html to it.

The boilerplate to output the page’s title is:

<?php $this->html( 'title' ); ?>

MonoBook and Vector output the title inside of a firstHeading h1, the full code to output the title in a MonoBook/Vector way is:

<h1 id="firstHeading" class="firstHeading"><?php $this->html('title') ?></h1>

The content area

The content area in MediaWiki usually has some extra styles like icons on external links.

MonoBook and Vector both have a <div id=”content”> and a <div id=”bodyContent”> though MonoBook styles stuff inside bodyContent while Vector styles stuff inside content.

Whether you keep these ids or not you should consider adding a class=”mw-body” wherever you do decide links should be styled. 1.19 has some stylesheets you can opt-in to that will use that class.

Tagline

MediaWiki has a “Tagline” which usually goes right below the title, however is also typically hidden by default. The text usually goes something like “From Your Wiki’s Name”. Some wiki like to show it and style it, though the primary purpose of this seams not to be for normal viewing, but for printing to identify the source of the content. Be sure to include this and test your print styles, we’ll go more into that in a later print section.

The tagline’s boilerplate is:

<?php $this->msg( 'tagline' ); ?>

You’ll probably want to wrap it in a div, it’s not optional so you don’t need a test. MonoBook/Vector uses a siteSub id and use this boilerplate:

<div id="siteSub"><?php $this->msg( 'tagline' ); ?></div>

Note that Vector does seem to wrap this inside a test to only output it on articles:

<?php if ( $this->data['isarticle'] ) { ?>...<?php } ?>

Subtitles

Besides the tagline MediaWiki has two subtitles below the title to take into account. One is used for various things like the subpage hierarchy and redirected from line while the other is specifically for the undelete message.

The two pieces to output the subtitle lines are:

<?php $this->html( 'subtitle' ); ?>
<?php $this->html( 'undelete' ); ?>

Naturally both of these are optional so you’ll want to test for them:

<?php if ( $this->data['subtitle'] ) { ?>...<?php } ?>
<?php if ( $this->data['undelete'] ) { ?>...<?php } ?>

MediaWiki/Vector uses contentSub and contentSub2 ids for these messages:

<?php if ( $this->data['subtitle'] ) { ?><div id="contentSub"><?php $this->html( 'subtitle' ); ?></div><?php } ?>
<?php if ( $this->data['undelete'] ) { ?><div id="contentSub2"><?php $this->html( 'undelete' ); ?></div><?php } ?>

Body Text

The body of the page is dead simple to output, you might even want to do this first:

<?php $this->html( 'bodytext' ) ?>

Categories

MediaWiki doesn’t have a good way to output categories besides the built-in catlinks block. Unfortunately the catlinks block includes an id, so for now we’re stuck outputting a catlinks block in all skins and being limited to only one list of categories.

To output this block use:

<?php $this->html( 'catlinks' ); ?>

dataAfterContent

MediaWiki skins have another little hack in place. Extensions have the ability to add blocks which should go somewhere after the body text, but they aren’t supposed to go before the catlinks block. Because the catlinks block typically goes right after the body text we have a separate key to insert these.

<?php $this->html( 'dataAfterContent' ); ?>

Be sure to include this or else some extensions may not work properly in your skin.

Personal Tools

BaseTemplate includes some helpers that make building a list of personal tool links easy. Vector and MonoBook display the personal tools in the far upper corner of the page. They generally contain things like the login linkt, logout link, the user’s username with a link to their userpage, talkpage, watchlist, preferences, and other links.

This boilerplate can output the personal tools as an unordered list.

<ul>
<?php foreach ( $this->getPersonalTools() as $key => $item ) { ?>
	<?php echo $this->makeListItem($key, $item); ?>

<?php } ?>
</ul>

If you want to omit some items from the personal tools you can set $this->getPersonalTools() to a variable and modify that then use that in the loop instead. The personal tools array uses keys so it’s easy to unset or modify something.

Content Actions

As of 1.18 MediaWiki supports the use of two different lists to access what is usually outputted as tabs for a page. content_actions provides these tabs as a single flat array, this was the original format of the actions and is used by MonoBook. The other is content_navigation which was introduced by Vector. content_navigation provides the list of tabs as a categorized array containing the categories ‘namespaces’, ‘views’, ‘actions’, and ‘variants’ with the tabs that are put into content_actions nicely separated into categories. If you want to differentiate between any of the tabs in any way at all, be it some special styling, or outputting them in separate lists, you are better off making use of the new content_actions. Note that the content_actions also includes a redundant ‘Read’ tab in views which is omitted in the flat content_actions list.

This is better explained with a number of examples.

This example outputs just the namespaces category of links.

<ul>
<?php foreach ( $this->data['content_navigation']['namespaces'] as $key => $tab ) { ?>
	<?php echo $this->makeListItem( $key, $tab ); ?>

<?php } ?>
</ul>

You can use this to output these categories wherever you want in whatever order you want.

This example outputs all the categories of links as separate lists:

<?php foreach ( $this->data['content_navigation'] as $category => $tabs ) { ?>
<ul>
<?php foreach ( $tabs as $key => $tab ) { ?>
	<?php echo $this->makeListItem( $key, $tab ); ?>

<?php } ?>
</ul>
<?php } ?>

This example outputs all the categories of links as one unified list. This will include the slightly redundant ‘Read’ tab, however you can use the separation of categories to add separators or other things wherever you want.

<ul>
<?php
	foreach ( $this->data['content_navigation'] as $category => $tabs ) {
		foreach ( $tabs as $key => $tab ) { ?>
			<?php echo $this->makeListItem( $key, $tab ); ?>

<?php
		}
	} ?>
</ul>

This example outputs the flat content_actions list as a single list:

<ul>
<?php
	foreach ( $this->data['content_actions'] as $key => $tab ) { ?>
		<?php echo $this->makeListItem( $key, $tab ); ?>

<?php
	} ?>
</ul>

For most of these you’ll probably want to add something like a css class or id based off the $category to target with styles as you please.

Currently for navigation MediaWiki only supports a built-in sidebar. For other types of navigation you can hardcode or preferably have someone implement the code to support your own custom navigation message. For now all I can show is the sidebar boilerplate.

<?php
foreach ( $this->getSidebar() as $boxName => $box ) { ?>
<div id='<?php echo Sanitizer::escapeId( $box['id'] ) ?>'<?php echo Linker::tooltip( $box['id'] ) ?>>
	<h5><?php echo htmlspecialchars( $box['header'] ); ?></h5>
<?php
	if ( is_array( $box['content'] ) ) { ?>
	<ul>
<?php
		foreach ( $box['content'] as $key => $item ) { ?>
		<?php echo $this->makeListItem( $key, $item ); ?>

<?php
		} ?>
	</ul>
<?php
	} else { ?>
		<?php echo $box['content']; ?>
<?php
	} ?>
<?php
} ?>

The boilerplate is of course a little plain. I’ve left it with the typical id and tooltip but you may want to add a class or something. If you don’t care about the ability to customize the output of lists you can use $this->getSkin( array( 'htmlOnly' => true ) ) and drop the whole is_array test and just keep the <?php echo $box['content']; ?>.

Some notes for those who have seen old skin boilerplate code. The toolbox and language boxes are now part of the array returned by getSidebar() you don’t need to special case them, just keep the code to output the content of a sidebar block and they’ll be outputted fine. The search block however is absent by default. If you want it to output you’ll need to use array( ‘search’ => true’ ) and special case the ‘SEARCH’ $boxName to output a search form and probably also a <label> in the header.

A MediaWiki page may have links to the same page in other languages on other wiki when interlanguage links are used in the page. If you do this traditionally like MonoBook and Vector you may output this as part of the sidebar.

The boilerplate to output the list of language links for a page is:

<ul>
<?php
	foreach ( $this->data['language_urls'] as $key => $langlink ) { ?>
		<?php echo $this->makeListItem( $key, $langlink ); ?>

<?php
	} ?>
</ul>

Since the language links are often not present you may want to display the ui around them conditionally:

<?php if ( $this->data['language_urls'] ) { ?>
	// [...]
<?php } ?>

Toolbox

The Mediawiki toolbox contains various links. Some are general links like a links to a list of Special Pages so a user always has a way to access them. Others are page-sensitive links like permalinks, printable links, block links, feed links, and a link to a list of pages linking to the current page.

The boilerplate to output the toolbox is:

<ul>
<?php
	foreach ( $this->getToolbox() as $key => $tbitem ) { ?>
		<?php echo $this->makeListItem( $key, $tbitem ); ?>

<?php
	}
	wfRunHooks( 'SkinTemplateToolboxEnd', array( &$this ) ); ?>
</ul>

The SkinTemplateToolboxEnd hook is included for compatibility with some extensions. As more skins use the BaseTemplateToolbox hook and more extensions switch to using it we may be able to drop the hook and and no longer include it.

Like personal urls the array that $this->getToolbox(); returns can be set to a variable can can be easily modified and unset if you want to move parts of the toolbox elsewhere in your skin.

Search Form

MediaWiki skins usually include a search form that can be used to search the wiki. If the feature is enabled and the form is setup correctly this form usually also supports search suggestions. MonoBook does this as part of the sidebar, while other skins such as Vector have a dedicated place for the search form.

To start the search form lets begin with the form itself:

<form action="<?php $this->text( 'wgScript' ); ?>">
	<input type='hidden' name="title" value="<?php $this->text( 'searchtitle' ) ?>" />
	// [...]
</form>

A search form itself uses the HTTP get method rather than a post. As a result you can’t include any query arguments in the action=””. So a search form is built initially of a form with the action pointed to the wiki’s index.php and a hidden input with the search page’s title to ensure it’s in the url.

You should probably give the form itself an id. Keep in mind that MediaWiki supports autocompletion of the search form. Right now in 1.18 MediaWiki will only look for search forms with one of the id’s “searchform” “searchform2″ “powersearch” or “search”. So the best practice is to use id=”searchform” for your form. Hopefully I may expand that later in MediaWiki to support search forms better. I think I’ll use class=”mw-search”, so you might as well include that as well in your form as well.

Moving on, naturally every search form needs a text field to enter a search query into. At it’s base level you can use this helper method to build a search input:

<?php echo $this->makeSearchInput(); ?>

However this helper supports an array input to change the intput that is output. I’ll explain some of the common important points:

Firstly the input that is output by default uses type=”search”. Note that in the browsers that support this they usually give a search input special styling. As a result if you are doing special styling like what Vector does for it’s simple search you may want to override this and force MediaWiki to output a text input instead of a search input:

<?php echo $this->makeSearchInput( array( 'type' => 'text' ) ); ?>

Another thing to keep in mind is you may want to use an id on your search input. You’ll probably have some label nearby like “Search:” or something, at the very least it may be one that is hidden but is still around for accessibility purposes. You’ll probably need an id to target the input with a label if you don’t put the input inside the label itself. This is especially true if you put the label inside a header like vector does. The search input’s helper also accepts an id to use. For example MonoBook and Vector use the id ‘searchInput’ to output a search input with the searchInput id you can use:

<?php echo $this->makeSearchInput( array( 'id' => 'searchInput' ) ); ?>

Note that the array is basically a list of attributes that are modified with the pieces important to a search input then passed to the Html:: method that generates the input. This means you can add just about any html attribute that you want. Feel free to use it to add a css class, a inline style, some html5 data- attribute, or so on.

Next we’re going to need a search button so that the user can submit the search. However this helper has 3 different modes to keep in mind. MediaWiki supports two forms of search. The default “Go” search which may redirect directly to a page if the search term matches a page’s title. And a “Full Text” search that will always go to the search page. The search input’s helper supports both a ‘go’ mode and a ‘fulltext’ mode. The 3rd mode is an ‘image’ mode which outputs a ‘go’ mode button but does it using an image instead of text. Vector uses this last mode to create it’s simplesearch icon. This helper also accepts an array of attributes as the second argument. The difference here is that in the ‘image’ mode the required ‘src’ attribute and optional ‘alt’ attribute go on the <img> while the rest of the attributes you specify go on the <button>. Here are some examples:

// A "Go" button
<?php echo $this->makeSearchButton( 'go' ); ?>
// A Full Text "Search" button
<?php echo $this->makeSearchButton( 'fulltext' ); ?>
// A Go button using the button text "Search"
<?php echo $this->makeSearchButton( 'go', array( 'value' => $this->translator->translate( 'searchbutton' ) ); ?>
// A image search button using the images/searchbutton.png image in your skin's folder.
<?php echo $this->makeSearchButton( 'image', array( 'src' => $this->getSkin()->getSkinStylePath( 'images/searchbutton.png') ); ?>

The tradition in MonoBook and Vector for id’s here is to use searchGoButton and mw-searchbutton for a pair of Go and Full Text Search buttons, both with a searchButton class. And to use the id searchButton for a Vector simple search style image button. The examples for those cases:

// A "Go" button
<?php echo $this->makeSearchButton( 'go', array( 'id' => 'searchGoButton', 'class' => 'searchButton' ) ); ?>
// A Full Text "Search" button
<?php echo $this->makeSearchButton( 'fulltext', array( 'id' => 'mw-searchButton', 'class' => 'searchButton' ) ); ?>
// A image search button using the images/searchbutton.png image in your skin's folder.
<?php echo $this->makeSearchButton( 'image', array( 'id' => 'searchButton', 'src' => $this->getSkin()->getSkinStylePath( 'images/searchbutton.png') ); ?>

There’s no standard for toggling between a classic Go+Search and a simple search as that introduction was specific to Vector. You’re also free to just have a plain go button with a “Search” title if it fits your theme. The old monobook had a config option to only have a Go button and instead have a powersearch link but I won’t go into that here.

A MediaWiki skin may contain a common list of links and pieces of text intended to be in the footer. These footer links may contain things like a license line and other links like about, privacy policy, and disclaimer links.

The footerlinks array is returned by the $this->getFoolerLinks(); helper which also accepts a “flat” argument to return a flat array instead of a categorized one.

The boilerplate to output a collection of footerlinks lists is:

<?php
foreach ( $this->getFooterLinks() as $category => $links ) { ?>
<ul>
<?php
	foreach ( $links as $key ) { ?>
	<li><?php $this->html( $key ) ?></li>

<?php
	} ?>
</ul>
<?php
} ?>

To output the footer links as a flat array you can instead us

<ul>
<?php foreach ( $this->getFooterLinks( "flat" ) as $key ) { ?
	<li><?php $this->html( $key ) ?></li>

<?php } ?>
</ul>

Remember that like content_navigation this is a categorized list, however in this case it’s not a fixed set of categories. You may want to output these as separate lists, give each list a class or id based on the category, and do the same for the list items as well.

The footer may also contain a common list of icons in it. Included in these icons are MediaWiki’s powered by icon, optionally a “copyright” icon with an icon representing the license the wiki has chosen. As well as any other icon that a user or extension has defined with $wgFooterIcons.

The footer icons list is categorized into groups.

To output a list of only icons where each group is a list item containing one or more icons you can use:

<ul>
<?php
	foreach ( $this->getFooterIcons( "icononly" ) as $blockName => $footerIcons ) { ?>
	<li>
<?php
		foreach ( $footerIcons as $icon ) { ?>
		<?php echo $this->getSkin()->makeFooterIcon( $icon ); ?>

<?php
		} ?>
	</li>
<?php
	} ?>
</ul>

Using the ‘withoutImage’ parameter to makeFooterIcon you can instead output a textual list instead of icons. Modern uses this kind of technique. Note that footer links has a textual representation of the wiki’s license, so including the copyright/license icon as a second piece of text doesn’t make that much sense. Hence to omit the copyright icon/text you can use the “nocopyright” parameter to getFooterIcons.

<ul>
<?php
	foreach ( $this->getFooterIcons( "nocopyright" ) as $blockName => $footerIcons ) { ?>
	<li>
<?php
		foreach ( $footerIcons as $icon ) { ?>
		<?php echo $this->getSkin()->makeFooterIcon( $icon, 'withoutImage' ); ?>

<?php
		} ?>
	</li>
<?php
	} ?>
</ul>

Taking care of special cases

MediaWiki has a number of variances in what it displays depending on the page you are on. When finishing up you skin you should go around to the different parts of MediaWiki and ensure your skin looks correct in those cases.

  • Test the “Redirected from” line on a redirected page.
  • And test the redirect page itself.
  • View a page’s permalink to ensure your skin works fine when viewing old revisions.
  • Test your wiki’s skin on the edit, history, and diff pages.
  • Look at a deleted page to ensure the undelete line is present.
  • Double check the styles on Special:SpecialPages.
  • Test at least a few of the other special pages.
  • Test your skin’s ui on pages as an anonymous user, a normal user, and as an admin each of which have different tabs.

In addition to these small things to test while building your skin there are a few major cases you should ensure function correctly when you’ve put your skin together.

MediaWiki supports page printing with a print stylesheet that removes most of the ui, this allows users to simply print any page on the wiki without any special printing mode. The ‘printable’ mode does little but disable screen stylesheets and take the print stylesheets and make them apply to the screen, it’s only around for users who still think that they need a separate mode to print.

You’ve likely added new interface pieces with new classes not included in the common printing stylesheet, hence you’ll need to make some tweaks to ensure your skin looks correctly while printing.

To tweak things in print mode the best method of doing so is to add a print stylesheet. Go back to that resource loader array and add a new stylesheet with a ‘print’ media instead of ‘screen’. From this stylesheet you can set display: none; on the parts of your interface that still show up in print mode. You may also wish to give some things like the tagline a better print style as well.

Alternatively you can add class=”noprint” to the elements you need to hide from print mode.

Other languages

Use your i18n

MediaWiki has a great i18n system. Making use of it means that text in your skin can be translated into other languages letting your skin be used on more wiki and supporting visitors that come to your wiki who happen to speak a different preferred native language. So, please do make use of the i18n system if you add any pieces of text to your skin. And by using the i18n system wikis can customize the text that is outputted.

To define a new i18n key for your skin make use of the SkinName.i18n.php file we created. Adding new entries to the $messages['en'] array will define new messages in the i18n system. Keep in mind that these messages keys are global, so it’s good practice to prefix any key you use with a ‘skinkey-’ to avoid conflicts.

After you’ve defined a new message you can use it in your skin with:

<?php $this->msg( 'msg-key' ); ?>

If you want to consider the message a block of WikiText to be parsed instead of outputted as escaped plain text you can use this:

<?php $this->msgWiki( 'msg-key' ); ?>

Do keep in mind though that this invokes the parser and may not be cached so nicely, so overusing msgWiki on a popular wiki can give you a performance hit, especially if you use anything advanced in the WikiText like parser functions and other extensions.

Language variants

MediaWiki supports the concept of ‘variants’ for some languages. Some languages such as Kazakh and Serbian support multiple writing scripts. MediaWiki includes a language converter that automatically converts content between these scripts. When on a wiki using a language that supports variants MediaWiki includes tabs in the ‘variants’ category of tabs.

When you’re done with the primary coding and styling of your skin you should consider testing to ensure you have variants tabs outputting correctly. You can do this by setting your development wiki’s content to ‘sk’ or ‘zh’ temporarily.

Right-to-Left (rtl) languages

Besides the many languages such as English which are written left-to-right (ltr), there are some languages that MediaWiki supports which are written right-to-left (rtl).

Even more importantly than language variants, you should probably test your skins compatibility with rtl languages. Because MediaWiki 1.18 includes better directionality support that allows the page language direction to be based on the user’s language rather than the content language you can test simply by including a uselang= parameter to a rtl language such as &uselang=fa (Persian) or &uselang=he Hebrew.

To make support for rtl languages easy ResourceLoader automatically flips many css properties automatically doing most of the work flipping the interface. There are however two cases where you may need to intervene and help directly to support rtl languages. Firstly ResourceLoader will flip things like margin-{left,right} and padding-{left,right} however it will not flip left/right margins and padding in the combined ‘margin’ and ‘padding’ properties, you may need to alter your css to use separated properties for left and right margins and padding. Secondly you may have some parts of the interface which may break when flipped and not work correctly in rtl, if you have a css property that is being flipped which needs to stay the same you can prefix it with /* @noflip */ to stop it from being flipped.

Accessibility

Another of MediaWiki’s heavy focuses is accessibility. Accessibility is heavily considered whenever a new skin is added to core, however simply by not taking accessibility into account when you build a new skin you can easily ruin the accessibility of your wiki.

A full explanation on how to make a website / skin accessible is out of the scope of this tutorial. For in-depth information you should probably look at other things such as WebAIM‘s information and WAI. But I will cover skip links here.

Skip links — also known as jump links in parts of MediaWiki — are an accessibility pattern traditionally used to allow users without the use of a mouse to skip large blocks of navigation and go directly to the content instead of having to tab through or listen to them all before reaching the content.

You should consider reading up on the concept here: http://webaim.org/techniques/skipnav/

Traditionally they have been called “Skip” links and implemented as “Skip navigation” or “Skip to content”. MediaWiki instead calls them “Jump” links and are implemented as “Jump to navigation”. I don’t know the origin of using jump instead of skip but it would likely be a good idea of keeping the pattern of creating “Skip …” links when your navigation is first and your content comes later on the page, and “Jump …” links when your content is the first thing on the page and your navigation comes later.

Skip links implementation is essentially the same as in a plain website. A div which you may or may-not hide containing a list of links pointing to ids you have in other spots in the markup to skip to that area.

While you’re at it you may want to consider adding WAI-ARIA roles to your skin for the browsers that implement it.

Installing your skin

In case you didn’t catch it from the very beginning of the tutorial. To install your skin include this inside your LocalSettings.php:

require_once( "$IP/skins/myskin/myskin.php" );

Replacing “myskin” with your skin’s key.

This pattern of skin works a little more like an extension, you have to require it, there is no skin autoloader now.

To try out your skin you can either change $wgDefaultSkin to make your new skin the default skin, go to Special:Preferences and change to the new skin, or follow the “Preview” link from Special:Preferences.

In closing…

After all I’ve explained here on how to create a MediaWiki skin, if you’re looking to make a skin for a big project do still consider contracting someone who specializes in building a MediaWiki skin. There are lots of things someone with experience will know which is completely out of scope of this tutorial. General things like how to properly make something accessible and browser compatibility and MediaWiki specific tricks like how to do something out of the ordinary to the typical patterns of outputting things that may look better in your design and let you design the best look for your wiki more freely.

Some companies and contractors like here at Redwerks can also do both the Web Design and skin building for you.