New custom widgets and their CSS

If I create a widget to be released as a standalone library usable by everyone and this requires some minimum fallback CSS in case themes don’t cover it (granted at the moment, since my widget does not exist yet), where do I have to install my style.css, so that it is always loaded by default by every GTK app?

I think it’s ~/.config/gtk-3.0/gtk.css or ~/.config/gtk-4.0/gtk.css depending on which version you app targets, I did use it quite a while ago with GTK3 and it worked, but right now I can’t find any official documentation of this specific feature so please don’t quote me on this.

@yPhil

If I create a package I need a system location, not a user location. And also I hope I don’t have to hack an existing CSS by pushing my code inside. Isn’t there a directory whose stylesheets are automatically loaded (blindly) by GTK?

No, there isn’t.

If you are shipping your custom widget in a library you’ll have to have an init function that creates a GtkCssProvider and applies it to the default display; you can then use that to load the custom CSS that you want to ship. This is what libadwaita does, for instance.

The CSS fragment can be in a GResource file, if you don’t want to inline it into your library; for Python modules it’s slightly less convenient because you have to ship an additional file, but it’s still easier to do that if you have to ship multiple other assets, like icons that your custom CSS relies on.

Hi Emmanuele,

Sorry if this sounds stupid. Let’s imagine that my widget is called XyzFooBar. If in my xyz_foo_bar_init() I create a GtkCssProvider and apply it to the default display, and the user creates 10 XyzFooBar widgets, will the same GtkCssProvider not be applied to the same display 10 times?

It will, though it’s a massive waste, since you’re creating 10 instances and registering the exact same CSS fragment for every instance.

That’s why I recommended a library-wide init function, like adw_init(), that any projects that depend on your library must call to initialise the custom styles you provide.

Thank you, all clear.

May I suggest that GTK introduces a /usr/share/gtk-4.0/css.d/ directory (or any other path) in which widget developers can drop their stylesheets, which will be automatically loaded?

That would be a complete disaster, so: no, it’s not going to happen.

People would be able to drop random fragments of CSS into that directory and then random applications would utterly break. What happens if two fragments conflict? What happens if two fragments depend on each other, but are loaded in the wrong order?

Libraries with random CSS fragments get to load their CSS when they need it; GTK won’t do that for them.

But a directory like /usr/share/gtk-4.0/css.d/ would not be for people, it would be for package managers to populate… And if a package breaks another package it won’t be distributed.

That’s at least my simple suggestion.

What makes you think that developers (or packagers, for that matter) are any better at putting files in the right place than anybody else?

Spoiler alert: they aren’t. I wasn’t even considering users: developers and packagers are perfectly able to make an awful mess by themselves.

Plus, let’s be honest: you put a directory somewhere, it will be abused. It’s the inevitable reality of having a shared hierarchy in the file system.

Let alone that often you want different css for light and dark, or for regular and high contrast styles.

Okay, then maybe there could be another solution. Isn’t there some sort of do_once() functionality in the creation of GObjects? A way to invoke some code only with the creation of the first instance?

Alternatively that could be done by adding a static GtkCssProvider * xyz_foo_bar_css variable and simply checking that it is not NULL during the creation of each XyzFooBar widget. In case it is NULL a GtkCssProvider will be created and assigned to the default display.

In this way there will be only one single CSS assigned, at the expense of checking if a pointer is NULL each time.

My idea was for fallback CSS – really the bare minimum necessary.

For instance in my composite widget the children of a GtkFlowBox can have an :indeterminate state (besides the usual :selected state). Without a minimum CSS there will be no visual difference between normal and :indeterminate cells.

You can write a simple utility function:

void
theme_init (void)
{
  static gboolean initialized = FALSE;

  if (G_LIKELY (initialized))
    return;

  GtkCssProvider *p = gtk_css_provider_new ();

  GError *error = NULL;
  gtk_css_provider_load_from_resource (p, "/com/example/yourlibrary/some-style.css", &error);
  if (error) {
     g_error ("Unable to load style: %s", error->message);

  gtk_style_context_add_provider_for_display (gdk_display_get_default (),
                                              GTK_STYLE_PROVIDER (p),
                                              GTK_STYLE_PROVIDER_PRIORITY_THEME);

  g_object_unref (p);

  initialized = TRUE;
}

Then you call this from the widget’s instance initialization.

Once again: having a library initialization function is an easy alternative, as it allows you to do more one-off work. For instance, you could put there the g_type_ensure() calls for all the types in your library, to simplify the use of composite templates.

1 Like

That’s not minimal at all though. That’s a whole new state to style.

Minimal would be e.g. making some label bold, or padding: 0; somewhere.

@ebassi

Thank you, Emmanuele.

At this point I am still deciding the destiny of my widget. It is an emblem picker for my Nautilus Annotations extension (I had shared a preview in this post). It began as a simple specific widget, but it ended up being a very complete one, which allows to set the emblems of multiple files at the same time and manage possible inconsistencies between files without losses.

So I will probably first publish it with the next release of my extension. If it works well I might propose it to Nautilus developers (Nautilus still doesn’t have a way to set emblems as far as I know!). Or I might decide to release it as part of a separate module.

But In the meanwhile I need a CSS. So I will go with the static variable for now…

@alicem

We are in the realm of subjectivity here. But in my mind “minimal” is that value below which you get zero. And that is what happens without setting a minimal :indeterminate selector for my widget: zero differentiation (basically a functionality gets lost).

―madmurphy

The thing is that selected states can vary quite a lot. For example, they are very different between gtk default and libadwaita, so you already need completely different styles if you want to support both. It’s not minimal at all.

My emblem picker requires the following two rules at least:

emblempicker emblem:indeterminate {
	background-color: rgba(53, 132, 228, .3);
}

emblempicker emblem:selected {
	background-color: rgba(53, 132, 228, 1);
}

Scenario 1: Assuming that mine is the only emblempicker on the market, can loading them break any existing app? Answer: NO.

Scenario 2: Assuming that mine is not the only emblempicker on the market, and for a strange coincidence the competitor emblem picker also has children widgets named emblem, and these can also be :selected and :indeterminate, can loading my CSS disturb any existing app? Answer: Possibly, but very unlikely.

Discussion: The CSS could only create conflicts between almost identical emblem picker widgets (very unlikely). The conflict would be limited to a background color.

An emblem picker has never existed in the history of GTK. Can we imagine that all of a sudden people started to populate the market with their own emblem pickers? Answer: NO.

Conclusion: The CSS is minimal.

Yes, that style already breaks with libadwaita. It looks like this:

Screenshot from 2022-11-15 06-08-52

Yes, this is a list,but it would be exactly same with a grid.

  1. Don’t assume the text color is white.
  2. Don’t assume the selection color is blue.

@alicem

I am not a GTK style guru, I am only trying to guarantee a functionality. As soon as the widget is ready I would be really happy to receive suggestions and even totally to delegate my widget’s CSS to someone better than me at styling widgets.

I don’t understand. This is how the emblem picker looks at the moment, and this is exactly how I want it to look like:

emblem_picker_prototype

(I have added round borders and a few other things)

P.S. I am using libadwaita.