Vaadin 7 Theming

Introduction

Wow, been staying up late cranking out some Vaadin blogs.  It's starting to catch up with me, but I have one more to add to the pile - Theming.

Vaadin Themes in Liferay

Vaadin in the portal is challenging because of theming.  In a Liferay portal window, well the surrounding frame is all managed and styled by the Liferay theme.  This poses a special problem for Vaadin because Vaadin portlets, like Vaadin servlets, want to be styled exclusively by a separate Vaadin theme.

In a Vaadin servlet this isn't a problem because there is no surrounding styling to worry about, but in the portal the Vaadin theme is styling a portlet within the context of the portal's theme, and these two are typically not aware of the impact they can have on each other.

I'll wager that any Vaadin theme you try, from the standard Vaadin themes to those available from http://www.vaadin.com, every one of these will end up rendering differently within the portal than from a servlet.

Some of the rendering differences will be minor, some will not.

Usually it comes down to this - Vaadin themes always assume they are starting from a blank slate, a totally unstyled starting point that the Vaadin styling can build off of.

In Liferay, however, this is never the case.  When Vaadin gets to start styling tags there's a whole mountain of CSS styling already in place, styling that the Vaadin theme doesn't think it has to undo.  For example, a Vaadin theme can style a tag by giving a "margin-left: 20px" style because it will assume that the tag has no margin styling from Vaadin; the problems arise when the Liferay theme applies generic margins to an element that Vaadin isn't undoing.  The correct Vaadin styling that should apply is a left margin of 20px but a top, right, and bottom margin of 0px if it doesn't want or need them.

So as you move forward creating Vaadin themes, keep in mind that you are probably not just styling Vaadin elements, you are also undoing or mixing in the styling from your Liferay theme.

Keep in mind a few key points to use in Vaadin theming:

  • You can specifically undo a style.  For example, setting a margin to 0px when you don't want a margin rather than assuming there just isn't one.
  • You can use !important to force a rule, but as always the more you use it the harder it is to work around it.
  • Higher specificity always rules.  So if you can match on a path or on multiple classes, writing a rule that results in higher specificity will give your rules precedence over inherited styling from the Liferay theme.

When to Theme in Vaadin

You create themes in Vaadin for the same reasons you create themes in Liferay - to package a set of styling rules that can be reused and applied to multiple portlets.

But you will also finding yourself creating themes because it's the only effective way to style your Vaadin portlets.

Many aspects of your Liferay theme that you hope would apply to your Vaadin elements simply won't because the Vaadin theme is in play.  The liferay-portlet.xml file way of defining CSS to include in the portlet, well even that starts breaking down within the Vaadin portlet where the Vaadin theme is getting in the way.

By creating and maintaining a Vaadin theme for your Vaadin portlets, you get a theme which will style the Vaadin widgets just the way you need every time.

How to Theme Vaadin Portlets

So you theme your Vaadin portlets in two ways.  The first is to add style classes to your widgets in the Java code and the second is to write CSS (usually from SCSS) that define the style rules for the classes.

Every Vaadin widget, from the UI class all the way down to a caption on a label, every widget can be assigned style classes.  You decorate your Vaadin widgets with classes when you need to style them differently from the norm.

For example, we're going to be taking the vaadin-sample-portlet project and modifying the look and feel of the user detail page.  We have these requirements to:

  • Change the caption for required elements red.
  • Increase the font size for the displayed values.
  • Indent the displayed values from their captions.
  • Adjust some margins so the elements are not all cramped together.

As a reminder, here's how the user details panel looks before styling is applied:

Existing User Detail Panel

Not very pretty, I think we'd all agree on that.

Adding Style Names to Vaadin Widgets

Now based upon our requirements we know that we are going to be indenting all of the values from the captions and we're also going to change the "required" caption to red.  We'll assume that the Name is our only required field (hey, this is just an example, I know email and screen name are also required, but how fun is that to make them all the same?).

So we'll use two classes, "indented" and "required-cap".  We'll be using two methods on the widgets, the setStyleName() and addStyleName() method.  The set method does that, it sets a single value, the add method adds additional style classes beyond the first.

Below is the code we'll use to style the widgets:

