In Ext GWT 2, component styles were defined in a single large CSS file. Developers could extend or redefine the styles in this file to control the appearance of a component or define a custom theme. Although this made it easy to give components a new look, it was hard to determine the relationship between the styles in the CSS file and the components in the library. Furthermore, there was no assurance that all of the styles in the CSS file were needed, or that the styles needed for a particular component were actually defined in the CSS file.
Let’s say you wanted to determine whether a particular style was no longer used and could be removed from the CSS file. The easiest approach would be to search the Java source files for string literals containing the style class name. However, there’s a catch. Not all of the class names occur as string literals. Some are generated dynamically (e.g. through string concatenation or template formatting), as the following simplified snippet from the Ext GWT 2.2.5 DataList.java illustrates:
StringBuffer sb = new StringBuffer();
As you can see, the style class name is generated dynamically by taking the value of the style property and concatenating an
-r to it.
These types of things make it difficult to generate style cross references, and without style cross references it is hard to maintain the CSS files for a large project. You can’t tell what will be affected by changing a style, nor can you tell whether a style is unused and can be removed. And even if the style is no longer used in Sencha code, it may still be used in customer code, meaning Sencha can’t remove it without the risk of breaking something. The result is that it is generally safer to add a new style, and CSS files grow over time.
To appreciate the scale of this problem, consider that gxt-all.css, the main stylesheet for Ext GWT 2, contains over 7,000 lines, with over 1,500 style rules that reference more than 800 unique class names, of which over 400 cannot be found as string literals in the java source files. Clearly, a new approach was needed.
The Appearance Pattern
In 2011, Google unveiled the GWT Appearance Pattern. The Appearance Pattern defines the contract between the component and how it looks on the screen (its appearance). This pattern offers many benefits for developers who define or style widgets, enabling them to:
- Replace the style of a widget
- Replace the DOM structure of a widget
- Reskin an entire application
- Isolate CSS code for each widget
- Ensure CSS styles defined are used
- Inject only CSS that is actually used
- Use conditional CSS and hide browser differences
- Encourage a separation of concerns
Much of the power of the Appearance Pattern is derived from its use of the GWT ClientBundle and CssResource interfaces. These interfaces work with the GWT generator feature to make it easy to manage client resources.
ClientBundle is the umbrella and provides a means of bundling content and sending it efficiently to the client. This content can include images, text files, generated URLs and CSS resources.
CssResource provides the CSS resource support for ClientBundle. It includes a powerful CSS preprocessing capability with image spriting, conditional processing, named constants and function calls. The conditional processing can utilize both deferred binding properties, such as the browser type, as well as values that are returned by function calls that are evaluated on the client at the point the styles are injected.
CssResource defines a one-to-one mapping between CSS class names and Java method names. Style classes that do not have a corresponding method trigger a compilation error. This makes it easy to detect missing style rules or styles that are not used, and is consistent with the Java approach in general: strongly typed methods and compile-time errors instead of runtime errors. When used with a modern IDE, you also get support for cross references as well as code completion (which eliminates typos that can occur easily in string literal class names).
So, to net it out, the Appearance Pattern brings Java-style strong typing to HTML and CSS. Although the Appearance Pattern requires some additional initial effort to use, it pays off in the long run due to reduced total development and maintenance costs.
See GXT 3 Appearance Design for more information on how GXT 3 components use the Appearance Pattern. Note that the Appearance Pattern addresses both the structure and the style of the component. In this blog we focus on the style.
Migration: a complete example
Now that we’ve seen some of the issues with the Ext GWT 2 approach to styles as well as some of the benefits of the GXT 3 use of the Appearance Pattern, let’s take a look at how to migrate styles and themes from Ext GWT 2 to GXT 3. In the following sections, we provide an example, starting by creating Ext GWT 2 custom styles for a simple application and then migrating to the GXT 3 Appearance Pattern.
Styling a Ext GWT 2 application
Let’s start by creating a simple set of Ext GWT 2 custom styles. We will use the trial and error approach. It consists of using a browser based web development tool like Firebug to inspect and modify the styles of a running application, recording the changes that produced a desired result until no remnants of the previous style are visible. This approach is fraught with peril: you only style the parts of each component that you know to visit, you may not appreciate how each class contributes its own styles to the hierarchy, and you are exploiting an internal interface. However, it can be done simply by trial and error, without really knowing the details, and it doesn’t require recompiling the application, so this approach was popular and there are many successful applications that were developed this way.
Here’s an example of how it works:
Open the application in the browser
Here we’ve written a simple application that displays a single
ContentPanel in the default style provided by the blue theme:
Start Firebug and display the HTML and Styles
Here we see Firebug with the HTML and Styles views open. You can use any web application development tool for any browser. We’ve used the inspect element feature to view the HTML and styles for the
Modify a style and check the result
Use the Style view to modify the CSS for the selected element to see what effect each style contributes to the user interface. Here we’ve found that by changing the
x-panel-header background-image to a new image we can get a red gradient instead of the default blue gradient:
Define style rules
Repeat this process until you track down all of the related bits of blue style. Then create a stylesheet that selectively overrides the default style rules with a new rule for each change you made:
Add a link to the new stylesheet to the project HTML
To permanently associate the styles with the component, add a link to the new stylesheet to the project HTML:
style="position: absolute; width: 0; height: 0; border: 0">
Try out the new style
Refresh your browser page and the
ContentPanel with the new style appears:
Styling a GXT 3 application
Now that we have a
ContentPanel that is styled with a set of Ext GWT 2 styles, let’s see what we need to do to utilize the Appearance Pattern for GXT 3.
Specifying an appearance
Recall that with the Appearance Pattern, the appearance of a component is separate from the component itself. How does an application tell a component which appearance to use? There are two possibilities: via configuration (in the GWT module XML file) or via constructor arguments. Components generally have multiple constructors, one set that creates the component with a default appearance (possibly modified by a configuration in a module file) and one set that allows the caller to pass a custom appearance.
Finding existing appearances (package organization)
So to create a new GXT 3 appearance for a custom style we need to implement an appearance and either configure it in a module file or pass it to the constructor. But how do we implement an appearance, and which one? Let’s see how the library does it. Since we’re styling a ContentPanel we need to look for
ContentPanel appearances. A quick search turns up several hits.
We see that there is a
ContentPanelBaseAppearance as well as two derived classes,
GrayContentPanelAppearance. This approach is common throughout the GXT 3 library: appearance aspects that are common to all themes are factored out into a base class, while theme-specific derived classes provide just the deltas necessary to complete the appearance. We also see that the header is styled separately from the remainder of the panel.
Deriving a new appearance
So now the question is, where does the new appearance fit into the class hierarchy? If it was an entirely new appearance, with both HTML structural and CSS style changes, we could create an alternative to
ContentPanelBaseAppearance. But really, we just have a new red appearance that is very similar to the blue appearance. Therefore, we can use the blue appearance as a model and create a separate set of files for the red appearance:
Then its a matter of updating each file to use the new red styles. Let’s take a moment to step through the changes needed for each file:
RedHeaderAppearance.java contain the wiring that makes the Appearance Pattern work. Generally, the base classes do most of the real work and the derived classes, which we’re modifying here, provide only the necessary customization for a particular theme. In our example, it is simply a matter of changing names containing the word blue to red. This will enable the interfaces to pick up the red versions of the .css files. If you’d like to learn more, we encourage you to read GXT 3 Appearance Design.
headerBackground.png contains the red gradient we developed for the Ext GWT 2 style.
RedContentPanel.css contains the red style rules for the panel and body.
RedHeader.css file contains the red style rules for the header.
Using the new appearance
As we saw in “Specifying an appearance”, there are two alternatives for using our new appearance: if we want to use it for all the panels in our application, we can do so by modifying the GWT module XML file for the project. On the other hand, if we want to use it for only a selected set of panels, we need to pass it to the
ContentPanel constructor when we create the
Specifying the default appearance in the GWT module XML file
First, let’s look at modifying the module file. GXT 3 components provide constructors that create a default appearance by invoking the
GWT.create method. This approach uses the replace-with directives in the module definition file to determine which class to use, in a process that is referred to as deferred binding. To tell
GWT.create to create an instance of the new appearance, we add the following to the module file (after any other inherited themes):
This effectively sets the application wide default appearance for every content panel.
If you have lots of appearance classes, it is a good practice to create a new module file that contains all of the replace-with directives for the new appearances and then add an inherits directive for this new file to module file of each application that will use it.
Specifying the appearance of a particular component using a constructor argument
Now let’s look at the second alternative for using the new appearance. This involves passing an instance of the appearance to the
ContentPanel redContentPanel = new ContentPanel(new RedContentPanelAppearance());
This is done wherever you need to use the new appearance. Alternatively, you can provide a factory that vends an instance of a content panel, optionally depending on a parameter that defines the desired appearance, or you can create a constructor that takes an enum and uses it to create and pass an instance of the desired appearance to the superclass constructor.
Theses approaches let you dynamically create instances of components, each with its own appearance:
Tips and tricks
Turn off style class name minification during development
One of the features provided by CssResource is to create unique, minified class names for each of the classes it finds in the CssResource derived interface. This approach produces reliable and efficient styles in production mode, but can be difficult to work with during development. To instruct CssResource to create detailed class names for development mode, put the following in the application’s module file:
Let GWT generate the CssResource interface
Keeping the CssResource interface in sync with the CSS file can be time consuming. As an alternative, use the GWT GenerateInterface utility to generate a CssResource interface from an existing CSS file:
java -cp gwt-dev.jar:gwt-user.jar com.google.gwt.resources.css.InterfaceGenerator
-standalone -typeName some.package.MyCssResource -css input.css
In software engineering there are frequently many ways to do something. Choosing the right approach involves weighing the trade-offs and maximizing those that are most important.
In Ext GWT 2, styling was hit or miss, and when you got a hit, it seemed relatively easy. However, it was hard to maintain.
The GXT 3 approach takes advantage of the Appearance Pattern, with ClientBundle and CssResource. It supports the common Java idioms of strong typing and detecting errors at compile-time rather than runtime. However, it requires more moving parts.
You may find that you prefer one approach over the other, but it is important to keep in mind that there is nothing you can do in Ext GWT 2 that you can’t do in GXT 3, and plenty you can do in GXT 3 that you can’t do in Ext GWT 2.
In retrospect, the days of huge monolithic CSS files are over, and large projects are going with something like the Appearance Pattern, or CSS preprocessors, such as Sass and Compass.
One last piece of guidance: if you are in the process of migrating styles and themes from Ext GWT 2 to GXT 3 and run into a problem, or have a question, please do not hesitate to use the GXT forums.