The hgweb interface is completely themable. All output is generated from templates, nothing is hardcoded. Here's how it works:

The Map File

The hgweb engine looks up templates in a file named templates/map (or templates/map-<style> if a style is specified). It looks something like this:

default = "changelog"
header = header.tmpl
footer = footer.tmpl
search = search.tmpl
changelog = changelog.tmpl
naventry = "<a href="?cmd=changelog;rev=#rev#">#label#</a> "
filedifflink = "<a href="?cmd=filediff;node=#node#;file=#file#">#file#</a> "
filenodelink = "<a href="?cmd=file;filenode=#filenode#;file=#file#">#file#</a> "
...

This maps a template name to either a file (for example 'header' is found in header.tmpl) or a simple quoted string (eg naventry). The latter is used for small templates where a separate file would be awkward.

A style is simply a separate map file and possibly some additional template files. Mercurial comes with a few styles already, including the default HTML style, an XML style for RSS feeds, and a 'raw' style that allows getting patches and source files as plain text.

Templates

Each template is simply a file or string with a number of tags of the form #variable# (or {variable}) that get replaced with the appropriate text when the template gets processed. For example, here's the template for the tags page in the default theme:

#header#
<title>#repo#: tags</title>
</head>
<body>

<div class="buttons">
<a href="?cmd=changelog;rev=#rev#">changelog</a>
<a href="?cmd=manifest;manifest=#manifest#;path=/">manifest</a>
</div>

<h2>tags:</h2>

<ul id="tagEntries">
#entries%tagentry#
</ul>

#footer#

Interpolations

Note the #entries%tagentry# line above. The entries variable is actually a list of variable mappings, and the % syntax instructs the template engine to apply the tagentry format to each of them.

Filters

There is also a set of filters that can be applied to replacements, for example:

<title>#repo|escape#: changeset #node|short#</title>

This applies the 'escape' filter to the 'desc' variable and the 'short' filter to the node variable. Multiple filters can be applied to a single variable:

<h2>changeset: #desc|escape|firstline#</h2>

The available filters include:

Creating your own theme

Now that you know how the templates work, creating your own theme is simply a matter of copying the stock map file to map-mytheme, modifying it, and copying any template files you modify. Then you can add the following to your .hg/hgrc file:

[web]
style = mytheme