// add some classes to the widgets, we'll sort them out in the theme name.setStyleName("indented"); userId.setStyleName("indented"); emailAddress.setStyleName("indented"); screenName.setStyleName("indented"); userPortrait.setStyleName("indented"); 
name.addStyleName("required-cap"); 

Just a quick word on style names.  Vaadin actually does some weird things with the class names that get applied, you will actually end up with one or two classes for each style name.  For example, the userId widget is a Label instance.  The HTML that results for this widget is:

Generated HTML

The userId element is the outer div for the "v-widget" and two inner divs.  The first inner div is the caption, the "User ID:" string.  The second inner div is the label, the "11005" value.

Note the classes on the second inner div, the "v-label" div.  It has both the "indented" and "v-label-indented" classes.  The first inner div, the "v-caption" div, that only has a manufactured class of "v-caption-indented".

So your simple assignment of "indented" as a style name results in classes that match the name but also classes prefixed with the widget type.

Although this may seem odd at first, it is actually kind of useful.  You can style using the generic "indented" class that would apply to all widgets or you can provide specific and different styling for indented captions vs indented labels.  The user portrait is an instance of an Embedded object; it's unique class is "v-embedded-indented".  When we create our theme we'll be taking advantage of these differences.

Creating a Vaadin Theme

So creating a Vaadin theme is pretty easy when using the Vaadin 7 Control Panel.

The Vaadin Theme panel lists the current themes available in your Vaadin shared environment:

Current Themes

In the window above, all of the listed themes are the standard themes.  These are handled differently than custom themes in that they cannot be edited, compiled or deleted.  Your custom themes can be edited, compiled or deleted.

Click the "Create Theme" button to create a theme.  We'll name our new theme "sample".  Note that theme names become part of the URL, so some characters will be invalid.

Creating a Theme

With our new theme created, it will be in the theme list.  We'll be selecting it so we can edit the theme.

When editing a theme, you begin from the Theme Structure panel with a tree view on the left showing all files included in the theme and a preview area on the right.  The preview area is read-only.

Viewing Theme Files

On the left is a tree view of the files that are in the theme.  All new themes will start with two files, the styles.scss file is the main one used to compile the theme and the second file is an SCSS file named from the theme.

You can actually right-click on the folder to create new subfolders, upload assets (such as images) or delete files.  There is no limit to what you can build into your theme.

The right side is a preview panel to view the current contents of the file (it will also preview image files).  The preview control is based on the Ace Editor Vaadin Add On and it is a syntax-aware code viewing/editing control.  

At the bottom of the panel is an Edit button, click the button to open the edit dialog.

By default, every custom theme inherits from the Liferay Vaadin theme.  Remember the Liferay Vaadin theme is not your Liferay theme, it's actually an adaptation of the Liferay Classic theme as a Vaadin theme.  You can change your theme inheritance to any other installed theme.  The Vaadin 7 Control Panel uses the Liferay theme as the default base theme because this has the least amount of conflict with the Liferay themes (well, the Liferay Classic theme, your Liferay theme can have it's own incompatibilities).

So, given our requirements we will add our own style definitions to handle our recently added classes:

Note how we've handled the v-label-indented and v-embedded-indented differently.  Also note the overlap in the margin-left and margin-top values.  We could have used a rule for the "indented" class that had the two margin settings and a rule for "v-label-indented" to have the margin-bottom and font-size changes.

Anyway, we would continue editing this file, adding other files, adding assets that we needed such as background images or sprites, whatever was needed to flush out the theme.  When finished, we click on the Back link from the top right of the Theme Structure panel to return to the theme list page.

On the theme list page, we have to choose the "Compile Theme" button to compile our theme (compiling the theme converts the SCSS into standard CSS for serving to the browser).

When we return to the theme list, we will see our theme with a corresponding compile date.

Like Vaadin Add Ons, when you are working in a cluster the theme needs to be copied to each node and compiled separately.  Fortunately the "Export Theme" and "Import Theme" buttons can help with this.

The Export Theme function bundles the theme file into a zip and uses the browser to download the theme to your desktop.  The Import Theme function lets you browse for a theme zip file on your desktop to upload to the server.

After importing the theme, you should compile the theme so it is ready on the node.  Repeat this step for all nodes in the cluster.

