Maven: The Complete Reference

10.7. Customizing Site Appearance

The default Maven template leaves much to be desired. If you wish to customize your project’s website beyond simply adding content, navigational elements, and custom logos. Maven offers several mechanisms for customizing your website that offer successively deeper access to content decoration and website structure. For small, per-project tweaks, providing a custom site.css is often enough. However, if you want your customizations to be reusable across multiple projects, or if your customizations involve changing the XHTML that Maven generates, you should consider creating your own Maven website skin.

10.7.1. Customizing the Site CSS

The easiest way to affect the look and feel of your project’s web site is through the project’s site.css. Just like any images or XHTML content you provide for the website, the site.css file goes in the src/site/resources directory. Maven expects this file to be in the src/site/resources/css subdirectory. With CSS it is possible to change text styling properties, layout properties, and even add background images and custom bullet graphics. For example, if we decided that to make the menu heading stand out a little more, we might try the following style in src/site/resources/css/site.css:

#navcolumn h5 {
font-size: smaller;
border: 1px solid #aaaaaa;
background-color: #bbb;
margin-top: 7px;
margin-bottom: 2px;
padding-top: 2px;
padding-left: 2px;
color: #000;
}

When you regenerate the website, the menu headers should be framed by a gray background and separated from the rest of the menu by some extra margin space. Using this file, any structure in the Maven-generated website can be decorated with custom CSS. When you change site.css in a specific Maven project, the changes will apply to that specific project. If you are interested in making changes that will apply to more than one Maven project, you can create a custom skin for the Maven Site plugin.

Tip

There is no good reference for the structure of the default Maven site template. If you are attempting to customize the style of your Maven project, you should use a Firefox extension like Firebug as a tool to explore the DOM for your project’s pages.

10.7.2. Create a Custom Site Template

If the default Maven Site structure just doesn’t do it for you, you can always customize the Maven site template. Customizing the Maven Site template gives you complete control over the ultimate output of the Maven plugin, and it is possible to customize your project’s site template to the point where it hardly resembles the structure of a default Maven site template.

The Site plugin uses a rendering engine called Doxia, which in turn uses a Velocity template to render the XHTML for each page. To change the page structure that is rendered by default, we can configure the site plugin in our POM to use a custom page template. The site template is fairly complex, and you’ll need to have a good starting point for your customization. Start by copying the default Velocity template from Doxia’s Subversion repository default-site.vm to src/site/site.vm. This template is written in a templating language called Velocity. Velocity is a simple templating language which supports simple macro definition and allows you to access an object’s methods and properties using simple notation. A full introduction is beyond the scope of this book, for more information about Velocity and a full introduction please go to the Velocity project site at http://velocity.apache.org.

The default-site.xml template is fairly involved, but the change required to customize the left-hand menu is relatively straightforward. If you are trying to change the appearance of a menuItem, locate the menuItem macro. It resides in a section that looks like this:

#macro ( menuItem $item )

...

#end

If you replace the macro definition with the macro definition listed below, you will injects Javascript references into each menu item which will allow the reader to expand or collapse the menu tree without suffering through a full page reload:

#macro ( menuItem $item $listCount )
#set ( $collapse = "none" )
#set ( $currentItemHref = $PathTool.calculateLink( $item.href,
$relativePath ) )
#set ( $currentItemHref = $currentItemHref.replaceAll( "\\", "/" ) )

#if ( $item && $item.items && $item.items.size() > 0 )
#if ( $item.collapse == false )
#set ( $collapse = "collapsed" )
#else
## By default collapsed
#set ( $collapse = "collapsed" )
#end

#set ( $display = false )
#displayTree( $display $item )

#if ( $alignedFileName == $currentItemHref || $display )
#set ( $collapse = "expanded" )
#end
#end
<li class="$collapse">
#if ( $item.img )
#if ( ! ( $item.img.toLowerCase().startsWith("http") ||
$item.img.toLowerCase().startsWith("https") ) )
#set ( $src = $PathTool.calculateLink( $item.img, $relativePath ) )
#set ( $src = $item.img.replaceAll( "\\", "/" ) )
<img src="$src"/>
#else
<img src="$item.img" align="absbottom" style="border-width: 0"/>
#end
#end
#if ( $alignedFileName == $currentItemHref )
<strong>$item.name</strong>
#else
#if ( $item && $item.items && $item.items.size() > 0 )
<a onclick="expand('list$listCount')"
style="cursor:pointer">$item.name</a>
#else
<a href="$currentItemHref">$item.name</a>
#end
#end
#if ( $item && $item.items && $item.items.size() > 0 )
#if ( $collapse == "expanded" )
<ul id="list$listCount" style="display:block">
#else
<ul id="list$listCount" style="display:none">
#end
#foreach( $subitem in $item.items )
#set ( $listCounter = $listCounter + 1 )
#menuItem( $subitem $listCounter )
#end
</ul>
#end
</li>
#end

