diff options
| author | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
|---|---|---|
| committer | cashmere <cashmere@autistici.org> | 2026-09-04 09:05:24 +0200 |
| commit | 4f49b16079b27d7ce633619e4cddfdf2bffbc952 (patch) | |
| tree | c296c1e7f3c3e2f6859c6d3a1b1b208779b89679 /www/customskin.md | |
| download | relic-4f49b16079b27d7ce633619e4cddfdf2bffbc952.tar.gz | |
Diffstat (limited to 'www/customskin.md')
| -rw-r--r-- | www/customskin.md | 598 |
1 files changed, 598 insertions, 0 deletions
diff --git a/www/customskin.md b/www/customskin.md new file mode 100644 index 0000000..2df2b13 --- /dev/null +++ b/www/customskin.md @@ -0,0 +1,598 @@ +# Skinning the Fossil Web Interface + +The Fossil web interface comes with a pre-configured look and feel. The default +look and feel works fine in many situations. However, you may want to change +the look and feel (the "skin") of Fossil to better suite your own individual tastes. +This document provides background information to aid you in that task. + +## <a id="builtin"></a>Built-in Skins + +Fossil comes with [multiple built-in skins](/skins). If the default skin does not +suite your tastes, perhaps one of the other built-in skins will work better. +If nothing else, the built-in skins can serve as examples or templates that +you can use to develop your own custom skin. + +The sources to these built-ins can +be found in the Fossil source tree under the skins/ folder. The +[skins/](/dir?ci=trunk&name=skins) +folder contains a separate subfolder for each built-in skin, with each +subfolders holding at least these five files: + + * css.txt + * details.txt + * footer.txt + * header.txt + * js.txt + +Try out the built-in skins by using the --skin option on the +[fossil ui](/help/ui) or [fossil server](/help/server) commands. + +## <a id="sharing"></a>Sharing Skins + +The skin of a repository is not part of the versioned state and does not +"push" or "pull" like checked-in files. The skin is local to the +repository. However, skins can be shared between repositories using +the [fossil config](/help/configuration) command. +The "fossil config push skin" command will send the local skin to a remote +repository and the "fossil config pull skin" command will import a skin +from a remote repository. The "fossil config export skin FILENAME" +will export the skin for a repository into a file FILENAME. This file +can then be imported into a different repository using the +"fossil config import FILENAME" command. Unlike "push" and "pull", +the "export" and "import" commands are able to move skins between +repositories for different projects. So, for example, if you have a +group of related repositories, you can develop a skin for one of them, +then get a consistent look across all the repositories by exporting +the skin from the first repository and importing into all the others. + +The file generated by "fossil config export" could be checked into +one of your repositories and versioned, if desired. This will not +automatically change the skin when looking backwards in time, but it +will provide an historical record of what the skin used to be and +allow the historical look of the repositories to be recreated if +necessary. + +When cloning a repository, the skin of the new repository is initialized to +the skin of the repository from which it was cloned. + +# Structure Of A Fossil Web Page + +Every HTML page generated by Fossil has the same basic structure: + +| Fossil-Generated HTML Header | +| Skin Header | +| Fossil-Generated Content | +| Skin Footer | +| Fossil-Generated HTML Footer | + +By default, Fossil starts every generated HTML page with this: + + <html> + <head> + <base href="..."> + <meta http-equiv="Content-Security-Policy" content="...."> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <title>....</title> + <link rel="stylesheet" href="..." type="text/css"> + </head> + <body class="FEATURE"> + +Fossil used to require a static version of this in every skin’s Header +area, but over time, we have found good cause to generate multiple +elements at runtime. + +One such is the `FEATURE` element, being either the top-level HTTP +request routing element (e.g. `doc`) or an aggregate feature class that +groups multiple routes under a single name. A prime example is `forum`, +which groups the `/forummain`, `/forumpost`, and `/forume2` routes, +allowing per-feature CSS. For instance, to style `<blockquote>` tags +specially for forum posts written in Markdown, leaving all other block +quotes alone, you could say: + + body.forum div.markdown blockquote { + margin-left: 10px; + } + +You can [override this generated HTML header](#override) by including a +“`<body>`” tag somewhere in the Header area of the skin, but it is +almost always best to limit a custom skin’s Header section to something +like this: + +<div class="sidebar" id="version-2.24">Prior to Fossil 2.24, we used +generic `<div>` elements to mark up these sections of the header, but we +switched to these semantic tag names to give browser accessibility +features more freedom to do intelligent things with the page content. +Those who made custom skins based on the old way of doing things will +need to track this change when upgrading, else the corresponding CSS +will mistarget the page header elements. Also, if you’re using Fossil’s +chat feature, failing to track this change will cause it to miscalculate +the message area size, resulting in double scrollbars. Simply diffing +your custom header in the skin editor against the stock version should +be sufficient to show what you need to change.</div> + + + <header> + ... + </header> + <nav class="mainmenu" title="Main Menu"> + ... + </nav> + <nav id="hbdrop" class="hbdrop" title="sitemap"></nav> + +See the stock skins’ headers for ideas of what to put in place of the +ellipses. + +The Fossil-generated Content section immediately follows this Header. +It will look like this: + + <div class="content"> + ... Fossil-generated content here ... + </div> + +After the Content is the custom Skin Footer section which should +follow this template: + + <footer> + ... skin-specific stuff here ... + </footer> + +As with the `<header>` change called out above, this, too, is a breaking +change in Fossil 2.24. + +Finally, Fossil always adds its own footer (unless overridden) +to close out the generated HTML: + + </body> + </html> + +## <a id="mainmenu"></a>Changing the Main Menu Contents + +The actual text content of the skin’s main menu is not +part of the skin proper if you’re using one of the stock skins. +If you look at the Header section of the skin, you’ll find a +`<div class="mainmenu">` element whose contents are set by a short +[TH1](./th1.md) script from the contents of the **Main Menu** section of +the Setup → Configuration screen. + +This feature allows the main menu contents to stay the same across +different skins, so you no longer have to reapply menu customizations +when trying different skins. + +See the [`capexpr`](./th1.md#capexpr) section of the TH1 docs for help +on interpreting the default contents of this block. + + +## <a id="override"></a>Overriding the HTML Header and Footer + +Notice that the `<html>`, `<head>`, and opening `<body>` +elements at the beginning of the document, +and the closing `</body>` and `</html>` elements at the end are automatically +generated by Fossil. This is recommended. + +However, for maximum design flexibility, Fossil allows those elements to be +supplied as part of the configurable Skin Header and Skin Footer. +If the Skin Header contains the text "`<body`", then Fossil assumes that +the Skin Header and Skin Footer will handle all of the `<html>`, +`<head>`, and `<body>` text itself, and the Fossil-generated header and +footer will be blank. + +When overriding the HTML Header in this way, you will probably want to use some +of the [TH1 variables documented below](#vars) such as `$stylesheet_url` +to avoid hand-writing code that Fossil can generate for you. + +# Designing, Debugging, and Installing A Custom Skin + +It is possible to develop a new skin from scratch. But a better and easier +approach is to use one of the existing built-in skins as a baseline and +make incremental modifications, testing after each step, to obtain the +desired result. + +The skin is controlled by five files: + +<dl> +<dt><b>css.txt</b></dt> + +<dd>The css.txt file is the text of the CSS for Fossil. +Fossil might add additional CSS elements after +the css.txt file, if it sees that the css.txt omits some +CSS components that Fossil needs. But for the most part, +the content of the css.txt is the CSS for the page.</dd> + +<dt><b>details.txt</b><dt> + +<dd>The details.txt file is short list of settings that control +the look and feel, mostly of the timeline. The default +details.txt file looks like this: + +<pre> +pikchr-background: "" +pikchr-fontscale: "" +pikchr-foreground: "" +pikchr-scale: "" +timeline-arrowheads: 1 +timeline-circle-nodes: 1 +timeline-color-graph-lines: 1 +white-foreground: 0 +</pre> + +The three "timeline-" settings in details.txt control the appearance +of certain aspects of the timeline graph. The number on the +right is a boolean - "1" to activate the feature and "0" to +disable it. The "white-foreground:" setting should be set to +"1" if the page color has light-color text on a darker background, +and "0" if the page has dark text on a light-colored background. + +If the "pikchr-foreground" setting +is defined and is not an empty string then it specifies a +foreground color to use for [pikchr diagrams](./pikchr.md). The +default pikchr foreground color is black, or white if the +"white-foreground" boolean is set. The "pikchr-background" +settings does the same for the pikchr diagram background color. +If the "pikchr-fontscale" and "pikchr-scale" values are not +empty strings, then they should be floating point values (close +to 1.0) that specify relative scaling of the fonts in pikchr +diagrams and other elements of the diagrams, respectively. +</dd> + +<dt><b>footer.txt</b> and <b>header.txt</b></dt> + +<dd>The footer.txt and header.txt files contain the Skin Footer +and Skin Header respectively. Of these, the Skin Header is +the most important, as it contains the markup used to generate +the banner and menu bar for each page. + +Both the footer.txt and header.txt file are +[processed using TH1](#headfoot) prior to being output as +part of the overall web page.</dd> + +<dt><b>js.txt</b></dt> + +<dd>The js.txt file is optional. It is intended to be javascript. +The complete text of this javascript might be inserted into +the Skin Footer, after being processed using TH1, using +code like the following in the "footer.txt" file: + +<pre> +<script nonce="$nonce"> + <th1>styleScript</th1> +</script> +</pre> + +The js.txt file was originally used to insert javascript +that controls the hamburger menu in the default skin. More +recently, the javascript for the hamburger menu was moved into +a separate built-in file. Skins that use the hamburger menu +typically cause the javascript to be loaded by including the +following TH1 code in the "header.txt" file: + +<pre> +<th1>builtin_request_js hbmenu.js</th1> +</pre> + +The difference between `styleScript` and `builtin_request_js` +is that the `styleScript` command interprets the file +using TH1 and injects the content directly into the output +stream, whereas the `builtin_request_js` command inserts the +Javascript verbatim and does so at some unspecified future time +down inside the Fossil-generated footer. +You can use either +approach in custom skins that you create. + +Note that the "js.txt" file is *not* automatically inserted into +the generate HTML for a page. You, the skin designer, must +cause the javascript to be inserted by issuing appropriate +TH1 commands in the "header.txt" or "footer.txt" files.</dd> +</dl> + +Developing a new skin is simply a matter of creating appropriate +versions of these five control files. + +### Skin Development Using The Web Interface + +Users with admin privileges can use the Admin/Skin configuration page +on the web interface to develop a new skin. The development of a new +skin occurs without disrupting the existing skin. So you can work on +a new skin for a Fossil instance while the existing skin is still in +active use. + +The new skin is a "draft" skin. You initialize one of 9 draft skins +to either the current skin or to one of the built-in skins. Then +use forms to edit the 5 control files described above. The new +skin can be tested after each edit. Finally, once the new skin is +working as desired, the draft skin is "published" and becomes the +new live skin that most users see. + +### Skin Development Using A Local Text Editor + +An alternative approach is to copy the five control files for your +baseline skin into a temporary working directory (here called +"./newskin") and then launch the [fossil ui](/help/ui) command +with the "--skin ./newskin" option. If the argument to the --skin +option contains a "/" character, then the five control files are +read out of the directory named. You can then edit the control +files in the ./newskin folder using you favorite text editor, and +press "Reload" on your browser to see the effects. + + +### Disabling The Web Browser Cache During Development + +Fossil is aggressive about asking the web browser to cache +resources. While developing a new skin, it is often helpful to +put your web browser into developer mode and disable the cache. +If you fail to do this, then you might make some change to your skin +under development and press "Reload" only to find that the display +did not change. After you have finished work your skin, the +caches should synchronize with your new design and you can reactivate +your web browser's cache and take it out of developer mode. + +## <a id="headfoot"></a>Header and Footer Processing + +The `header.txt` and `footer.txt` control files of a skin are the HTML text +of the Skin Header and Skin Footer, except that before being inserted +into the output stream, the text is run through a +[TH1 interpreter](./th1.md) that might adjust the text as follows: + + * All text within <th1>...</th1> is omitted from the + output and is instead run as a TH1 script. That TH1 + script has the opportunity to insert new text in place of itself, + or to inhibit or enable the output of subsequent text. + + * Text of the form "$NAME" or "$<NAME>" is replaced with + the value of the TH1 variable NAME. See the [TH1 Variables](#vars) + section for more information on the two possible variable formats. + +For example, first few lines of a typical Skin Header will look +like this: + + <header> + <div class="title"><h1>$<project_name></h1>$<title>/div> + +After variables are substituted by TH1, that will look more like this: + + <header> + <div class="title"><h1>Project Name</h1>Page Title</div> + +As you can see, two TH1 variable substitutions were done. + +The same TH1 interpreter is used for both the header and the footer +and for all scripts contained within them both. Hence, any global +TH1 variables that are set by the header are available to the footer. + +## <a id="menu"></a>Customizing the ≡ Hamburger Menu + +The menu bar of the default skin has an entry to open a drop-down menu with +additional navigation links, represented by the ≡ button (hence the name +"hamburger menu"). The Javascript logic to open and close the hamburger menu +when the button is clicked is usually handled by a script named +"hbmenu.js" that is one of the [built-in resource files](/test-builtin-files) +that are part of Fossil. + +The ≡ button for the hamburger menu is added to the menu bar by the following +TH1 commands in the `header.txt` file, right before the menu bar links: + + html "<a id='hbbtn' href='$home/sitemap'>☰</a>" + builtin_request_js hbmenu.js + +The hamburger button can be repositioned between the other menu links (but the +drop-down menu is always left-aligned with the menu bar), or it can be removed +by deleting the above statements. The "html" statement inserts the appropriate +`<a>` for the hamburger menu button (some skins require something slightly +different - for example the ardoise skins wants "`<li><a>`"). The +"builtin_request_js hbmenu.js" asks Fossil to include the "hbmenu.js" +resource files in the Fossil-generated footer. + +The hbmenu.js script requires +the following `<div>` element somewhere in your header, in which to build +the hamburger menu. + + <div id='hbdrop'></div> + +Out of the box, the contents of the panel is populated with the [Site +Map](/sitemap), but only if the panel does not already contain any HTML +elements (that is, not just comments, plain text or non-presentational white +space). So the hamburger menu can be customized by replacing the empty `<div +id='hbdrop'></div>` element with a menu structure knitted according to the +following template: + + <div id="hbdrop" data-anim-ms="400"> + <ul class="columns" style="column-width: 20em; column-count: auto"> + <!-- NEW GROUP WITH HEADING LINK --> + <li> + <a href="$home$index_page">Link: Home</a> + <ul> + <li><a href="$home/timeline">Link: Timeline</a></li> + <li><a href="$home/dir?ci=tip">Link: File List</a></li> + </ul> + </li> + <!-- NEW GROUP WITH HEADING TEXT --> + <li> + Heading Text + <ul> + <li><a href="$home/doc/trunk/www/customskin.md">Link: Theming</a></li> + <li><a href="$home/doc/trunk/www/th1.md">Link: TH1 Scripts</a></li> + </ul> + </li> + <!-- NEXT GROUP GOES HERE --> + </ul> + </div> + +The custom `data-anim-ms` attribute can be added to the panel element to direct +the Javascript logic to override the default menu animation duration of 400 ms. +A faster animation duration of 80-200 ms may be preferred for smaller menus. The +animation is disabled by setting the attribute to `"0"`. + + +## <a id="vars"></a>TH1 Variables + +Before expanding the TH1 within the header and footer, Fossil first +initializes a number of TH1 variables to values that depend on +repository settings and the specific page being generated. + +Variables holding text that is loaded from "external, potentially untrusted" +sources (including the repository settings) are treated as [tainted strings] +(./th1.md#taint) and must be noted in the `$<NAME>` form, instead of `$NAME`, +or they may trigger an error (see the linked document for details). The +`$<NAME>` form corresponds to the TH1 statement `puts [ htmlize "$NAME" ]`, +where the [htmlize](./th1.md#htmlize) function escapes the tainted string, +making it safe for output in HTML code. + + + * **`project_name`** - The project_name variable is filled with the + name of the project as configured under the Admin/Configuration + menu. This is a [tainted string](./th1.md#taint) variable and must + be used as `$<project_name>`. + + * **`project_description`** - The project_description variable is + filled with the description of the project as configured under + the Admin/Configuration menu. This is a [tainted string] + (./th1.md#taint) variable and must be used as `$<project_description>`. + + * **`mainmenu`** - The mainmenu variable contains a TCL list with the main + menu entries. See the [mainmenu](/help/mainmenu) setting for details. + + * **`title`** - The title variable holds the title of the page being + generated. + + The title variable is special in that it is deleted after + the header script runs and before the footer script. This is + necessary to avoid a conflict with a variable by the same name used + in my ticket-screen scripts. + + * **`baseurl`** - The root of the URL namespace for this server. + + * **`secureurl`** - The same as $baseurl except that if the scheme is + "http:" it is changed to "https:" + + * **`home`** - The $baseurl without the scheme and hostname. For example, + if the $baseurl is "http://projectX.com/cgi-bin/fossil" then the + $home will be just "/cgi-bin/fossil". + + * **`index_page`** - The landing page URI as + specified by the Admin/Configuration setup page. + + * **`current_page`** - The name of the page currently being processed, + without the leading "/" and without query parameters. + Examples: "timeline", "doc/trunk/README.txt", "wiki". + + * **`current_checkin`** - The [check-in name](./checkin_names.wiki) to + which the current + [/ci](/help/www/ci), + [/dir](/help/www/dir), + [/tree](/help/www/tree), + [/timeline](/help/www/timeline) or + [/vinfo](/help/www/vinfo) + page refers, or undefined for all other pages. This variable is + derived from query parameters and is therefore a [tainted string] + (./th1.md#taint). + + * **`csrf_token`** - A token used to prevent cross-site request forgery. + + * **`default_csp`** - [Fossil’s default CSP](./defcsp.md) unless + [overridden by custom TH1 code](./defcsp.md#th1). Useful within + the skin for inserting the CSP into a `<meta>` tag within [a + custom `<head>` element](#headfoot). + + * **`nonce`** - The value of the cryptographic nonce for the request + being processed. + + * **`release_version`** - The release version of Fossil. Ex: "1.31" + + * **`manifest_version`** - A prefix on the check-in hash of the + specific version of fossil that is running. Ex: "\[47bb6432a1\]" + + * **`manifest_date`** - The date of the source-code check-in for the + version of fossil that is running. + + * **`compiler_name`** - The name and version of the compiler used to + build the fossil executable. + + * **`login`** - This variable only exists if the user has logged in. + The value is the username of the user. + + * **`stylesheet_url`** - A URL for the internal style-sheet maintained + by Fossil. + + * **`logo_image_url`** - A URL for the logo image for this project, as + configured on the Admin/Logo page. + + * **`background_image_url`** - A URL for a background image for this + project, as configured on the Admin/Logo page. + +All of the above are variables in the sense that either the header or the +footer is free to change or erase them. But they should probably be treated +as constants. New predefined values are likely to be added in future +releases of Fossil. + + +## <a id="procedure"></a>Suggested Skin Customization Procedure + +Developers are free, of course, to develop new skins using any method they +want, but the following is a technique that has worked well in the past and +can serve as a starting point for future work: + + 1. Select a built-in skin that is closest to the desired look. Make + copies of the css, footer, and header into files name "css.txt", + "details.txt", + "footer.txt", and "header.txt" in some temporary directory. + + If the Fossil source code is available, then these three files can + be copied directly out of one of the subdirectories under skins. If + sources are not easily at hand, then a copy/paste out of the + CSS, footer, and header editing screens under the Admin menu will + work just as well. The important point is that the three files + be named exactly "css.txt", "footer.txt", and "header.txt" and that + they all be in the same directory. + + 2. Run the [fossil ui](/help/ui) command with an extra + option "--skin SKINDIR" where SKINDIR is the name of the directory + in which the three txt files were stored in step 1. This will bring + up the Fossil website using the tree files in SKINDIR. + + 3. Edit the *.txt files in SKINDIR. After making each small change, + press Reload on the web browser to see the effect of that change. + Iterate until the desired look is achieved. + + 4. Copy/paste the resulting css.txt, details.txt, + header.txt, and footer.txt files + into the CSS, details, header, and footer configuration screens + under the Admin/Skins menu. Alternately, import them using the + process described below. + +An alternative to step 4 is to convert the skin files into a form +which can be imported into a repository using `fossil config import`. +It requires compiling [a small tool from the fossil source +tree](/file/tools/skintxt2config.c): + +> +``` +$ cc -o s2c /path/to/fossil/checkout/tools/skintxt2config.c +``` + +With that in place, the custom skin files can be converted with: + +> +``` +$ ./s2c yourskin/*.txt > skin.config +``` + +It can be imported into an arbitrary fossil repository with: + +> +``` +$ fossil config import skin.config +``` + +And it can be pushed to a remote repository with: + +> +``` +$ fossil config push skin +``` + +That approach has proven to be an effective way to locally develop +skin changes then push them to a "live" site. + + +## See Also + +* [Customizing the Timeline Graph](customgraph.md) |
