Developing Custom Skins for MediaWiki Sites
Not every wiki wants the default Vector look; some need a sleek corporate vibe, others a retro‑gaming aesthetic
Why Bother with a Custom Skin?
Imagine you just walked into a coffee shop that smells exactly like your grandma’s kitchen. That feeling – familiar, cozy, but with a dash of surprise – is what a well‑crafted MediaWiki skin can do for a wiki community. Not every wiki wants the default Vector look; some need a sleek corporate vibe, others a retro‑gaming aesthetic. And yes, you can pull that off without becoming a full‑blown front‑end wizard.
But before we dive into the “how”, let’s acknowledge the elephant in the room: skinning MediaWiki used to be a hair‑pulling ordeal. Those days of hunting for skin.php files in a labyrinth of extensions are fading fast. Since MediaWiki 1.41, the skin.json manifest and modern ResourceLoader make the whole thing feel like assembling Lego bricks.
Getting Your Hands Dirty – The Minimal Skeleton
First things first: create a folder inside skins/. Let’s call it MyCoolSkin. Inside, you’ll need three files to get the engine ticking:
MyCoolSkin.skin.php– the PHP entry point.skin.json– metadata, dependencies, and the ResourceLoader map.MyCoolSkinTemplate.php– the HTML skeleton (yes, it’s a PHP class).
Don’t worry, the names are flexible; just keep them consistent.
// MyCoolSkin.skin.php
if ( !defined( 'MEDIAWIKI' ) ) {
exit;
}
class SkinMyCoolSkin extends SkinTemplate {
public $skinname = 'mycoolskin';
public $template = 'MyCoolSkinTemplate';
public function initPage( OutputPage $out ) {
parent::initPage( $out );
$out->addModules( [ 'skins.mycoolskin' ] );
}
}
That snippet is about as short as a haiku. It tells MediaWiki, “Hey, I’m a skin called mycoolskin, and my HTML lives in MyCoolSkinTemplate.” That’s it, really.
What Goes Into skin.json?
Think of skin.json as the skin’s résumé. It lists the job title, the required MediaWiki version, and the assets you’ll be serving (CSS, JS, maybe a splash of SVG). Here’s a starter:
{
"name": "mycoolskin",
"author": "Jane Doe",
"url": "https://github.com/janedoe/mycoolskin",
"description": "A minimalistic skin for modern wikis",
"license-name": "MIT",
"type": "skin",
"requires": {
"MediaWiki": ">= 1.41"
},
"ResourceModules": {
"skins.mycoolskin": {
"styles": "modules/main.css",
"scripts": "modules/main.js",
"dependencies": [ "mediawiki.skinning.interface" ]
}
}
}
Notice the dependencies line? It pulls in the core mediawiki.skinning.interface module, which makes sure your skin plays nice with the built‑in UI (like the collapsible sidebars).
Crafting the HTML – The Template Class
Now for the meat: the template. You might be tempted to write raw HTML, but MediaWiki loves its ParserOutput objects. A typical template looks like this:
// MyCoolSkinTemplate.php
class MyCoolSkinTemplate extends BaseTemplate {
public function execute() {
// Shortcut for global OutputPage object
$this->html( 'headelement' );
// Main content area
echo '<div id="content" class="mw-body">';
$this->html( 'bodycontent' );
echo '</div>';
// Footer
$this->html( 'footer' );
$this->printTrail();
echo '</body></html>';
}
}
That’s a bit of a skeleton, but it demonstrates the pattern: you call $this->html() for each pre‑rendered chunk MediaWiki hands you, then you sprinkle your own markup where you want it.
Styling the Skin – CSS Tricks That Won’t Break the Bank
If you’ve ever tried to “just add a CSS file” and ended up with a broken layout, you know the pain. The trick is to target the right selectors, and to respect the official skinning guidelines. Below is a tiny main.css that gives the page a subtle blue tint and a custom font.
/* main.css */
html, body {
background: #f0f8ff; /* AliceBlue for a soft vibe */
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
}
#content {
max-width: 960px;
margin: 0 auto;
padding: 1.5rem;
}
#p-logo a {
background-image: url("../images/logo.svg");
background-size: contain;
width: 120px;
height: 60px;
}
Pro tip: use relative URLs (../images/...) so the skin works whether it lives in skins/ or a separate extension directory.
JavaScript – When You Need a Little Interaction
Maybe you want a “back to top” button that slides in when the user scrolls. A 30‑line script does the trick:
// modules/main.js
$( function () {
const $btn = $( '<button id="back-to-top">↑ Top</button>' );
$btn.css({
position: 'fixed',
bottom: '20px',
right: '20px',
display: 'none',
'z-index': 1000
}).appendTo( 'body' );
$( window ).on( 'scroll', function () {
if ( $( this ).scrollTop() > 300 ) {
$btn.fadeIn();
} else {
$btn.fadeOut();
}
});
$btn.on( 'click', function () {
$( 'html, body' ).animate({ scrollTop: 0 }, 400 );
});
} );
Notice the tiny “$” prefix everywhere? That’s jQuery, still shipped with MediaWiki for backward compatibility. If you’re feeling adventurous, swap it for plain document.querySelector – the skin will still load, but you’ll need to add mediawiki.jqueryMsg to the module’s dependencies.
Testing Your Skin – The Fast‑Track Development Loop
Okay, you’ve built the files. Time to see if they actually render without blowing up the server. Here’s a quick checklist:
- Clear the cache:
php maintenance/eval.php "purgeAllCaches();"(or just deletecachedirectory). - Visit
Special:Version– your skin should appear in the “Skin” section. - Open the wiki in a private/incognito window to avoid stale CSS.
Enable the skin in LocalSettings.php:
$wgDefaultSkin = "mycoolskin"; // or add to $wgAllowDisplayTitle if you needIf you see a blank page, check your PHP error log. A missing use statement or a stray ?><?php tag can cause a white‑screen nightmare – something I’ve learned the hard way after a coffee‑spill on my laptop.
Debugging Tips
When I first tried to add a custom font, I accidentally pointed the @font-face URL to a relative path that resolved to /w/index.php. The result? The whole site tried to download a PHP page as a font and threw a 404 error that displayed a “You are not allowed to view this page” message inside the font preview. Hilarious for me, baffling for users.
Lesson: always test assets with your browser’s network panel. Also, keep a README.md in your skin folder for future you – you’ll thank yourself when you revisit the repo months later.
Advanced Topics – Going Beyond the Basics
Now that you’ve got a working skin, you might wonder what else you can do. Below are a few ideas that push the envelope without turning your wiki into a Frankenstein monster.
Responsive Layouts with Flexbox
MediaWiki’s default layout is pretty rigid. If you want a sidebar that collapses on mobile, replace the #p-navigation markup with a Flexbox container. Example CSS:
/* Responsive flex layout */
#mw-panel {
display: flex;
flex-direction: column;
}
@media (max-width: 768px) {
#mw-panel {
flex-direction: row;
overflow-x: auto;
}
}
Combine that with a tiny JS snippet that toggles a collapsed class on the #mw-panel when a #menu-toggle button is clicked. The result: a slick, mobile‑first navigation bar.
Internationalisation (i18n)
Don’t forget that wikis are multilingual beasts. To expose your skin’s UI strings (like “Back to top”), create an i18n folder with en.json and fr.json files. The JSON format looks like this:
{
"@metadata": {
"authors": [ "Jane Doe" ],
"last-updated": "2024-10-02"
},
"mycoolskin-backtotop": "Back to top"
}
Then, in main.js, replace the hard‑coded button label with mw.msg( 'mycoolskin-backtotop' ). The wiki will automatically serve the right translation based on the user’s language settings.
Integrating OOUI for Consistent Form Elements
If you need a settings page for your skin, use the OOUI library. It gives you widgets that match the rest of the MediaWiki UI. A quick example:
// In an extension's Special page
$layout = new OOUI\PanelLayout( [
'padded' => true,
'expanded' => false,
'content' => [
new OOUI\ButtonWidget( [
'label' => $this->msg( 'mycoolskin-save' )->text(),
'icon' => 'check',
'flags' => [ 'primary' ]
] )
]
] );
$output->addHTML( $layout );
That snippet is a little out‑of‑scope for a pure skin, but many wikis bundle a tiny “skin‑settings” extension that lives right next to the skin folder. It’s a neat way to let admins tweak colors without editing CSS.
Deploying the Skin – From Local Test to Production
When you’re ready to share the skin with the world, zip the MyCoolSkin folder and upload it to the skins/ directory of your production wiki. Make sure file permissions are set to 664 for files and 775 for directories – a detail that trips up many newcomers.
Alternatively, host the skin on GitHub and use Composer to pull it in. Create a composer.json like this:
{
"name": "janedoe/mycoolskin",
"type": "mediawiki-skin",
"require": {
"mediawiki/core": "^1.41"
},
"autoload": {
"psr-4": {
"MyCoolSkin\\": "src/"
}
}
}
Then run composer require janedoe/mycoolskin on the server. MediaWiki will automatically detect the new skin if the composer.json includes the type “mediawiki-skin”.
Security Checklist
- Never trust user‑generated CSS. If you expose a CSS editor, whitelist allowed properties.
- Sanitize any data that ends up in
HTML– use$out->addHTML()orHtml::rawElement()wisely. - Keep the skin’s PHP free of database queries; skins should be presentation‑only.
Final Reflections
At the end of the day, a custom skin is more than just pretty colors. It’s a statement: “We care about the experience, we’re willing to roll up our sleeves, and we have a community that deserves a unique home.”
If you’ve ever felt the itch to change the default look of a wiki, I hope this rambling guide nudged you past the “it looks scary” barrier. Pick a tiny tweak, test it, and keep iterating. In a few weeks you’ll have a skin that feels like it was built just for your wiki, not slapped on by a generic template.