Managing your CSS (GSS) files with variables and a theme

My previous article showed you how to use CSS inside your GWT application. Today, we will introduce the concept of variables, and show you how to manage them effectively using theme files.

Variables

Post_GSS_Howto2_v1-03-white

One of the best examples of variable use in CSS is probably color management. If your application has a default blue color, let’s say #2054e8, and it is used in multiple places, you will need to remember this particular color code every time you specify that color. Then if you decide to make your default color the lovely #f31ae5 shade of pink, you will need to do a find and replace across your whole project, and hope that everything went well.

All these hassles can be avoided if you use variables for your colors, so let’s do that!

Creating the Colors file

We will create Colors.java in src/main/java/com/company/project/client/resources.

package com.company.project.client.resources;

public class Colors {
    /* -> App colors -- */
    public static final String C_PRIMARY = "#2054e8";
    public static final String C_SECONDARY = "#bea885";
    /* -> Default colors -- */
    public static final String C_BACKGROUND = "#f0efef";
    public static final String C_TEXT = "#323232";
    public static final String C_TEXT_LIGHT = "#a1a1a1";
}

We will discuss variable naming conventions later. Right now, you only need to understand that we can define a variable, like C_PRIMARY, and assign it a value, such as our initial shade of blue #2054e8.

The main purpose of this file is to contain all color values in the same place. It will then be easy to replace them later, and create different themes for our app.

Creating the associated GSS file

This is where we start to feel the real power of GSS over regular CSS files. After compilation, GSS files output basic CSS, transforming variables into the values associated with them.

In order to easily use our variables, we need to define them in a file. We will name that file colors.gss and put it into src/main/resources/com/company/project/client/resources/css.

@provide 'colors';

@def C_PRIMARY        eval("com.company.project.client.resources.Colors.C_PRIMARY");
@def C_SECONDARY      eval("com.company.project.client.resources.Colors.C_SECONDARY");
@def C_BACKGROUND     eval("com.company.project.client.resources.Colors.C_BACKGROUND");
@def C_TEXT           eval("com.company.project.client.resources.Colors.C_TEXT");
@def C_TEXT_LIGHT     eval("com.company.project.client.resources.Colors.C_TEXT_LIGHT");

We define variable names, and point them to the associated variables inside Colors.java.

The most important part is the very first line:

@provide 'colors';

We are providing this file with a name that we will be able to import later on, making the variables defined in this file accessible in other contexts.

Binding it with your default resources style file

Before we can use this file, we need to supply it to our style’s resource file. My default style file is named style.gss and it uses the AppResources.java resource file, located in src/main/java/com/company/project/client/resources.

You can probably guess what you’ll find in this file:

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 {
        // Your classes here
    }

    @Source("css/style.gss")
    Style style();
}

We can see that we are using style() with the file located at css/style.gss. We need to add the color.gss to @Source, so it’s accessible. Instead of a string, we need to pass an array of strings, pointing to the desired files.

    @Source({"css/colors.gss", "css/style.gss"})
    Style style();
}

Note that we need to declare colors.gss before style.gss. Otherwise, style.gss will try to load the colors.gss file, but it will not yet be defined, causing an error.

Using variables inside of the style file

Remember when we defined @provide 'colors'? It is now required inside style.gss. Once that’s done, you will have access to your variables!

@require "colors";

body {
    background-color: C_BACKGROUND;
    color: C_TEXT;
    font-size: 1.5rem;
}

Structure

You can define variables for a lot of things beside colors, like sizes and fonts. You could decide to create a variables.gss file and add all your variables there, but this can pose a problem as your application grows. I prefer to structure my files by what they do, giving each set of variables their own file:

// src/main/java/com/company/project/client/resources/variables
    -> Colors.java
    -> Fonts.java
    -> Sizes.java

// src/main/resources/com/company/project/client/resources/variables
    -> colors.gss
    -> fonts.gss
    -> sizes.gss

This way, if you only need to use Colors inside of a specific style sheet, you can import them alone. Variable types become more modular.

Naming Conventions

As variables are not prefixed with any symbols, it helps to write them in uppercase. That way it’s easy to spot them in your CSS.

I also like to prefix them with a single letter that represents the type of variable that it is.

- Colors.java   -> C_VARIABLE_NAME (C_PRIMARY, C_BACKGROUND, C_TEXT ...)
- Fonts.java    -> F_VARIABLE_NAME (F_PRIMARY, F_SECONDARY_BOLD ...)
- Sizes.java    -> S_VARIABLE_NAME (S_SECTION_PADDING, S_LINE_HEIGHT ...)

Give your variables meaningful names, something that you will be able to remember and understand upon reading. Describe the purpose of the variable, not its value.

For example, F_PRIMARY is the primary font used inside the app, the most common one that will be almost everywhere. I find the use of primary / secondary easy to remember. It has a strong meaning, and it’s about what the variable is used for, and not about the value held by the variable. A bad example would be F_ARIAL for the same variable with the same purpose. It’s bad because if, in the middle of the project, you decide to change the main font from Arial to Helvetica, you will need to refactor the name of your variable. This is what we are trying to avoid.

Instead of C_RED for an error message color, go for C_ERROR. Or even better : C_STATE_ERROR. You will be able to manage your state colors easily, like C_STATE_SUCCESS and C_STATE_DEFAULT without worrying about the color it is.

Theme

Post_GSS_Howto2_v1-04-white

The problem

If you create different variable files, you gain the ability to only load the ones you want. This is both good and bad. If you need several variable types, you have to specify each one of them inside of your resource loader, and then require them in your GSS file. This can quickly become annoying, and it makes your code harder to maintain.

