Friday 9 January 2015

Wicket + BEM: A powerful combination

In this article, I will explain how to combine Wicket, one of the leading Java frontend frameworks, with BEM, one of the leading CSS coding standards, to create a frontend architecture that makes creating and maintaining your HTML frontend a lot simpler, easier and faster.

Wicket
Java frontend frameworks come in two flavours: Action based and Component based. Action based frameworks focus on manually handling each HTTP request by figuring out which data and what markup to return to the client. The best known example of an Action based framework is Spring MVC, but there are many others.

Component based frameworks take this one step further, by allowing the developers to create components: reusable panels that are fully self contained and represent a single item on a website, for example a title bar or a registration form. These components contain both frontend logic (i.e. calls to the service layer to send and retrieve data) and markup and can then be reused on various pages without the page having to worry about the internals of the components. It's like playing with Lego: instead of having to retrieve all the right data and select all the right markup for each HTTP request, you can simply slap existing components on a page and the components will take care of themselves. Each page is then associated with a URL extension. This way, handling HTTP requests is hidden from the programmer.

When it comes to HTML, some Component based frameworks hide this as well by providing their own XML tags that get transformed into HTML. One example of this is JSF. In this article however, we will talk about my favourite Component based framework: Wicket. This framework does not hide HTML from the programmer. Instead, the programmer writes plain HTML, augmented with some Wicket specific tags and attributes to bind the appropriate HTML elements to the Java code. A Wicket component is known as a panel and consists of a Java file for the frontend logic and a corresponding HTML file for the markup. It looks like this:

TitleBarPanel.java:
public class TitleBarPanel extends Panel {
    public TitleBarPanel(String id) {
        super(id);
        this.add(new Label("titleLabel", "Hello World!"));
    }
    //...other frontend logic and communication with the backend goes here...
}
TitleBarPanel.html (matched to the Java file by fully qualified name):
Note: Due to Blogger's peculiarities, I can't use self-closing tags with a wicket:id attribute in the example code, but in reality, it's perfectly okay (and recommended) to use them.

    
    
        
        
Of course, panels can contain other panels as well in an unlimited container/component pattern:

TitleBarPanel.java:
public class TitleBarPanel extends Panel {
    public TitleBarPanel(String id) {
        super(id);
        this.add(new Label("titleLabel", "Hello World!"));
        this.add(new MenuPanel("menuPanel")); //MenuPanel is another panel that we've created
    }
}
TitleBarPanel.html:

    
        
If you don't have any experience with Wicket, you can read some tutorials on wicket.apache.org. A good article that further explores the differences between Action based and Component based frameworks is this one.

BEM
BEM is a CSS coding standard designed to solve the problem that arises when you create a large web application with lots of pages. If you change the CSS rules that correspond to a CSS class in order to change something on one page, then things all over the application suddenly change as well if that CSS class is used elsewhere. BEM solves this by defining so-called blocks. A block is a piece of the website that is independent from the rest of the website and can be designed and maintained separately, for example a title bar or a registration form. Hmmm, where have we heard that before?

BEM achieves this by enforcing a CSS class naming standard that guarantees that no naming conflicts will happen among the CSS classes of your application. For example:
Hello World!
What do we see there? The block is defined by a single outer div with the name of the block, in this case "b-NewsItems". Then, inside the block, each CSS-class starts with the name of the block! This way, we only have to take care of the uniqueness of the block name. If we make sure that each block name is unique across our application, then CSS naming conflicts will never occur and you can create and maintain each block independently from the rest of the application.

So what's with the "b-" prefix? In BEM, "b-" means that we're dealing with block classes. There are two more prefixes that you can use for other purposes:

  • The "p-" prefix means that it's a so-called pattern class: a class that can be applied to HTML elements regardless of which block they're in. For example, if you have designed a very unique border that needs to be applied to a lot of elements, then you put this into a pattern class "p-MyBorder", to be used throughout your application. In a way, this is the opposite of a block class, since it does not correspond to a specific part of the application and when we change it, we do want things to change everywhere.

  • The "js-" prefix means that it's a JavaScript class: a class that is applied to HTML elements so that you can identify these elements in your JavaScript code. Normally, you would use HTML id's for this, but since most Component based frameworks use the id to keep track of the various elements of the application, it's better to use classes for this.
In BEM, all CSS rules must be in a selector that starts with either a block class or a pattern class, to avoid any conflicts with other HTML tags in your application.

Combining Wicket and BEM
By now, you must have an idea of where we're heading. Both Wicket and BEM focus on components: pieces of a web application that are fully self contained and are created and maintained independently from the rest of the application. Wicket calls them panels and BEM calls them blocks. Sounds like they're an ideal match for one another! What if, in addition to the logic and the markup, we put the style of a component in a Wicket panel too, with the help of BEM? Let's see what that would look like!

TitleBarPanel.java:
public class TitleBarPanel extends Panel {
    public TitleBarPanel(String id) {
        super(id);
        this.add(new Label("titleLabel", "Hello World!"));
        this.add(new MenuPanel("menuPanel")); //MenuPanel is another panel that we've created
    }
}
TitleBarPanel.html:

    
    
        
    

    
        
        
Now, the TitleBarPanel is fully self contained: Logic, markup and style all in one place for a highly cohesive panel that is completely decoupled from the rest of our application and can be maintained independently of anything else.

