GWT can be hard to tackle at first, especially if your experience is entirely in front-end web development. You may be an HTML and CSS ninja, but not much of a Java wizard. This tutorial introduces how style sheets are used in GWT. I will not explain how to create a new GWT project, and will assume that you have an application you can run. That will let us focus on style sheets in GWT.
Once completed, don’t forget to have a look at the second part : Managing your CSS (GSS) files with variables and a theme.
From the beginning
If there is no style sheet inside of your application, or if you want a rough understanding of how it works, here’s a small step-by-step guide to add your default CSS file. Note that your application might be using a different folder structure than my example program, and that you will have to replace com/company/project
to match your project.
The CSS file
We will create our initial CSS file into src/main/resources/com/company/project/client/resources/css
and will name it style.gss
. That’s right, .gss
and not .css
, as GSS files are like CSS files, but with more power. And we always want more power, don’t we?
We should add at least one line in our file, so we know that it’s working. Let’s use :
body { background-color: pink; }
At this point, it should not be working yet.
The Resource file
We then need to create the resource file that will be the bridge between your GSS file and your Java classes. This is because GWT will obfuscate class names on compliation, so this file will handle the links between the obfuscated names and classes inside of your GSS file.
Let’s create AppResources.java
inside of src/main/java/com/company/project/client/resources
The code inside of it goes like this:
package com.company.project.client.resources; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; public interface AppResources extends ClientBundle { interface Style extends CssResource { } @Source("css/style.gss") Style style(); }
The Resource Loader file
We now need to setup the loader file to, well, to load everything inside the application.
Once again inside src/main/java/com/company/project/client/resources
, create a file named ResourceLoader.java
, containing :
package com.company.project.client.resources; import javax.inject.Inject; public class ResourceLoader { @Inject ResourceLoader( AppResources appResources) { appResources.style().ensureInjected(); } }
The ensureInjected() is the important part here, as it will inject your CSS into the DOM.
Don’t forget to bind this file inside your ClientModule, probably located at src/main/java/com/company/project/client/gin/ClientModule.java
package com.company.project.client.gin; // [your imports] public class ClientModule extends AbstractPresenterModule { @Override protected void configure() { // [your code] bind(ResourceLoader.class).asEagerSingleton(); } // [you might have code here too] }
Activate GSS
GSS has been included in GWT since version 2.7, but is not enabled by default. To enable it, you need to add one line of code inside your GWT module. Mine is located at src/main/java/com/company/project/AppModule.gwt.xml
and looks like this:
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.7.0//EN" "http://gwtproject.org/doctype/2.7.0/gwt-module.dtd"> <module rename-to="AppModule"> // [Inheriting some stuff] // [Source some stuff] // The line that you need to add <set-configuration-property name="CssResource.enableGss" value="true"> // [Some more code extending ... thing?] </module>
Voilà! If things went well, your application should build and run, loading your style.gss
file in the process. Don’t worry if it fails, as you might have your application structured in a different way than shown in this tutorial. If you take time to read compile errors, you should manage to make it right. If you don’t, but have a backend dev near you, it’s always good to ask for help.
Using classes
One of the most confusing thing at first while working on GSS files in GWT is that you will need to declare your classes inside of your resource file to be able to access them.
Let’s add some classes into our style.gss
file :
body { background-color: pink; padding: 2em 0; } .my_first_class { color: red; } .my_second_class { color: blue; }
We then need to adapt the resource file accordingly:
package com.company.project.client.resources; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; public interface AppResources extends ClientBundle { interface Style extends CssResource { String my_first_class(); String my_second_class(); } @Source("css/style.gss") Style style(); }
Congratulations! You can now use you classes inside your views (you know, the files ending with .ui.xml
) and even with your Java files.
We will first need a view. The file that we will be working on, aka ApplicationView.ui.xml
, looks like this:
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'> <div> <h1>Hello World!</h1> <p>This is my first paragraph.</p> <p>This is my second paragraph.</p> </div> </ui:UiBinder>
In order to use our classes, we will need to import the desired resource into the view :
<ui:with field="resources" type="com.company.project.client.resources.AppResources"/>
We can now call the required classes using resources.style.name_of_my_class
In the precedent view, the final code would be :
<ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'> <ui:with field="resources" type="com.company.project.client.resources.AppResources"/> <div> <h1>Hello World!</h1> <p class="{resources.style.my_first_class}">This is my first paragraph.</p> <p class="{resources.style.my_second_class}">This is my second paragraph.</p> </div> </ui:UiBinder>
You should know that Java uses the CamelCase naming convention, and that hyphenated class names using “-"
will not compile. In order to make it work, you will need to use @ClassName
inside of your CssResource:
@ClassName("my-class-name") String myClassName();
Structure
It’s good to split your CSS into multiple files. It will be easier to maintain and understand, and will help you think in blocks and structure your classes accordingly; rather than having only one CSS file that does everything, but where you are not sure what that everything is.
A structure that I like, (but feel free to adapt it), is this one:
/resources /css /elements // -> Elements that you reuse here and there section.gss form.gss anyElement.gss /pages // -> Pages related style login.gss contact.gss style.gss // -> Your main style
Working with multiple GSS files
While working with multiple GSS files, you have two options: you can create a resource file for that GSS and include it only in views that will need it, or you can declare multiple GSS files into the same resource.
One resource file per GSS
It might sound easier to stack everything into the same resource file, but as with CSS files, it’s always better to break your code in blocks. If you have a contact.gss
file that will only be used inside of your contact.ui.xml
, you should create a dedicated resource for that. And I’d guess that you would also need your default style inside that view.
// src/main/resources/com/company/project/client/resources/css/pages/contact.gss .contact { background-color: orange; padding: 4em; } .contact_form { width: 100%; border: 1px solid #00f; }
// src/main/java/com/company/project/client/resources/pages/ContactResources.java package com.company.project.client.resources.pages; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; public interface AppResources extends ClientBundle { interface Style extends CssResource { String contact(); String contact_form(); } @Source("com/company/project/client/resources/css/pages/contact.gss") Style style(); }
// src/main/java/com/company/project/client/application/contact/ContactView.ui.xml <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'> <ui:with field="resources" type="com.company.project.client.resources.AppResources"/> <ui:with field="pageResources" type="com.company.project.client.resources.pages.ContactResources"/> <div class="{pageResources.style.contact}"> <h1>My contact page!</h1> <p class="{resources.style.class_from_style_gss}"> This is a beautiful contact form. </p> <form class="{pageResources.style.contact_form}"> <input type="email" placeholder="your email please" /> <input type="submit" value="Go!" /> </form> </div> </ui:UiBinder>
Multiple GSS files in the same resource
Let’s say you have a small application with only 3 pages: homepage, services and contact. For each page, you created a corresponding GSS file (homepage.gss
, services.gss
and contact.gss
).
You could create one resource file for each, like in the previous example. You could also create one resource file to rule them all, named PageResources.java
.
// src/main/java/com/company/project/client/resources/PageResources.java package com.company.project.client.resources; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; public interface AppResources extends ClientBundle { interface Homepage extends CssResource { String homepage(); String homepage_big_title(); [other cool classes related to homepage only] } interface Services extends CssResource { String services(); [other cool classes related to services only] } interface Contact extends CssResource { String contact(); String contact_form(); [other cool classes related to contact only] } @Source("com/company/project/client/resources/css/pages/homepage.gss") Homepage homepage(); @Source("com/company/project/client/resources/css/pages/services.gss") Services services(); @Source("com/company/project/client/resources/css/pages/contact.gss") Contact contact(); }
You will need to update your ResourceLoader.java
accordingly:
package com.company.project.client.resources; import javax.inject.Inject; public class ResourceLoader { @Inject ResourceLoader( AppResources appResources) { appResources.homepage().ensureInjected(); appResources.services().ensureInjected(); appResources.contact().ensureInjected(); } }
Now, you can include the PageResources in each of your pages. It will give you access to every interfaces in it (homepage, services, contact).
// src/main/java/com/company/project/client/application/contact/ContactView.ui.xml <ui:UiBinder xmlns:ui='urn:ui:com.google.gwt.uibinder'> <ui:with field="resources" type="com.company.project.client.resources.AppResources"/> <ui:with field="pageResources" type="com.company.project.client.resources.PageResources"/> <div class="{pageResources.contact.contact}"> <h1>My contact page!</h1> <p class="{resources.style.class_from_style_gss}"> This is a beautiful contact form. </p> <p class="{pageResources.homepage.homepage_big_title}"> Hey look, I can also use a class from the homepage, even though I should probably don't do this, right? </p> <form class="{pageResources.contact.contact_form}"> <input type="email" placeholder="your email please" /> <input type="submit" value="Go!" /> </form> </div> </ui:UiBinder>
Using images
If you want to use images inside of your GSS file, you will need to declare them as an ImageResource. Your image will need to be inside your resources folder, and like your GSS files, it’s always a good thing to create a proper folder structure in order to maintain control.
// src/main/java/com/company/project/client/resources/pages/ContactResources.java package com.company.project.client.resources.pages; import com.google.gwt.resources.client.ClientBundle; import com.google.gwt.resources.client.CssResource; import com.google.gwt.resources.client.ImageResource; // ^ don't forget to import the ImageResource package public interface AppResources extends ClientBundle { interface Style extends CssResource { String contact(); String contact_form(); } @Source("images/contact/background.jpg") ImageResource background(); @Source("com/company/project/client/resources/css/pages/contact.gss") Style style(); }
// src/main/resources/com/company/project/client/resources/css/pages/contact.gss @def BACKGROUND resourceUrl("background"); .contact { background: BACKGROUND no-repeat center center; padding: 4em; } .contact_form { width: 100%; border: 1px solid #00f; }
Conclusion
CSS with GWT is not an easy task at first, but after mastering the basics, you can regain your status as a front end ninja in GWT-land. Find the workflow that suits your need, structure your files and don’t forget to have fun!
In my next post, I will teach you how to leverage the real power behind GSS and simplify your life, using variables and a theme file. Stay tuned!
How to insert code blocks in comments? I’m stuck 😦
To publish code (no spaces after [ and bedore ]) :
Markdown has been activated for comments. It should be working for you now 🙂
Instead of using ui:with you’d better use ui:import in UiBinder templates.
Then in ui.xml you can get more efficient and shorter code: