Wibbly Stuff

When you make a GTK3 theme...

GTK themes in Gnome 3 follow a new syntax that is different from GTK2. The new CSS syntax in GTK3 makes it easier to make themes. Though it is easy to understand the CSS syntax than the old gtkrc syntax, it is not enough to know just CSS. GTK themes are more than that. A good start can be this tutorial and specifically this.

I will try to explain in brief what I have learnt about GTK3 theming. It might not be perfect, but I think it can do a little help.

You will certainly not want to make a theme from scratch. It is a lot of work to do. Instead you should start by modifying an existing theme. It is important what theme you choose, as it will affect the quality, ease of customization and maintainability of your theme. Here I highlight some steps to take into consideration.

Choose a GTK3 engine

The theme engine is what draws the theme. Different theme engines may support different features and syntax. For GTK3, currently I'm aware of two theme engines, namely Adwaita and Unico.

The Adwaita engine is from the official Gnome project. If you want to make a simple theme, choose this. By simple, I mean you don't need multiple borders and glows on buttons and likes. Adwaita supports the box shadow property (only inset) and it will be sufficient if you need multiple borders and shadows in those few cases. You can make box shadows look like borders, don't you?

Examples: Adwaita, Gnomish Gray etc.

The Unico engine has more options like inner and outer strokes, glows etc. It will be suitable for you if you gonna make use of the features.

Examples: Ambiance, Zukitwo, Elementary etc.

Make sure you install the required theme engine before you start making your theme. Or it will not look right.

You can also make a theme without using any engine. In this case it will use the inbuilt engine.

Examples: Evolve, Albatross

Understand the theme files

Usually, GTK themes reside in the ~/.themes (user specific) or /usr/share/themes directory. The GTK3 theme should be in a directory named gtk-3.0 inside the theme directory.

Example: For the Ambiance theme, it would be /usr/share/theme/Ambiance/gtk-3.0

Inside the gtk-3.0 folder, you have different files, namely gtk.css, gtk-dark.css, gtk-widgets.css, settings.ini etc.

The gtk.css file is the main theme file. It mainly contains theme color definitions. The gtk-dark.css file contains a dark color scheme which, if present, is used by apps that ask for a dark theme instead of gtk.css. The settings.ini file consists of various gtk options.

Widget specific styles are mainly described in the gtk-widgets.css file, which is imported into the gtk.css file so that it will be read by GTK.

Any additional CSS files would have to be imported in order to be read by the theme.

Here is a somewhat basic example of the structure of the gtk.css file.

/* This defines the color scheme */
@define-color bg_color #ccc;
@define-color fg_color #333;
@define-color base_color #fff;
@define-color text_color #000;
@define-color selected_bg_color #57c;
@define-color selected_fg_color #eee;
@define-color tooltip_bg_color #ccc;
@define-color tooltip_fg_color #333;

/* An example of how a CSS file is imported */
@import url("gtk-widgets.css");

Here the colors are defined with the @define-color rule. To import another CSS file, the @import rule is used. You can study the gtk.css file of different themes to have a better idea.

The gtk-widgets.css file describes the widget styles in CSS syntax. To understand it, I recommend to have a look here (assuming you already know CSS).

At the top of the gtk-widgets.css file, you may notice the theme engine declaration, some gtk properties, engine specific properties and style properties.

/* The "*" means it will apply to all widgets */
* {
/* This defines the theme engine */
engine: adwaita;

/* Some common style properties */
border-width: 1px;

/* GTK properties */
-GtkArrow-arrow-scaling: 0.6;
-GtkCheckButton-indicator-size: 16;
-GtkProgressBar-min-horizontal-bar-height: 21;
-GtkProgressBar-min-vertical-bar-width: 21;
-GtkTextView-error-underline-color: @error_color;
-GtkWidget-link-color: @link_color;

/* Theme engine specific properties */
-adwaita-focus-border-color: alpha (@theme_selected_bg_color, 0.5);
-adwaita-focus-border-radius: 2;
-adwaita-focus-border-dashes: 0;
}

Different widgets may also contain GTK and engine specific properties. For example,

.button {
color: #333;
padding: 3px;

-GtkButton-child-displacement-x: 0;
-GtkButton-child-displacement-y: 0;

-adwaita-focus-border-color: transparent;
}

As you can see, you can either use the hex, rgb or rgba values to represent colors, or you can use the symbolic colors which are defined in the gtk.css file prefixed by a @ symbol.

You can also manipulate the colors using alpha, shade, mix etc. for adding transparency, darkening the color and mixing two colors respectively.

Best practices

It is always necessary to follow some common practices throughout your theme. It will make maintenance easier.
  • Try not to unnecessarily repeat code. For example if you defined border-width: 1px for the whole theme at the top, no need to write it everywhere you want a 1px border.
  • Use symbolic colors instead of hard coded hex values. It makes it easier to change the color scheme and work on things like a dark theme.
  • Try to use well thought and distinct files for different scenario, e.g. - gtk.css, gtk-dark.css, gtk-widgets.css, gtk-widgets-backdrops.css, gtk-widgets-assets.css.
  • Put app specific styles and assets separated from the main theme files in a different folder for easy maintenance and less clutter.
  • Take advantage of the theme engine features. For example, the Unico engine can draw radios and check-boxes. So you don't need to use image files for that, unless your style is very different.
  • Write the code with proper readability. Use indention where necessary. If you use tab for indention, then use the same throughout the theme. If you use spaces for indention, then use the same throughout the theme.

I don't say these are the best, but at least they are good practices which I try to folllow.

Pick up a theme to modify

Now it's time to pick up a suitable GTK3 theme. It is always better to choose official themes like Ambiance (if you want to use Unico engine) and Adwaita (if you want to use Adwaita engine), since they tend to have lesser bugs.

I will also recommend using Evolve as a base since it is pretty minimal, but still covers almost all widgets. It doesn't use any specific theme engine, and hence you can use whatever engine you want.

Work on the theme

After you choose a theme, you can begin modifying it. Be sure to test it before releasing. I recommend installing the gtk-3-examples package which contains the gtk3-widget-factory to test your themes. Also install libgranite-demo if you also want to support the granite widgets used in elementary.



You may find that some properties are not supported in some widgets. For example, the status bar widget won't support borders an so on. You may try to find alternatives in such cases. For example, you may use -unico-inner-stroke or box-shadow properties instead of border where they are supported.

After you're done, it is a good idea to start a GTK application from Terminal to see if any error messages appear about the theme.

For distribution, you may distribute it via repositories for different distros or in compressed archives. Distributing in zip archives is more suitable as tools like Gnome Tweak Tool support installing themes directly from zip files.

The challenges

Let's face it, you cannot a make GTK3 only theme. You have to provide a GTK2 theme which matches the GTK3 theme. Otherwise there will be no integration between GTK2 and GTK3 apps and it will look really bad. And you may also provide a matching window theme, like metacity, mutter, xfwm etc. GTK2 and metacity theming are a pain, but you have to do it if you want people to use your theme.

This is a small attempt to explain various aspects of GTK3 theming in very brief and by no means a full blown tutorial. I believe you can learn as you do, tutorials won't make much difference. I hope this post was useful.