This change adds a new parameter to the menuItem macro. For the new functionality to work, you will need to change references to this macro, or the resulting template may produce unwanted or internally inconsistent XHTML. To finish changing these references, make a similar replacement in the mainMenu macro. Find this macro by looking for something similar to the following template snippet:

#macro ( mainMenu $menus )
...
#end

Replace the mainMenu macro with the following implementation:

#macro ( mainMenu $menus )
#set ( $counter = 0 )
#set ( $listCounter = 0 )
#foreach( $menu in $menus )
#if ( $menu.name )
<h5 onclick="expand('menu$counter')">$menu.name</h5>
#end
<ul id="menu$counter" style="display:block">
#foreach( $item in $menu.items )
#menuItem( $item $listCounter )
#set ( $listCounter = $listCounter + 1 )
#end
</ul>
#set ( $counter = $counter + 1 )
#end
#end

This new mainMenu macro is compatible with the new menuItem macro above, and also provides support for a Javascript-enabled top-level menu. Clicking on a top-level menu item with children will expand the menu and allow users to see the entire tree without waiting for a page to load.

The change to the menuItem macro introduced an expand() Javascript function. This method needs to be added to the main XHTML template at the bottom of this template file. Find the section that looks similar to the following:

<head>
    ...
    <meta http-equiv="Content-Type"
          content="text/html; charset=${outputEncoding}" />
    ...
</head>

and replace it with this:

<head>
    ...
    <meta http-equiv="Content-Type"
          content="text/html; charset=${outputEncoding}" />
    <script type="text/javascript">
        function expand( item ) {
        var expandIt = document.getElementById( item );
        if( expandIt.style.display == "block" ) {
        expandIt.style.display = "none";
        expandIt.parentNode.className = "collapsed";
        } else {
        expandIt.style.display = "block";
        expandIt.parentNode.className = "expanded";
        }
        }
    </script>
    #if ( $decoration.body.head )
    #foreach( $item in $decoration.body.head.getChildren() )
    #if ( $item.name == "script" )
    $item.toUnescapedString()
    #else
    $item.toString()
    #end
    #end
    #end
</head>

After modifying the default site template, you’ll need to configure your project’s POM to reference this new site template. To customize the site template, you’ll need to use the templateDirectory and template configuration properties of the Maven Site plugin.

Customizing the Page Template in a Project’s POM. 

<project>
    ...
    <build>
        <plugins>
            <plugin>
                <artifactId>maven-site-plugin</artifactId>
                <configuration>
                    <templateDirectory>src/site</templateDirectory>
                    <template>site.vm</template>
                </configuration>
            </plugin>
        </plugins>
    </build>
    ...
</project>

Now, you should be able to regenerate your project website. When you do so you may notice that the resources and CSS for the maven site are missing. When a Maven project customizes the site template, the Site plugin expects the project to supply all of the default images and CSS. To seed your project’s resources, you may want to copy the resources from the default Doxia site renderer project to your own project’s resources directory by executing the following commands:

$ svn co \
      http://svn.apache.org/repos/asf/maven/doxia/doxia-sitetools/\
      trunk/doxia-site-renderer
$ rm \
      doxia-site-renderer/src/main/resources/org/apache/maven/\
      doxia/siterenderer/resources/css/maven-theme.cs+
