In most Liferay projects, a custom Liferay theme needs to be developed and this article explains to developers how to do this.
Note that the Liferay documentation already has some nice pages on creating Liferay themes, but we are taking a more hands on approach.

Setting up the theme project

We are assuming you will be using Eclipse, but will always tell what is going on under the hood, so other IDE(or commandline!) guys should also be able to follow.

You should start out by downloading the Liferay Plugins SDK from the bottom of the Liferay additional files page.
Eclipse users should also install the Liferay IDE Eclipse Plugin and add a Liferay Server(and runtime) after. More info on how to do this can be found in the Liferay documentation on installing the Liferay IDE or at this very own blog, in the last part of the getting started with portals and Spring Portlet MVC post.

If you were already working with Liferay, you probably had these installed already anyway and the only thing you need to do then, is start your Liferay server.

Then, within Eclipse, choose New>Project>Liferay>Liferay Project and then check Theme in the box.

Creating a Liferay Theme project in Eclipse

What the theme plugin does is run a script from the <liferay-sdk-home>/themes folder that generates a Liferay theme project for you, in your Eclipse workspace.
You can run the script manually by entering

./create.sh integrating-stuff "Integrating Stuff"

or

create.bat integrating-stuff "Integrating Stuff"

in a terminal from the said <sdkhome>/themes folder.

The Liferay IDE also takes a look at your Liferay runtime and copies a bunch of files to the docroot folder of your project.

Note: That is, if you have the “Build automatically” feature on in Eclipse. The copying of these files is done by the Ant build script included in the project and this script is ran automatically when the project gets built.

These files represent the _styled theme, a very basic Liferay theme, which can be found in the portal source in the portal-web/docroot/html/themes folder or in the docroot/html/themes folder of your Liferay install.
As the Liferay documentation indicates, almost all themes will use the “_styled” theme as a base. If you want to start from scratch, the parent of your theme would be the “_unstyled” theme. Inheriting from “_unstyled” would give you quite a bit more work but would give you full control of the theme.

Note: _styled and _unstyled are very basic, abstract parent themes. They are not production ready. The default Liferay theme – “Classic” – inherits from _styled and is the official example of a production ready theme.

Any of the files that were copied to your docroot folder can be overridden by your theme by placing them in your _diffs folder. In other words, the _diffs folder will contain all files that differ from your parent theme.

Note that, as already indicated above, the “_styled” theme is not production ready. Since our _diffs folder is still empty, our theme – which is currently not overriding anything and is an exact representation of the “_styled” theme – would not look that great if we would deploy it to Liferay right now.
Therefore, it is a better idea to copy the content of the _diffs folder of the production ready theme you want to start from into your own theme’s _diffs folder. To start from the default “Classic” Liferay theme, download the portal source(link) and copy the _diffs folder from the Classic theme at portal-web/docroot/html/themes to your own theme’s _diffs folder.
Now your theme is production ready and ready for deployment.

2. Deploying the theme to Liferay

You probably already noticed the ant build script in the root of your project. If you run the default ant goal “deploy” of this script, the theme is published to your Liferay server.
(If you are not doing this from within Eclipse, you need to properly set the values in the build.properties file in the ssdk’s theme folder.)

Let’s deploy and enable our theme on our Liferay server, which for now, looks exactly the same as the default “Classic” theme. If the theme deployed successfully, the Liferay logging should contain something like:

Liferay Theme being deployed - logging

Note: To set the theme on a page on Liferay and test it, go to Manage>Page>Look and Feel, and select our new theme there. Note that the theme description is lacking a thumbnail. If you want to add one, take a screenshot of a Liferay page with your theme applied, name it thumbnail.png, scale it down to 150 pixels wide and 120 pixels high and put in in the _diffs/images folder folder, as described in the Liferay documentation on theme thumbnails.

3. Overriding parts of the parent theme

The content of the_diffs folder we copied from the Classic theme consisted of 3 folders: css, images and js(javascript). In the _styled theme there is one additional folder, templates, of which the classic theme does not override anything. This is recommended practice: themes should try to avoid overriding template files since they are likely to change in between Liferay versions so changing them might make porting the theme to a new Liferay version more difficult.

a. overriding css

Now, to add a nice gradient background image and change the default text size and font, we have to modify our custom.css file.

We will change the

body {
	background: #EEF0F2;
	font-size: 11px;
}

into:

body {
        background-color: #4F555B;
	background-image: url("../images/layout/bg-grad.gif");
	background-repeat: repeat-x;
	color: #FFFFFF;
	font-family: Verdana,Helvetica,sans-serif;
	font-size: 11px;
}

and also create a layout_folder in our images folder and add the file bg-grad.gif to it.

If we redeploy the theme and load our Liferay page, we see that the background has changed and text appears a bit different. Pretty easy, huh.

Liferay background changed with new theme

Note: I also replaced the Liferay logo with the Integrating Stuff logo by navigating to Manage>Settings>Logo and uploading the logo file there.

b. Overriding and adding images

In the previous section, we already added a background image to the theme. If you want to override an image, you just need to add it to the images folder as well, but the image needs to have the same folder structure and file name as the one you want to override.
For example, if you want to override some emoticons, you would create an emoticons folder(you need to look at the _styled theme files to know what you can override) and start putting names there of files that also exist in the parent theme(angry.gif, blank.gif,..).

c. Overriding the custom javascript

This can be done the same way images are overridden. However, the _styled theme only has one javascript file: main.js, which, with Liferay 6, only contains some logic that keeps the Liferay breadcrumbs always on screen.

d. Overriding the template files

As mentioned already, the Liferay documentation advises to avoid overriding the template files. That being said, almost all projects I know off involved creating themes that were modifying these templates.
If one wants to add or remove parts of the page and the modifications go beyond styling dom elements, these templates are usually changed. The view technology these files use is Velocity.

The two most important ones are portal-normal.vm – which renders the whole page – and portlet.vm – which renders one portlet. The other ones respectively define additional velocity variables(init_custom.vm), render the navigation bar(navigation.vm) and render the popup pages(portal_pop_up.vm).

Let’s take a look at the portal-normal.vm Velocity template:

<!DOCTYPE html>

#parse ($init)

<html class="#language("lang.dir")" dir="#language("lang.dir")" lang="$w3c_language_id">

<head>
	<title>$the_title - $company_name</title>

	$theme.include($top_head_include)
</head>

<body class="$css_class">

#if($is_signed_in)
	#dockbar()
#end

<div id="wrapper">
	<a href="#main-content" id="skip-to-content">#language("skip-to-content")</a>

	<header id="banner" role="banner">
		<hgroup id="heading">
			<h1 class="company-title">
				<a class="logo" href="$company_url" title="#language("go-to") $company_name">
					<span>$company_name</span>
				</a>
			</h1>

			<h2 class="community-title">
				<a href="$community_default_url" title="#language("go-to") $community_name">
					<span>$community_name</span>
				</a>
			</h2>

			<h3 class="page-title">
				<span>$the_title</span>
			</h3>
		</hgroup>

		#if(!$is_signed_in)
			<a href="$sign_in_url" id="sign-in" rel="nofollow">$sign_in_text</a>
		#end

		#if ($update_available_url)
			<div class="popup-alert-notice">
				<a class="update-available" href="$update_available_url">#language("updates-are-available-for-liferay")</a>
			</div>
		#end

		#if ($has_navigation)
			#parse ("$full_templates_path/navigation.vm")
		#end
	</header>

	<div id="content">
		<nav class="site-breadcrumbs" id="breadcrumbs">
			<h1>
				<span>#language("breadcrumbs")</span>
			</h1>

			#breadcrumbs()
		</nav>

		#if ($selectable)
			$theme.include($content_include)
		#else
			$portletDisplay.recycle()

			$portletDisplay.setTitle($the_title)

			$theme.wrapPortlet("portlet.vm", $content_include)
		#end
	</div>

	<footer id="footer" role="contentinfo">
		<p class="powered-by">
			#language("powered-by") <a href="http://www.liferay.com" rel="external">Liferay</a>
		</p>
	</footer>
</div>

</body>

$theme.include($bottom_include)

</html>

If one would want (and is allowed to) remove the “Powered by Liferay” line, he can easily do this by just removing the line containing the text.

If the static htmldesign/prototypes are already made by web designers, it is easy for developers to port this created design to Liferay by copying the html to the portal-normal.vm file and inserting the Velocity directives where required.
All these Velocity templates are clearly readable. When taking a look at the above file, it is obvious how to replace the whole Liferay header, for example.

4. More advanced stuff

There is more advanced stuff one can use to fine tune a theme and is not covered by this tutorial – such as adding settings or color schemes to a theme. Although this tutorial gets you started from a practical point of view, it is probably a good idea to scan through the Liferay documentation on themes as well.