The Vaadin 7 Control Panel also supports automated theme deployment similar to the automated Add On deployment discussed in a previous blog entry.  A future blog entry will cover the automated theme deployment functionality of the Vaadin 7 Control Panel.

So our new sample theme is done and ready for use in our portlets, but our vaadin-sample-portlet project needs one more change to use the theme.

Assigning the Vaadin Theme for a Vaadin Portlet

We have one minor change we must make so the vaadin-sample-portlet uses the new sample theme.

In the com.dnebinger.liferay.vaadin.sample.UsersUI class we had an annotation indicating we wanted to use the liferay theme:

// Use the standard Liferay theme for the widgets. @Theme("liferay") public class UsersUI extends UI implements Page.BrowserWindowResizeListener { 

We need to change the annotation for our sample theme:

// Use our own theme @Theme("sample") public class UsersUI extends UI implements Page.BrowserWindowResizeListener { 

Through this annotation our portlet will always use the sample theme.  If the sample theme is not available, Vaadin will fall back to a standard theme (the Liferay theme).

Beginning with Vaadin 7.3.0, you can call the UI's setTheme() method with a theme name, allowing for a runtime theme change.

Page Resizing

If you were closely following the previous blog posting, the addition of the "implements Page.BrowserWindowResizeListener" would stand out as a new addition.

Although this isn't necessary Vaadin Theme related, it does affect the view presentation, so I'm including it here as a bonus.

If you remember, there was code in the UsersUI init() method that set the height of the UI to 600px.  This is an issue for Vaadin that the UI has to have a defined height or it will not show.  However, 600px is a large and arbitrary value, surely there is a way we could size the portlet better.

And there is.  The com.vaadin.ui.Page class has access to the current browser details.  To demonstrate, I decided I would make my minimum portlet size 200px but, if there was more vertical space available, I'd expand to use it.  Not a very complex implementation, but it shows what is possible.  I ended up with the following:

int height = 200; 
Page pg = Page.getCurrent(); 
if (pg != null) {  int browserHeight = pg.getBrowserWindowHeight(); 
 // so normally you may know that there is stuff on your page that should normally be about 800 px high.  // we would want a minimum of 200 px in height, but if the browser height is 1500 why not grow to take  // advantage of it? 
 height = browserHeight - 800; 
 if (height < 200) height = 200; } 
setHeight(height + "px"); 

This code in the init() method allows for choosing a dynamic starting size, but what if we want to change the height based upon the browser window size changing?  We can deal with that by registering as a browser resize listener.

First our class implements the Page.BrowserWindowResizeListener interface, then we can register as a listener in the UsersUI init() method:

if (pg != null) pg.addBrowserWindowResizeListener(this); 

And our implementation of the interface uses similar logic to handle the resize:

/**  * browserWindowResized: Event handler for the browser window resize.  * @param browserWindowResizeEvent  */ @Override public void browserWindowResized(Page.BrowserWindowResizeEvent browserWindowResizeEvent) {  int bheight = browserWindowResizeEvent.getHeight(); 
 int height = bheight - 800; 
 if (height < 200) height = 200; 
 setHeight(height + "px"); } 

Like I said, this implementation is not the best.  If your height of stuff above the portlet is 800 px on a wide screen but grows to 1200 px when shrunk so the upper content wraps, well then the math is all wrong.  Also there's no thought given to the footer height in the calculation.

But the point was not to show a perfect implementation, just to show some capability.

Conclusion

So our theme is compiled and ready to go.  The code changes are complete so we build and deploy the updated portlet.  When we see our results, they look a lot better than the starting image:

Themed User Details

Compared to the starting image, it is easy to see that applying some Vaadin theming will make your Vaadin portlets just look better.  No, this is still probably not a view that will excite anyone, but after seeing how easy it is to edit and compile a theme using the Vaadin 7 Control Panel, it should be clear that it could be styled to get it up to anyone's expectations.

An important feature of the shared Vaadin environment and theme editing using the Vaadin 7 Control Panel: theme changes take effect (almost) immediately, no deployments necessary.  After you compile the theme, any time the Vaadin portlet is refreshed or a new user starts an instance, they'll get the updated theme.  This can be a big advantage if deployments within your organization are significant events that must be scheduled well in advance.

Happy Theming!