$ cp -rf \
      doxia-site-renderer/src/main/resources/org/apache/maven/\
      doxia/siterenderer/resources/* \
      sample-project/src/site/resources

Check out the doxia-site-renderer project, remove the default maven-theme.css file and then copy all the resources to your project’s src/site/resources directory.

When you regenerate the site, you’ll notice that a few menu items look like regular unstyled text. This is caused by a quirky interaction between the site’s CSS and our new custom page template. It can be fixed by modifying our site.css to restore the proper link color for these menus. Simply add this:

li.collapsed, li.expanded, a:link {
    color:#36a;
}

After regenerating the site, the menu’s link color should be corrected. If you applied the new site template to the same sample-project from this chapter, you’ll notice that the menu now consists of a tree. Clicking on "Developer Resources" no longer takes you to the "Developer Resources" page; in stead, it expands the sub-menu. Since you’ve turned the Developer Resources menu-item into a dynamically-folding sub-menu, you have lost the ability to reach the developer/index.apt page. To address this change, you should add an Overview link to the sub-menu which references the same page:

Adding a Menu Item to a Site Descriptor. 

<project name="Hello World">
    ...
    <menu name="Main Menu">
        ...
        <item name="Developer Resources" collapse="true">
            <item name="Overview" href="/developer/index.html"/>
            <item name="System Architecture" href="/developer/architecture.html"/>
            <item name="Embedder's Guide" href="/developer/embedding.html"/>
        </item>
    </menu>
    ...
</project>

10.7.3. Reusable Website Skins

If your organization is creating many Maven project sites, you will likely want to reuse site template and CSS customizations throughout an organization. If you want thirty projects to share the same CSS and site template, you can use Maven’s support for skinning. Maven Site skins allow you to package up resources and templates which can be reused by other projects in lieu of duplicating your site template for each project which needs to be customized.

While you can define your own skin, you may want to consider using one of Maven’s alternate skins. You can choose from several skins. These each provide their own layout for navigation, content, logos, and templates:

  • Maven Classic Skin - org.apache.maven.skins:maven-classic-skin:1.0
  • Maven Default Skin - org.apache.maven.skins:maven-default-skin:1.0
  • Maven Stylus Skin - org.apache.maven.skins:maven-stylus-skin:1.0.1

You can find an up-to-date and comprehensive listing in the Maven repository: http://repo1.maven.org/maven2/org/apache/maven/skins/.

Creating a custom skin is a simple matter of wrapping your customized maven-theme.css in a Maven project, so that it can be referenced by groupId, artifactId, and version. It can also include resources such as images, and a replacement website template (written in Velocity) that can generate a completely different XHTML page structure. In most cases, custom CSS can manage the changes you desire. To demonstrate, let’s create a designer skin for the sample-project project, starting with a custom maven-theme.css.

Before we can start writing our custom CSS, we need to create a separate Maven project to allow the sample-project site descriptor to reference it. First, use Maven’s archetype plugin to create a basic project. Issue the following command from the directory above the sample-project project’s root directory:

$ mvn archetype:create -DartifactId=sample-site-skin
      -DgroupId=org.sonatype.mavenbook

This will create a project (and a directory) called sample-site-skin. Change directories to the new sample-site-skin directory, remove all of the source code and tests, and create a directory to store your skin’s resources:

$ cd sample-site-skin
$ rm -rf src/main/java src/test
$ mkdir src/main/resources

10.7.4. Creating a Custom Theme CSS

Next, write a custom CSS for the custom skin. A custom CSS stylesheet in a Maven site skin should be placed in src/main/resources/css/maven-theme.css. Unlike the site.css file, which goes in the site-specific source directory for a project, the maven-theme.css will be bundled in a JAR artifact in your local Maven repository. In order for the maven-theme.css file to be included in the skin’s JAR file, it must reside in the main project-resources directory, src/main/resources.

As with the default the default site template, you will want to start customizing your new skin’s CSS from a good starting point. Copy the CSS file used by the default Maven skin to your project’s maven-theme.css. To get a copy of this theme file, save the contents of maven-theme.css from the maven-default-skin project to src/main/resources/css/maven-theme.css in our new skin project.

Now that we have the base theme file in place, customize it using the CSS from our old site.css file. Replace the #navcolumn h5 CSS block with the following:

#navcolumn h5 {
font-size: smaller;
border: 1px solid #aaaaaa;
background-color: #bbb;
margin-top: 7px;
margin-bottom: 2px;
padding-top: 2px;
padding-left: 2px;
color: #000;
}

Once you’ve customized the maven-theme.css, build and install the sample-site-skin JAR artifact to your local Maven repository by running:

$ mvn clean install

Once the installation is complete, switch back to the sample-project project directory, if you already customized the site.css earlier in this chapter, move site.css to site.css.bak so it no longer affects the output of the Maven Site plugin:

$ mv src/site/resources/css/site.css src/site/resources/css/site.css.bak

To use the sample-site-skin in the sample-project site, you’ll need to add a reference to the sample-site-skin artifact in the sample-project's site descriptor. A site references a skin in the site descriptor using the skin element:

Configuring a Custom Site Skin in Site Descriptor. 

<project name="Sample Project">
    ...
    <skin>
        <groupId>org.sonatype.mavenbook</groupId>
        <artifactId>sample-site-skin</artifactId>
    </skin>
    ...
</project>

You can think of a Maven Site skin as a site dependency. Site skins are referenced as artifacts with a groupId and an artifactId. Using a site skin allows you to consolidate site customizations to a single project, and makes reusing custom CSS and site templates as easy as reusing build logic through a custom Maven plugin.