You might have noticed that a lot of icons in GNOME 41 have been redrawn to be larger. They are still 16×16, but take up more of the space within the canvas. Compare:
This was a preparation for a larger change that has just landed in the main branch in libadwaita: buttons in header bars and a few other widgets have no background now, matching this mockup.
For example, this is how the recent GTK4 branch of GNOME Software looks like now:
Making the style feel lighter and reducing visual noise is a major goal for the style refresh we’re doing for libadwaita. While we’ve done lots of smaller changes and cleanups across the stylesheet to bring us closer to that goal, this is probably the highest-impact part of it due to how prominent header bars are in GNOME apps.
This is not a new idea either — pretty much everyone else is doing it, e.g. macOS, Windows, iOS, Android, elementary OS, KDE.
In fact, we’ve been doing this for a long time in view switchers. So this just extends it to the whole header bar.
However, if applied carelessly, it can also make certain layouts ambiguous. For example, a text-only button with no background would look exactly same as a window title. To prevent that, we only remove background from buttons that we can be confident won’t look confusing without it — for example, buttons containing a single icon.
While we avoid ambiguous situations, it also means that apps will need changes to have consistent buttons. In my opinion this is a better tradeoff: since the API is not stable yet, we can break behavior, and if an app hasn’t been updated, it will just get inconsistent look and not accessibility issues.
Details
The exact rules of what has background and what doesn’t are as follows:
The following buttons get no background:
- Buttons that contain icons (specifically the
.image-button
style class). - Buttons that contain an icon and a label, or rather, the
.image-text-button
style class. - Buttons with the
.flat
style class. - UPDATE: Any
GtkMenuButton
s with a visible arrow (the.arrow-button
style class). - UPDATE: Any split buttons (more on that later).
The following buttons keep their default appearance:
- Text-only buttons.
- Buttons with custom content.
- Buttons with
.suggested-action
or.destructive-action
style classes. - Buttons inside a widget with the
.linked
style class. - A new addition: buttons with the
.raised
style class, as inspired by the elementary OS stylesheet.
The appearance of GtkMenuButton
and AdwSplitButton
(more on that later) is decided as if they were regular buttons.
UPDATE: menu buttons with visible arrows and split buttons don’t have a background anymore regardless of their content.
This may look a lot like the old GtkToolbar
, and in a way it is. While GTK4 doesn’t have GtkToolbar
, it has the .toolbar
style class to replace it, and this style is now shared with header bars and also GtkActionBar
.
Special cases
While the simple icon-only buttons are easy, a lot of applications contain more complex layouts. In that case we fall back to the default appearance and apps will need changes to opt into the new style for them. Let’s look at some of those patterns and how to handle them:
Menu buttons with icons and dropdown arrows
This case works as is if you use the standard widgets — namely, GtkMenuButton
with an icon set via icon-name
and always-show-arrow
set to TRUE
.
The only reason this case is special is because always-show-arrow
is relatively new, having only been added in GTK 4.4, so a lot of apps will have custom menu buttons, or, if porting from GTK3, GtkMenuButton
containing a GtkBox
with an icon and an arrow. Since we don’t remove backgrounds from buttons with custom content, both of them will have backgrounds.
Text-only buttons
This is the most common case outside icon-only buttons. For these buttons the solution, rather counter-intuitively, is to add an icon. Since the icon has a label next to it, it doesn’t have to be very specific, so if an action is hard to describe with an icon, an only tangentially related icon is acceptable. If you still can’t find anything fitting — open an issue against the GNOME icon development kit.
With GTK widgetry, the only way to create such buttons is to pack a GtkBox
inside, and create the icon and the label manually. Then you’ll also have to add the .image-button
and .text-button
style classes manually, and will need to set the mnemonic-widget
property on the label so that mnemonics work.
Since this is tedious and parts like connecting the mnemonic are easy to miss, libadwaita now provides AdwButtonContent
widget to do exactly that. It’s intended to be used as a child widget of GtkButton
, GtkMenuButton
or AdwSplitButton
(more on that below), as follows:
<object class="GtkButton"> <property name="child"> <object class="AdwButtonContent"> <property name="icon-name">document-open-symbolic</property> <property name="label" translatable="yes">_Open</property> <property name="use-underline">True</property> </object> </property> </object>
If it’s a GtkMenuButton
, it would also make sense to show a dropdown arrow, as follows:
<object class="GtkMenuButton"> <property name="menu-model">some_menu</property> <property name="always-show-arrow">True</property> <property name="child"> <object class="AdwButtonContent"> <property name="icon-name">document-open-symbolic</property> <property name="label" translatable="yes">_Open</property> <property name="use-underline">True</property> </object> </property> </object>
UPDATE:Menu buttons with visible arrows don’t have background by default anymore, the step above is not necessary.
Note: the child
property in GtkMenuButton
is fairly new, and is not in a stable release as of the time of writing. It should land in GTK 4.6.
Notice we didn’t have to add any style class to the buttons or to connect mnemonics like we would have with GtkLabel
. AdwButtonContent
handles both automatically.
Split buttons
This is a fairly rare case, but also a difficult one. Historically, these were implemented as 2 buttons in a .linked
box. Without a background, it’s easy to make it look too similar to a regular menu button with a dropdown arrow, resulting in an ambiguous layout.
While existing split buttons will keep their background thanks to the .linked
style class, we now have a way to make consistent split buttons – AdwSplitButton
.
Whether they get a background or not depends on the content of their button part, while the dropdown part follows the suit – they will have background if it has only a label, will have no background if it has an icon, and will keep their default appearance outside header bars or toolbars. If it has no background, a separator is shown between them and they gain a shared background when hovered, pressed, or the dropdown is open:
They can be adapted the same way as regular buttons — via AdwButtonContent
:
<object class="AdwSplitButton"> <property name="menu-model">some_menu</property> <property name="child"> <object class="AdwButtonContent"> <property name="icon-name">document-open-symbolic</property> <property name="label" translatable="yes">_Open</property> <property name="use-underline">True</property> </object> </property> </object>
UPDATE: split buttons with text or custom content don’t get background by default anymore, so the step above is not necessary.
Meanwhile, buttons like the list/grid selector in Files are as simple as:
<object class="AdwSplitButton"> <property name="menu-model">some_menu</property> <property name="icon-name">view-list-symbolic</property> </object>
Otherwise, AdwSplitButton
API is mostly a union of GtkButton
and GtkMenuButton
– the button part can have a label, an icon or a custom child, an action name and target, and a clicked
signal if you prefer to use that. Meanwhile, the dropdown part has a menu model or a popover, and a direction that affects where the popover will be shown, as well as where the dropdown arrow will point.
Finally, in a lot of cases layouts that were using split buttons can be redesigned not to use them – for example, to use a simple menu button for opening files like in Text Editor instead of a split button in Apostrophe).
Linked buttons
With visible frames, linking buttons is a nice way to visually group them. For example, we commonly do that for back/forward buttons, undo/redo buttons, mode switching buttons. We also use multiple groups of linked buttons to separate them from each other.
For the most part linked buttons can, well, stop being linked. For example, back/forward buttons like this:
can become this:
However, when multiple linked groups are present, just unlinking will remove the separation altogether:
In that case, additional spacing can be used. It can be achieved with a GtkSeparator
with a new style class .spacer
:
<object class="GtkSeparator"> <style> <class name="spacer"/> </style> </object>
Action dialog buttons
This special case is less special than other special cases (or more special, if you prefer), in that apps don’t need to handle it, but I’ll mention it for the sake of completeness.
The two primary buttons in an action dialog or a similar context (for example, when changing display resolution in Settings, or the Cancel button in selection mode) should keep their current style — that is, they don’t have icons and keep their background. Meanwhile any extra buttons follow the new style.
In most situations this will already be the case so no changes are needed.
Other
There will undoubtedly be cases not covered here. The .flat
and .raised
style classes can always be used to override the default appearance if need be.
Finally, not everything has to have no background. For example, the remote selector in Software is probably best kept as is until it’s redesigned to also make it adaptive.
And in rare cases, the existing layout just doesn’t work and may need a redesign.
Bundled icons
In addition to all of that, if you bundle symbolic icons, there’s a good chance there are updated larger versions in the icon library. It would be a good idea to update them to match the new system icons.
Examples
Let’s update a few apps. App Icon Preview and Obfuscate should show off most of the edge cases.
App Icon Preview
The version on Flathub is still using GTK3 as of the time of writing, but it’s GTK4 in main. So let’s start from there.
App Icon Preview has 2 windows, each with its own header bar — the main window and the "New App Icon" dialog.
After the libadwaita update, the dialog hasn’t changed, meanwhile the main window looks like this:
It has a custom split button, as well as a text-only Export button when a file is open.
First, let’s replace the split button with an AdwSplitButton
:
<object class="GtkBox"> <child> <object class="GtkButton"> <property name="label" translatable="yes">_Open</property> <property name="use_underline">True</property> <property name="tooltip_text" translatable="yes">Open an icon</property> <property name="action_name">win.open</property> </object> </child> <child> <object class="GtkMenuButton" id="recents_btn"> <property name="tooltip_text" translatable="yes">Recent</property> <property name="icon_name">pan-down-symbolic</property> </object> </child> <style> <class name="linked"/> </style> </object>
This will become:
<object class="AdwSplitButton" id="open_btn"> <property name="label" translatable="yes">_Open</property> <property name="use_underline">True</property> <property name="tooltip_text" translatable="yes">Open an icon</property> <property name="action_name">win.open</property> </object>
Since we’ve changed the class and the GtkBuilder
id, we also need to update the code using it. Hence this:
#[template_child] pub recents_btn: TemplateChild<gtk::MenuButton>,
becomes this:
#[template_child] pub open_btn: TemplateChild<adw::SplitButton>,
and the other recents_btn
occurences are replaced accordingly.
UPDATE: after the menu button and split button change, the Open and Export buttons don’t get background anymore, so only the previous step is necessary.
Now we need to actually remove the background. For that we’ll add an icon, and it’s going to be just document-open-symbolic
.
So we’ll remove the label
and use-underline
and instead add an AdwButtonContent
child with the same lines together with icon-name
inside it:
<object class="AdwSplitButton" id="open_btn"> <property name="tooltip_text" translatable="yes">Open an icon</property> <property name="action_name">win.open</property> <child> <object class="AdwButtonContent"> <property name="icon_name">document-open-symbolic</property> <property name="label" translatable="yes">_Open</property> <property name="use_underline">True</property> </object> </child> </object>
Now, let’s look at the Export button. It needs an icon as well, but adwaita-icon-theme
doesn’t have anything fitting for it. So instead, let’s check out Icon Library (which doesn’t have a lot of edge cases itself).
While it doesn’t have icons for export either, it has a share
icon instead:
So that’s what we’ll use. We’ll need to bundle it in the app, and let’s rename it to export-symbolic
while we’re here. Now we can do the same thing as for the Open button:
<object class="GtkMenuButton" id="export_btn"> <property name="always_show_arrow">True</property> <child> <object class="AdwButtonContent"> <property name="icon_name">export-symbolic</property> <property name="label" translatable="yes">_Export</property> <property name="use_underline">True</property> </object> </child> </object>
So far so good. See the merge request for the complete changes.
Obfuscate
This app has only one header bar, but it can change its state depending on if there’s a file open:
After building with newer libadwaita, we see there’s quite a lot to update.
First, we need to add an icon to the Open button. It’s done exactly the same way as in App Icon Preview, so I won’t repeat it.
Instead, let’s look at the other buttons:
Here we have two groups of linked buttons — so .linked
is used both to group related actions together, and to separate the 2 groups.
So, first we need to unlink those buttons. Since it’s just removing the 2 GtkBox
widgets and instead putting the buttons directly into a header bar, I won’t go in details.
However, now we’ve lost the separation between the undo/redo group and the tools. So let’s add some spacing:
<object class="GtkSeparator"> <style> <class name="spacer"/> </style> </object>
And the end result is the following:
This information is also present in the libadwaita migration guide, and will be in the stylesheet documentation once all changes are finalized.
For now, happy hacking!
UPDATE (on the same day as published): Menu buttons with visible arrows and split buttons don’t get background by default anymore. The steps and examples have been updated accordingly.