// src/main/java/com/company/project/client/resources/AppResources.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 Style extends CssResource {
        // Your classes here
    }

    @Source({"css/variables/colors.gss",
             "css/variables/fonts.gss",
             "css/variables/sizes.gss",
             "css/style.gss"})
    Style style();
}
/* src/main/resources/com/company/project/client/resources/css/style.gss */

@require "colors";
@require "fonts";
@require "sizes";

body {
    background-color: C_BACKGROUND;
    color: C_TEXT;
    font-family: F_PRIMARY;
    line-height: S_LINE_HEIGHT;
}

As you can see, if you have multiple GSS files, you need to include required variables inside each of those files. And if you need to add another variable set, or remove one, you will need to go inside each of your Resource and GSS files and adapt them accordingly. And that sucks.

The solution

Faced with the above problem, you might think going back to having just one Variables.java file and a variables.gss file is not a bad idea after all. But we can have our cake and eat it too. We can keep the separation in our Java files and have only one GSS to rule them all. This way, variable files will remain modular, and yet we will still have only one file to require in each GSS file that need to access variables. If we need to change the structure, like adding a new subset of variables, we will only need to update one file, and that’s all!

I call this super GSS file theme.gss because it will be used to produce different themes, but you could name it variables.gss and it would still be totally accurate.

// src/main/resources/com/company/project/client/resources/css/theme.gss

@provide 'theme';

/**
 * Colors
 */

@def C_PRIMARY                  eval("com.company.project.client.resources.theme.Colors.C_PRIMARY");
@def C_PRIMARY_BACKGROUND       eval("com.company.project.client.resources.theme.Colors.C_BACKGROUND");
@def C_TEXT                     eval("com.company.project.client.resources.theme.Colors.C_TEXT");
/* ... */

/**
 * Fonts
 */

@def F_PRIMARY                  eval("com.company.project.client.resources.theme.Fonts.F_PRIMARY");
@def F_PRIMARY_BOLD             eval("com.company.project.client.resources.theme.Fonts.F_PRIMARY_BOLD");
/* ... */

/**
 * Sizes
 */

@def S_LINE_HEIGHT              eval("com.company.project.client.resources.theme.Sizes.S_LINE_HEIGHT");
@def S_SECTION_PADDING          eval("com.company.project.client.resources.theme.Sizes.S_SECTION_PADDING");
/* ... */

With this new method, things are now easier to maintain:

// src/main/java/com/company/project/client/resources/AppResources.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 Style extends CssResource {
        // Your classes here
    }

    // We only need to source the theme file
    @Source({"css/theme.gss",
             "css/style.gss"})
    Style style();
}
/* src/main/resources/com/company/project/client/resources/css/style.gss */

/* And we only have one file to require */
@require "theme";

body {
    /* All variables are still accessibles */
    background-color: C_BACKGROUND;
    color: C_TEXT;
    font-family: F_PRIMARY;
    line-height: S_LINE_HEIGHT;
}

As the constants’ values are stocked in Java files, these constants can be modified easily. With this technique, it is now super easy to create different themes that can be user-based or changed at compile time.

Updated Structure

Using a theme file, my structure now looks like this:

src/main/java/com/company/project/client/resources/
    -> elements/
    -> pages/
    -> theme/
        -> Colors.java
        -> Fonts.java
        -> Sizes.java
    -> AppResources.java
    -> ResourcesLoader.java

src/main/resources/com/company/project/client/resources/css
    -> elements/
    -> pages/
    -> style.gss
    -> theme.gss

Tips

A variable will output the string of text inside of it into your CSS. Sometimes, you will need a variable to output more then one set of properties. You can do so this way:

// src/main/java/com/company/project/client/resources/theme/Fonts.java
public class Fonts {
    public static final String F_PRIMARY = "'My Cool Typo', sans-serif";
    public static final String F_PRIMARY_BOLD = "'My Cool Typo', sans-serif; font-weight: 700";
}

Can you spot the trick in F_PRIMARY_BOLD? The variable is defining the font-family, closed with the semicolon ;, then define the font-weight, without a closing semicolon ; because the closure will be handled in the CSS.

/* src/main/resources/com/company/project/client/resources/css/theme.gss */
body {
    font-family: F_PRIMARY;
    /* will output : */
    font-family: 'My Cool Typo', sans-serif;
}

h1 {
    font-family: F_PRIMARY_BOLD;
    /* will output : */
    font-family: 'My Cool Typo', sans-serif;
    font-weight: 700;
}

Conclusion

You now understand the true power of variables. When used with a theme file, the management of your application becomes a breeze. Things can be made even easier, using a tool to manage the generation of your theme and the associated files. But that, my friends, is a topic for another blog post.

Post_GSS_Howto_v2-03

2 comments

  1. Ed · May 1, 2015

    Thanks for the inspiration.
    I am missing some practical details like how to switch between Themes or how to output app’s with different themes (further details about ResourceLoader maybe)
    You might want to have a look at the Theming comment (with code example) in this post: https://groups.google.com/forum/#!topic/google-web-toolkit/mrBctJ2BGtI (posted in detail before).
    It’s component based, flexible, dynamic (usage of instance methods), and use of the java compiler (GSS not detect errors while typing).
    Currently I use a mixture of GSS and the Preference mechanism. The theme gwt config files include the required preference classes.

    Keep up the good work.

  2. Lu · May 4, 2015

    There is a similar way using the old CssResources (different sintax) for creating themes.
    I did something similar using .css. (instead of colors called palette)

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s