Skin Guidelines
How A Skin Works
The main component of a skin is its template file. Pmwiki goes through (parses) the template and makes substitutions when it sees special layout markers (e.g. layout variables, also called skin directives).
The skin's template file is named either pub/skins/<skin-name>/<skin-name>.tmpl
or pub/skins/<skin-name>/skin.tmpl
where <skin-name>
is all lower case with no spaces or hyphens. For example, a template file for "Foo Skin"
would be pub/skins/foo/foo.tmpl
. To have PmWiki use this skin, you'd add
## Use the Foo Skin. $Skin = 'foo';
to your local/config.php
local configuration file.
In the <skin-name>.tmpl
file:
There are three required layout markers:
-
<!--HTMLHeader-->
- Required in the
<head>
section of the template. PmWiki will insert tags (using$HTMLHeaderFmt
) and CSS selectors (using$HTMLStylesFmt
) in this location. Read more at LayoutAdvanced?. -
<!--PageText-->
- Required in the
<body>
section of the template. Pmwiki will place the page content in this location. -
<!--HTMLFooter-->
- Required in the
<body>
section of the template. Many recipes rely on this directive, and at some point the core may rely on it.
So a template file takes this basic form:
<html> <head> ...some header stuff... <!--HTMLHeader--> ...some more header stuff... </head> <body> ...some HTML... <!--PageText--> ...some more HTML.... <!--HTMLFooter--> </body> </html>
Of course the "header stuff" and "HTML" may include all kinds of things that may change dynamically in many ways.
Often a skin also has at least one CSS stylesheet file and a PHP file. The PHP file must be named either skin.php
or <the skin's folder name>.php
, (e.g. foo.php
for a skin in the pub/skins/foo/
directory). A documentation text file should also be included in a skin's folder. Example files for a Foo Skin:
pub/skins/foo/ |-- README.txt Documentation |-- foo.tmpl Template |-- foo.css CSS stylesheet `-- foo.php PHP script
Page Sections
Sections of output can be denoted using <!--Page...Fmt-->
directives. The default sections can be suppressed by special page directives in wiki markup, where an author can use (:notitle:)
, (:noheader:)
, etc. to turn off components of the page.
When a section is "turned off", output is suppressed from the <!--Page...Fmt-->
until the next directive, a closing <!--/Page...Fmt-->
or the end of the template is reached, whichever comes first.
A page section can also be turned off in a recipe or local configuration script using markup like
SetTmplDisplay('PageXyzFmt', 0);
that will suppress the <!--PageXyzFmt-->
section of the template.
Since PmWiki 2 beta 22, you can make your own Page...Fmt
sections. The reason you might want to do this is that you can then use the SetTmplDisplay()
function in a skin.php
or config.php
to show or hide these sections, or you can create a 5pmhlt%(:noxyz:)
directive so it can be controlled with wiki markup.
Note that SetTmplDisplay()
expects each <!--PageMySectionFmt-->
to have a unique name. If you need to hide or show a bunch of small items scattered about the page, you are best off using CSS classes and the property display:none
.
Skin directives that denote page sections
These are provided by default:
-
<!--PageHeaderFmt-->
- This is a convenient section to place a logo, a link to the homepage, anything you wish to place in the head of the pages. This section may be suppressed with the
(:noheader:)
directive. -
<!--PageTitleFmt-->
- This section is the standard section for placing the title and group links. This section may be suppressed with the
(:notitle:)
directive. -
<!--PageActionFmt-->
- This section is used for wiki action links. It may be suppressed with the
(:noaction:)
directive. -
<!--PageLeftFmt-->
- This section is customarily used for placing a
SideBar
menu, in the form of
<!--wiki:$Group.SideBar $SiteGroup.SideBar-->
. This section may be suppressed with the (:noleft:)
directive.
-
<!--PageRightFmt-->
- This section would be used for right-side content. This section may be suppressed with the
(:noright:)
directive. -
<!--PageFooterFmt-->
- This section may hold footer content, often links to page actions and a "last-modified" line. This section may be suppressed with the
(:nofooter:)
directive.
Special skin directives
PmWiki includes special markers for inserting content from a wiki page, markup in the template, a PHP function, or a file on disk.
-
<!--wiki:pagename[ pagename ...]-->
- This directive allows you to insert content from a wiki page. If multiple pages are specified, the first that exists will be used.
-
<!--markup:wiki-markups-->
- This directive allows the skin author to insert wiki markup like
(:searchbox:)
. Keep it all on one line; no line-breaks! Avoid leading spaces too; something like<!--markup: (:tag:)-->
can get treated as monospace markup (<pre>...</pre>
) the same as using leading whitespace in a wiki page. -
<!--function:FunctionName args-->
- This directive allows you to insert content generated by a PHP function.
-
<!--file:/path/to/file-->
- This directive allows you to insert content from a file.
-
<!--IncludeTemplate: {$Group}.tmpl default.tmpl-->
- This directive allows the inclusion of another template file, see
$SkinTemplateIncludeLevel
.
The Page Title
Each HTML page requires a <title>
tag. The content of it gets used in the heading of the results of search engines like Google. You may wish to include both the site title and the page title in your template.
Here is a basic solution:
<title>$WikiTitle - $Titlespaced</title>
One problem with this is that any (:notitle:)
directive will not suppress $Titlespaced
or $Title
used that way in the Head.
Here is a solution which achieves that, by using a new variable in the template:
<title>$HTMLTitle</title>
Define the variable in the skin PHP file, and add a modified markup for the (:notitle:)
directive:
global $HTMLTitle, $WikiTitle; $HTMLTitle = $WikiTitle.' - '.PageVar($pagename, '$Titlespaced'); ## Markup (:notitle:) Markup('notitle','directives','/\\(:notitle:\\)/', "NoTitle2"); function NoTitle2() { global $HTMLTitle, $WikiTitle; SetTmplDisplay('PageTitleFmt', 0); $HTMLTitle = $WikiTitle; }
Sample basic templates
If all these elements are in place the resulting skin will be fully functional, but rather bare to look at. Here is a bare-bone skin template, with just a minimum of styling so the SideBar
appears beside the page content. This is a table-less template that uses absolute positioning of the sidebar to the left side and a margin in the body tag to shift all other elements to the right.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'> <head> <title>$WikiTitle</title> <style type='text/css'><!-- body { margin:1em 1em 0 14em; } #location { margin:0 0 8px 0; } #sidebar { position:absolute; top:10px; left:1em; width:10em; } --></style> <!--HTMLHeader--> </head> <body> <!--PageHeaderFmt--> <a href='$ScriptUrl/'><img src='$PageLogoUrl' alt='$WikiTitle' border='0' /></a> <!--/PageHeaderFmt--> <hr /> <!--PageTitleFmt--> <h1 id='location'><a href='$ScriptUrl/$Group'>$Group</a> » $Title</h1> <!--PageText--> <hr /> <!--PageFooterFmt--> <a href='$ScriptUrl/$[$Group/RecentChanges]'>$[Recent Changes]</a> | <a href='$PageUrl?action=print' target='_blank'>$[Printable View]</a> | <a href='$PageUrl?action=diff'>$[Page History]</a> | <a href='$PageUrl?action=edit'>$[Edit Page]</a><br /> $[Page last modified on $LastModified] <!--PageLeftFmt--> <div id='sidebar'><!--wiki:$Group.SideBar $SiteGroup.SideBar--></div> <!--HTMLFooter--> </body> </html>
This skin template provides functionality and a basic page layout with the content ahead of the menu, which is advantageous for handheld browsers.
Here's another one, this time with a <!--PageActionsFmt-->
section.
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'> <head> <title>$WikiTitle</title> <style type='text/css'><!-- body { margin:1em 1em 0 14em; } #location { margin:0 0 8px 0; } #sidebar { position:absolute; top:10px; left:1em; width:10em; } #actions { position:absolute; top:10px; right:1em; white-space:nowrap; } #actions ul { list-style:none; margin:0px; padding:0px; } #actions li { display:inline; margin:0px 5px; } --></style> <!--HTMLHeader--> </head> <body> <!--PageHeaderFmt--> <a href='$ScriptUrl/'><img src='$PageLogoUrl' alt='$WikiTitle' border='0' /></a> <!--/PageHeaderFmt--> <hr /> <!--PageTitleFmt--> <h1 id='location'><a href='$ScriptUrl/$Group'>$Group</a> » $Title</h1> <!--PageText--> <hr /> <!--PageLeftFmt--> <div id='sidebar'><!--wiki:$Group.SideBar $SiteGroup.SideBar--></div> <!--PageActionsFmt--> <div id='actions'><!--wiki:$SiteGroup.PageActions--></div> <!--PageFooterFmt--> <a href='$ScriptUrl/$[$Group/RecentChanges]'>$[Recent Changes]</a> (<a href='$ScriptUrl/$[$SiteGroup/AllRecentChanges]'>$[All]</a>)<br /> $[Page last modified on $LastModified] <!--HTMLFooter--> </body> </html>
Translation
The text inside $[
]
brackets (e.g. $[Page History]
, $[Recent Changes]
, etc.) will be processed by PmWiki's internationalization feature (see PmWiki.Internationalizations), and replaced by the appropriate text from the current XLPage.
CSS Files: Controlling Page Style
Page style can come from four places:
- Core PmWiki styles
- Recipe styles
- Skin styles
- Local stylesheets
A skin can provide styling three ways:
- A stylesheet linked from the
skin.tmpl
template file - From
skin.php
using$HTMLStylesFmt
- A stylesheet linked from
skin.php
using$HTMLHeaderFmt
The main difference among these three is the order in which styles appear, which affects how skin styles take precedence compared to styles from the other sources. This is important because with CSS values assigned to attributes later will take precedence over what was assigned earlier. The last wins.
A single-stylesheet approach
Using this method, which is preferred by some very experienced skin authors, a single stylesheet is loaded via a $HTMLHeaderFmt
definition in skin.php
:
global $HTMLHeaderFmt; $HTMLHeaderFmt['skin'] = " <link rel='stylesheet' href='\$SkinDirUrl/skin.css' type='text/css' />\n ";
The <link rel='stylesheet' ... />
line is omitted from the skin template
entirely.
Here a hierarchy is created where where the skin styles override the core PmWiki styles and recipe styles from local configuration files such as config.php
. The wiki administrator can take final control over styling using local stylesheets (but not recipes).
Of course multiple stylesheets may be loaded this way also, allowing logic to determine which stylesheet links appear late in the <head> section of the page. The point is that using $HTMLHeaderFmt
in skin.php
to link a stylesheet causes the load order of styles to be
- Core PmWiki styles and local/admin styles from recipes
- Skin stylesheet styles
- Local/admin styles from /pub/css/local.css, /pub/css/$Group.css, and /pub/css/$Group.$Name.css
Two-stylesheet approach
The default PmWiki skin, which has no skin.php
file, uses a single stylesheet linked from the skin.tmpl
file.
<link rel='stylesheet' href='$SkinDirUrl/skin.css' type='text/css' /> <!--HTMLHeader-->
The order of the two lines is very important. If they are reversed, the skin stylesheet link appears extremely late in the <head> section and the wiki administrator can't override the skin's styling. With the two lines in the correct order the load order of styles is
- Skin stylesheet styles
- Core PmWiki styles and
local/admin
styles from recipes Local/admin
styles from/pub/css/local.css
,/pub/css/$Group.css
, and/pub/css/$Group.$Name.css
A skin author may, however, want to override the PmWiki defaults.
The most reliable way to override the PmWiki defaults is to insert a second stylesheet using $HTMLHeaderFmt
in skin.php
, similar to the single-stylesheet method above.
global $HTMLHeaderFmt; $HTMLHeaderFmt['skin'] = " <link rel='stylesheet' href='\$SkinDirUrl/skin2.css' type='text/css' />\n ";
By linking stylesheets from both skin.tmpl
and skin.php
, the skin author can allow some styles to be overridden by recipes, but not others. The load order of styles is
- Skin styles from a stylesheet linked from the skin's template (
skin.tmpl
) - Core PmWiki styles and local/admin styles from recipes
- Skin styles from a stylesheet linked from skin.php using
$HTMLHeaderFmt
- Local/admin styles from
/pub/css/local.css
,/pub/css/$Group.css
, and/pub/css/$Group.$Name.css
Why pmwiki.php
embeds CSS
The reason why pmwiki.php
embeds the CSS is to
reduce the overhead on skin authors and to preserve an upgrade path.
The styles defined by $HTMLStylesFmt
['pmwiki'] are really the
essential ones that are needed for several of PmWiki's core
markups. Without these in place, a number of PmWiki's built-in
features simply won't work.
In the past many have indicated that we should "dis-embed" the CSS
from pmwiki.php
, and require every skin author to include these
minimal definitions (possibly modified) in the skin's .css file.
This slightly increases the work required to develop a skin,
because the skin author has to copy the definitions somewhere.
But more importantly, moving all of PmWiki's CSS properties into
the skins can make future PmWiki upgrades a bit of a pain, because
when a style definition is added or changed in the core, every
existing skin would then need to be modified to incorporate the
new minimal specification. I prefer to avoid that level of
coupling between core and skins.
The current approach allows skin CSS properties to be completely
decoupled from CSS properties needed for core and recipe markups.
In fact, in this sense pmwiki.php
uses $HTMLStylesFmt
[] in exactly
the same way that recipes do. $HTMLStylesFmt
[] allows
module-specific CSS properties to be injected into the output
without the skin ever having to be aware of them, while preserving
the capability for skins and/or administrators to override the
module-specific properties if they wish to do so.
Replacing default CSS entirely
A skin that wants to completely replace the pmwiki.php
defaults
with its own can do the following in skin.php
:
global $HTMLStylesFmt; $HTMLStylesFmt['pmwiki'] = ''; $HTMLStylesFmt['diff'] = ''; $HTMLStylesFmt['simuledit'] = ''; $HTMLStylesFmt['markup'] = ''; $HTMLStylesFmt['urlapprove']= ''; $HTMLStylesFmt['vardoc'] = ''; $HTMLStylesFmt['wikistyles']= '';
Bundling wiki pages
Starting with PmWiki 2.0.beta51 you can distribute skin-specific pages with your skin. What those pages might contain is limited only by your imagination.
Custom Edit Form and Preferences Page
You can use skin-specific pages to bundle a custom Edit Form and a Preferences page with your skin. Here's an example block of code from the Foo Skin's PHP script:
## Add a custom page storage location for bundled pages. $PageStorePath = dirname(__FILE__)."/wikilib.d/\$FullName"; $where = count($WikiLibDirs); if ($where>1) $where--; array_splice($WikiLibDirs, $where, 0, array(new PageStore($PageStorePath))); ## Enable the Preferences page. XLPage('foo', "$SiteGroup.FooXLPage"); array_splice($XLLangs, -1, 0, array_shift($XLLangs)); ## Enable the skin's custom EditForm, either ## configurable via a prefs page (XLPage) or not. if ($EnableEditFormPrefs == TRUE) { SDV($PageEditForm, "$[$SiteGroup.FooEditForm]"); } else { SDV($PageEditForm, "$SiteGroup.FooEditForm"); }
$WikiLibDirs
MUST be global for above code to work | Finar)
The customized edit page is pub/skins/foo/wikilib.d/Site.FooEditPage
and the Preferences page is pub/skins/foo/wikilib.d/Site.FooXLPage
, which contains the following wiki markup:
This is the Foo Skin Preferences page:
[= # Define a custom edit form. 'Site.EditForm' => 'Site.FooEditForm', =]
The Preferences page tells PmWiki to use the custom Edit Form, but it could also be used for other translations as well. The preferences page would also be a good place to put translation strings that are specific to the skin.
An admin who doesn't want users to create custom edit forms or other customizations can disable user preferences entirely by setting $EnablePrefs = 0
.
JavaScript Tip
"Favouring writers over readers": Putting the following code into the onload-handler (i.e. in window.onload = function(){ ... }
) of the template gets the cursor focused on the first form element of the first form in the content area after the page is loaded, i.e. exactly where we want to have it (username field, edit field, etc.):
var contentNode = document.getElementById("content"); var contentForms = contentNode.getElementsByTagName("form"); var firstContentForm = contentForms[0]; if (firstContentForm) { for (var i=0; i < firstContentForm.length; i++) { if (firstContentForm.elements[i].type != "hidden") { firstContentForm.elements[i].focus(); break; } } }
Enclose <!--PageText-->
by an appropriate <div id="content">
clause for it to work.
Tested with NotSoSimpleSkin. ThomasP (PmWiki 2.1.beta25)
Notes
In references to skin.php
above - skin can both be the literal 'skin' or the name of the skin in question, the latter takes precedence over the former. Chris Stiles
About <!--function:func param1 param2 ...
: This will call the PHP function func
with parameters param1
, param2
, ... and have the function's result inserted into the text. You can call any function that's defined by PHP, PmWiki, or an installed module/cookbook/skin recipe. (Warning: PmWiki functions may change name or functionality from release to release. Avoid calling them unless you have either foregone ever updating PmWiki, or asked in the mailing list whether using that function is OK. All functions might emit error messages if not called properly, most likely destroying your nice skin layout. You best bet is to write iron-clad functions in your skin.php
that would degrade gracefully rather than let any error message out.)
See also
- SkinTemplates for more information about creating advanced skins
- Skins:TestPageDirectives a recipe to help you test your skin with different directives like
(:noheader:)
- Cookbook:Removing HTML Styles removing PmWiki's default styles from the page head
- Cookbook:CSS In Wiki Pages having styles defined in a wiki page
- Cookbook:LocalCSS having styles defined in a wiki page
- Skins list of skins
- Cookbook:Module Guidelines general cookbook recipe guidelines
User notes? : If you use, used or reviewed "Skin Guidelines", you can add your name. These statistics appear in the Skins listings and will help newcomers browsing through the wiki.