StudyBlue is an academic social networking application, built with GWT, that helps students study smarter, faster. StudyBlue has done a great job with their application styling and design, so much so that they are shooting for a Crunchie Award on Tech Crunch for Best Design 2008.
We're fortunate to have StudyBlue President Chris Klundt and Eric Wuebben, Creative Director for StudyBlue, share their experience styling their potentially award-winning application using GWT and well-planned CSS techniques. If you've ever wondered how you can turn your GWT project into the next most styling application on the block, read on below.
We've been approached by many programmers and designers alike that question how we got such a custom look using GWT. Our response is simple, "Use GWT to build the house, not to paint it." When you build a new house, you don't have your building contractor pick out the furniture, choose the color palette, or stencil the walls. Similarly, you shouldn't use GWT to inject all the "style" into your website. Instead, you rough it out with GWT and push the design to CSS.
Whether you're building a small widget to embed in an existing site, or you're building an entire web-application from scratch (that's us :-) ), we recommend the following three tenants to ensure maximum design efficiency and flexibility:
Sticking with our home construction analogy, you might go about building a home by first having an architect envision a space, then having his apprentice detail the blueprints, before finally having a contractor build the house. Similarly, you should assemble a good design team before diving into the construction of your site with GWT. Chances are, if you are a strong Java programmer (i.e. "the GWT Programmer"), you are not as strong at CSS design and image slicing. You need to know what team members you have at your disposal, and what their strengths are. Once you're set with a Designer and a CSS Stylist (they may be the same person), we recommend you follow this general workflow:
Let's elaborate a bit on points 2, 3 and 4.
Part of knowing your team means understanding what your Web Designer's preferences are when it comes to using Tables or DIVs. GWT offers widgets that encompass both Tables (HorizontalPanel, FlexTable, etc.) and DIVs (FlowPanel, HTML, etc.). There are benefits and drawbacks to using each of them. For example, Tables might be good for vertical/horizontal centering or adding stability to a grid. They are poor for dynamic positioning, hover states, "rounded corners", etc. Whatever the case, both the GWT Programmer and the Web Designer should have an agreed understanding on which layouts (Tables or DIVs) are to be used and when it's appropriate to make exceptions. We prefer to do use DIVs, using FlowPanel much more often than HorizontalPanel or VerticalPanel.
Creating a "blueprint" is important for three reasons. First, it helps eliminate guess work from the GWT Programmer. An HTML "blueprint" can quickly be tested on all browsers to prove that the style renders to the structure as expected. Secondly, "blueprints" are an easy way to communicate between GWT Programmer and Web Designer without both parties having to be in the same room at the same time. Plus it makes it easy to assign blame when things don't look right :-). Finally, creating the "blueprint" will help you realize what looks in the original artwork are impossible to recreate on the site (hopefully none).
Note: Using the WebDeveloper plugin (or Firebug's Inspect) for Mozilla Firefox is a great way to inspect your final layout as a GWT Programmer, to make sure you match the "blueprint". More on this later.
Finally, when you start creating the layout in GWT, remember to break down each piece into a Composite class. Not every Composite you create will be reusable. Sometimes you have to make your widgets so specific that they can't be reused. That's OK, it's a common occurrence in UI programming. However, many widgets you can design to be reusable. One obvious example is "buttons" on your website.
We can't stress this point enough: PUSH YOUR STYLING TO A CASCADING STYLE SHEET (CSS). Just about everything you can do style wise can be done with CSS, so keep it that way.
HTML randomText = new HTML("This is some random text in a DIV"); DOM.setElementProperty(randomText.getElement(), "color", "green"); DOM.setElementProperty(randomText.getElement(), "fontSize", "16px");
HTML randomText = new HTML("This is some random text in a DIV"); randomText.setStyleName("sb-TextWidgets-RandomText");
.sb-TextWidgets-RandomText{ color: green; font-size: 16px; }
So let's look at a more complex example, a navigation bar with "hover" and "selected" states. Below you'll notice we've created reusable code for our "hover" and "selected" state that can be used by any widget that we want to implement "hover" or "selected".
Hover Class:
public class Hover { private static MouseListenerAdapter mla; public static MouseListenerAdapter getHoverMla(){ if(mla == null){ mla = new MouseListenerAdapter(){ public void onMouseEnter(Widget hoverableWidget){ hoverableWidget.addStyleDependentName("hover"); } public void onMouseLeave(Widget hoverableWidget){ hoverableWidget.removeStyleDependentName("hover"); } }; } return mla; } . . . }
public class Selected { private List<HTML> itemList; private ClickListener cl; public Selected(){ itemList = new ArrayList<HTML>(); cl = new ClickListener(){ public void onClick(Widget selectedItem) { for(HTML item : itemList){ if(item.equals(selectedItem)){ item.addStyleDependentName("selected"); } else{ item.removeStyleDependentName("selected"); } } } }; } public ClickListener getSelectedCL(){ return cl; } public void addItem(HTML item){ itemList.add(item); item.addClickListener(cl); } public void removeItem(HTML item){ itemList.remove(item); item.removeClickListener(cl); } }
public class NavBar extends Composite{ public static String BLOG_HOME="http://blog.studyblue.com/"; private int ID=1; private Selected selector; public NavBar(){ super(); selector = new Selected(); FlowPanel holder = new FlowPanel(); holder.setStyleName("NavBar-Holder"); holder.add(link("Blog","")); holder.add(link("About Us","about")); holder.add(link("Jobs","about/jobs")); setWidget(holder); } private HTML link(String text, String location){ HTML ret = new HTML(text); ret.setStyleName("nav-item-"+ID); ret.addStyleName("nav-item"); selector.addItem(ret); ret.addMouseListener(Hover.getHoverMla()); ret.addClickListener(new ClickListener(){ public void onClick(Widget sender){ ///Open popup with blog page location } }); ID++; return ret; } }
.nav-item { background-image: url(images/nav.png); background-repeat: no-repeat; background-position: 0 0; display: block; height: 0px; padding: 30px 0 0 0; overflow: hidden; } .nav-item-1 { background-position: 0 0; width: 65px; } .nav-item-2 { background-position: -65px 0; width: 90px; } .nav-item-3 { background-position: -155px 0; width: 65px; } .nav-item-1-hover { background-position: 0 -30px; } .nav-item-2-hover { background-position: -65px -30px; } .nav-item-3-hover { background-position: -155px -30px; } .nav-item-1-selected { background-position: 0 -60px; } .nav-item-2-selected { background-position: -65px -60px; } .nav-item-3-selected { background-position: -155px -60px; }
So, let's break down what is going on here. The NavBar is a Composite which is holding a FlowPanel ("holder"). This FlowPanel is a DIV which is going to hold our 3 links: "blog", "about us", and "jobs". Each one of these links is an HTML widget (i.e. a DIV). Inside the method "link" we first are creating each HTML with the appropriate text, setting its style name with a unique integer, adding an additional style name. From there, we add the link to our "selector" as well as add a "hover" listener. Finally, we add a ClickListener which initiates an action (in this case taking the user to the blog).
Some things to note:
Without our CSS, the result would simply look like this:
Blog About Us Jobs
However, by adding a few style names, we are able to achieve a look like this:
We also streamlined our development by standardizing the use of stand-alone buttons on the site. That way, the GWT Programmer can create a button widget that can be reused depending on the size and color button needed. The widget uses DEPENDENT style names to produce small, medium, or large buttons colored with green, white, blue, or red that are preset in the style sheet and work for flexible content lengths.
It is really important to have a test environment for your Web Designer to mess with. As a GWT Programmer, you can't expect that your Web Designer will be able to run Hosted Mode. Also, until the GWT guys implement Out-of-Process Hosted Mode (OOPHM), you will definitely want to take a look at your product in all the major browsers before deploying to production. We run a Tomcat instance on a powerful box over at Amazon Web Services. Using SVN and Ant, we update all our code to the test machine, compile it, create a WAR and deploy it using one command. The key here is that this process takes over 6 minutes every time we want to deploy new code. Six minutes may not seem like that long, but when your Web Developer is waiting 6 minutes every time you need to make a small change to the layout, it gets annoying. So we implemented two ideas to help ease the pain.
First, if you take one thing away from this post, don't style using the GWT DOM class. Set your style names and let the CSS do the work. If we had put all the styles in the GWT code, it would take us 6 minutes to change the color of some trivial text, only to find out that we liked it better the old way (another 6 minutes).
Second, pull out your CSS files and images directory to some location outside your WAR. The idea here is that the Web Designer can manipulate the CSS and imagery without ever getting inside your WAR. This way, when its time to deploy new code, you don't overwrite the Web Designer's changes by replacing the CSS file and/or images on accident. This also helps with remote collaboration. The Web Designer gives the GWT Programmer the "blueprints", the GWT Programmer builds the app and deploys it to the test server, the Web Designer is free to make changes to the CSS and images while checking compatibility across all the browsers. Theoretically, you'd never have to be in the same room (although we don't prefer it that way).
Now get out there and build an awesome GWT house!