But what if we want to change a tiny little thing in the MenuPanel? What if the MenuPanel has a <span> with the CSS class "b-MenuPanel-menuHeader" that has red text, but we want to make it blue in the TitleBarPanel? Well, we can simply override the color attribute of that span by using our own block name to create a higher CSS specificity than the default setting in MenuPanel.

MenuPanel.html:

    
        
    

    
        
    
TitleBarPanel.html:

    
        
    
    ...
You can do this cumulatively as many times as you like. Suppose the TitleBarPanel is used in six different panels and in one of those panels, say the FrontPanel, we want the menu header to be red again? Simple, put this in the <style> tag of the FrontPanel:
.b-FrontPanel-titleBarPanel .b-TitleBarPanel-menuPanel .b-MenuPanel-menuHeader {
    color: red;
}
Now, all the MenuPanels will have a red header, except when used in a TitleBarPanel, then it will be blue. Except when that TitleBarPanel is used in a FrontPanel, then it will be red again. This will never break, no matter how deep you go, since it will only ever affect panels that are contained inside the panel where you put this CSS selector.

From an architectural point of view, this is a very clean solution: The container has control over its children and can override certain CSS settings, but the children are completely unaware of this. Each panel focuses on itself and its children, but does not know where and how it is used in other panels and pages. In technical terms: Each HTML file only has CSS selectors that begin with its own top level BEM class and most certainly not others!

Benefits
Aside from the fact that this architecture makes developing and maintaining your application a lot simpler and easier, it has other benefits too:

  • All CSS that a page needs is present in the <style> tag of the HTML document. This reduces the amount of HTTP requests that would otherwise be necessary to retrieve the .css files. An exception to this are the pattern classes, of course. They are still in separate .css files, since they are not specific to a page.

  • Only the CSS that is needed to display the page will be retrieved. No additional CSS that is used for other pages will be retrieved. This reduces the size of the CSS that needs to be downloaded.

  • Since the CSS is sent within the HTML file, it won't be cached by the browser. This might sound like a drawback, but can also be a benefit: when you deploy a new version of your application with changed CSS, your users won't see any glitches because the browser still uses an old CSS file and won't have to press F5 to view the page correctly. Pressing F5 might seem trivial to you and me, but a lot of non technical users might not know this.

  • CSS is easy to find. It's right there in front of you when you write your panel, not in some file in some directory elsewhere.

  • It brings CSS closer to web developers. In a lot of companies, CSS is written and maintained by different people than the Java developers, which often leads to a situation where the Java developers don't really know a lot about it. Also, they are often less interested in writing proper CSS and maintain an "If it looks OK, then it is OK" attitude. This will lead to an increasingly complex and buggy CSS codebase. With the approach in this article, the CSS is right in front of the programmer when he's creating Wicket pages. It will increase the sense of ownership and will make writing CSS more fun, since you're only working on your little bit, without having to worry about the rest of the application.

  • CSS gets deleted when a panel gets deleted. This is a major advantage. Old unused CSS often lingers around and since unused code detectors work really well for Java, but not well at all for CSS, an automatic solution to this problem is a nice thing to have.
Drawbacks
As with every architectural approach, this one has its drawbacks too:

  • It does not support SCSS. SCSS is a CSS meta language that allows you to write CSS code in a more advanced way. An SCSS compiler then compiles your SCSS files to CSS files. Since in this architecture, the CSS is inside your HTML files, it cannot be compiled by the SCSS compiler. There are a few silver linings to this though:
    • Adding SCSS compilation to your build process makes it more complex and increases the build time, so not having to do this saves time and effort.
    • It can be difficult to link a CSS rule on your page to an SCSS rule in the original document. This can make it harder to debug errors.
    • The experimental version of Google Chrome supports native SCSS, meaning that you can just send SCSS to the browser directly. Once this becomes mainstream, you can just put SCSS code in your <style> tags. This will make it compatible with this architecture: All the benefits of SCSS, without the aforementioned drawbacks!
  • It is not possible to switch CSS files at runtime. This is sometimes done to let the user choose the look and feel of the page. Since in this architecture, the CSS is embedded in the HTML, this won't be fully possible. It will only be possible with the pattern classes, that are still in separate CSS files. This will still give you plenty of possibilities though, since the pattern classes often contain look-and-feel stuff, whereas the block classes contain more positioning stuff.

  • The HTTP responses will be slightly larger, since the HTML files will be bigger. There will be less HTTP responses though, since there will be less CSS files to retrieve.

  • Internet Explorer 9 and lower are not supported, since they only support a maximum of 31 <style> tags in the head. As mentioned before, Wicket aggregates all <wicket:head> tags of all the panels into the final <head> tag of the page, but the separate <style> tags will remain. so if you have more than 31 panels on a page (which is quite common in Wicket), then any <style> tags after the 31st will be ignored. It is of course possible to write a Filter that puts all the content of all the <style> tags into a single <style> tag, but this will require some effort.
If these drawbacks don't deter you, then I'm sure that the simplicity and the high cohesion of your panels make your frontend development a lot easier. And who knows, it might even transform you from a good Java developer into a CSS master!

No comments:

Post a Comment