Tutorial: Creating a subskin in MediaWiki 1.18+

By <daniel@redwerks.org>

In my last MediaWiki skinning tutorial I detailed how to create a brand new custom skin for MediaWiki. Since then I’ve found that there are also a number of people who want to create a new skin that only contains css tweaks of one of the built-in core skins. Many people try to do this the wrong way by copying the php file and then editing the references. Of course they then fail and come into #mediawiki asking for help. The reason for this is that since Resource Loader was introduced skins now include modules using $out->addModuleStyles( 'skin.theskinname' );. However the modules themselves are defined inside resources/Resources.php in core. As a result when the user changes skin.vector to skin.theirskinname the skin tries to load a module that does not exist and their new skin does not work. Copying the php is also a bad idea due to the unnecessary duplication of code and the way your derivative skin becomes out of date when you upgrade.

This tutorial goes over how to use a subclass to create a derivative of Vector and create a new skin with some extra css.

Creating the skin

Since there are so few things different from the last tutorial, I’ll lift some of what I wrote last time about skin names and the starting files.

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' => 'mywiki-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.

Now we can move on with the new MySkin.skin.php file. This is so short we’ll put the usual good practice header and SkinTemplate definition in one preformatted block:

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

require_once( dirname( __FILE__ ) . '/../Vector.php' );

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

	var $skinname = 'myskin', $stylename = 'myskin';

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

}

Just like before:

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.

And now; Well, we’re done ;).

Make note of the SkinVector and the new require_once line. If you wish to derive a skin from another core skin like MonoBook you can update these values. The require_once line is needed since core skin classes are not autoloaded. In the future it would be ideal to include skins into the autoloader but for now we need to use a require_once.

Note that due to bug 45229 the CSS outputted will be sorted alphabetically by module names instead of in the order you explicitly said the modules should be loaded (ie: The css for the skin your skin is based on, followed by your custom css). So keep in mind that if you base your skin off “skin.vector” and create “skin.foo” MediaWiki will incorrectly load “skin.foo” first instead of “skin.vector”. For now you will have to work around this by either using module names that alphabetically sort higher than the name of the skin you’re deriving or using stronger selectors in your skin’s css so that your rules always override the ones in the base skin no matter what the cascade order is.

From this point you can start including custom css into your own screen.css file. Remember not to go copying all of Vector’s css. Vector’s module will already be included so all the css will be there. You just need to add your extra css rules to modify the skin.

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.