This page describes best practices for custom styling in GTK3. With GTK4, some details might be different.
Custom Style
GTK+ uses CSS for styling widgets nowadays. While it is not a 100% conformant CSS implementation, it is getting more and more complete and expressive. Applications can use this powerful machinery to change how widgets are rendered – for both stock GTK+ widgets and custom widgets.
A possible problem here is that custom styles may look fantastic with the theme that the developer was using at the time, but appear out-of-place (or even unusable) with another theme.
This HowDoI page aims to show best practices to get the most out of custom CSS while minimizing the downsides.
Standard Style Classes
UI elements with similar functions should look similar, to help the user infer the function. GTK+ defines style classes for a broad range of 'standard' widgets, which you should use where appropriate, to 'inherit' theming for your custom widgets.
E.g. if you have a MyEntry widget that works similar to a GtkEntry, it is a good idea to reuse the predefined .entry style class. In C code, you would place this fragment in your instance init() function:
context = gtk_widget_get_style_context (widget);
gtk_style_context_add_class (context, GTK_STYLE_CLASS_ENTRY);
If you are using a template to define your widget, you can also place this information in the .ui file:
<style>
<class name="entry"/>
</style>
Custom Style Classes
You are not limited to using the style classes that are predefined in GTK+: you can add your own, and refer to them from your custom CSS. This is preferable to hard-coding the class name in the CSS. But be aware that it is unlikely that third-party themes will provide styling for your custom style classes, so you should have a default style that works reasonably.
Custom CSS
Custom CSS style sheets can be associated with individual widgets, or applied for the whole application. In most cases, the latter will be more practical. To avoid extra disk I/O, it is common to store custom CSS in a GResource. This can look as follows:
screen = gdk_screen_get_default ();
provider = gtk_css_provider_new ();
gtk_css_provider_load_from_resource (provider, "/application/xyz/custom.css");
gtk_style_context_add_provider_for_screen (screen, provider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
And you must not forget to set up the resource for inclusion in your .gresource.xml file:
<gresource prefix="/application/xyz">
<file>custom.css</file>
</gresource>
Theme Changes
GTK+ includes the Adwaita and HighContrast themes, but users are equally likely to use third-party themes. To work well in this case, you should consider only loading custom CSS for themes that you have tested it with. To achieve this, you should set up a signal handler that listens to theme changes and reloads the style provider accordingly:
static void
theme_changed (GtkSettings *settings, GParamSpec *pspec, MyApplication *app)
{
g_autofree gchar *theme = NULL;
g_object_get (settings, "gtk-theme-name", &theme, NULL);
if (g_strcmp0 (theme, "HighContrast") == 0)
gtk_css_provider_load_from_resource (app->provider, "/application/xyz/highcontrast.css");
else if (g_strcmp0 (theme, "Adwaita") == 0)
gtk_css_provider_load_from_resource (app->provider, "/application/xyz/adwaita.css");
else
gtk_css_provider_load_from_resource (app->provider, "/application/xyz/default.css");
}
g_signal_connect (gtk_settings_get_default (), "notify::gtk-theme-name",
G_CALLBACK (theme_changed), app);
Of course, you can include suitable CSS for other themes as well.