Drawing GNOME App Mockups

I’ve written about designing GNOME apps at a high level before, but not about the actual process of drawing UI mockups the way we do on the GNOME design team. In this tutorial we’ll pick up the Read It Later example from previous tutorials again, and draw some mockups in Inkscape from scratch.

Before we start, let’s look at the sketches we’re going to base this on. I’ve re-drawn some of the sketches from my last app design blog post with just the parts we’ll need for this tutorial.

I recommend having a look at that other blog post before jumping into this one, as it will give you some background on the basic design patterns and show step by step how we got to this layout.

What’s in a Mockup?

After you’ve designed the basic structure of your app (e.g. as a sketch on paper) but before starting implementation, it’s good to check what your layout will look like with real UI elements.

Left: The initial mockup drawn in Inkscape, right: Screenshot of the real app

This doesn’t mean mockups need to recreate every gradient and highlight from the GTK stylesheet. Doing that would make mockups very hard to edit and keep in sync as the stylesheet evolves. However, things like spacing, border radii, button styling, etc. can be made to look very close to how they’ll look in the implemented version with relatively little effort. This is why on the GNOME design team we use a simplified style somewhere between a wireframe and a mockup, where sizes and metrics are mostly pixel-perfect, but UI visuals are not.

This level of fidelity is great for trying variations on layouts, placement of individual controls, different icon metaphors, etc. which are the most important things to validate before starting development. Once the implementation is in progress there’s usually additional rounds of iteration on different aspects of the design, but those don’t always require mockups as you can just iterate directly in code at that point.


In order to be able to follow along with this tutorial, you’ll need to install a few apps:

  • Inkscape: The vector drawing app we’ll be using to draw our mockup
  • Icon Library: A handy app for finding symbolic icons to use in mockups

Next, you need the GNOME mockup template. This is an SVG file with many of our most common UI patterns, which enables you to make mockups by copying and adapting these existing components, rather than having to draw every element yourself. You can download the template from GNOME Gitlab.

Finally, you need a recent version of Cantarell, GNOME’s interface font. It’s possible that while you may have a font with that name installed, it’s not the right version, because some distributions and Google Fonts are still on an old version (which has only 2 weights, rather than 5). You can download the new version here.

Inkscape Basics

If you’ve never used Inkscape before it might be good to do some more general beginner tutorials as a first step. I’ll assume familiarity with navigation and object manipulation primitives such as selection, moving/scaling/rotating, duplicating, manipulating z-index, grouping/ungrouping, and navigating through group hierarchies.

Nevertheless, here’s a quick overview of the features we’ll be using primarily.


Inkscape has a lot of features, but we only need a small subset for what we’re doing. Most interfaces are just nested rectangles, after all ;)

Toolbox (the toolbar on the left edge):

  • Selection/movement/scaling tool (S)
  • Rectangle tool (R)
  • Ellipse tool (E)
  • Text tool (T)
  • Color picker (D)

Properties Sidebar (configuration dialogs docked to the right side):

  • Fill & Stroke (Ctrl + Shift + F)
  • Align & Distribute (Ctrl + Shift + A)
  • Export (Ctrl + Shift + E)
  • Document Properties (Ctrl + Shift + D)

Snap Controls (the toolbar on the right edge): Inkscape has very fine-grained snapping controls to configure what should be snapped to when you move items on the canvas (e.g. path nodes, object center, path intersections). It’s a bit fiddly, but very useful for making sure things are aligned to the grid. The icon tooltips are your friends :)

When aligning things or working with the pixel grid it’s very helpful to have the page grid visible. It can be toggled with the # shortcut or in the View menu.

Advanced: Partial Rounded Corners

One sort of advanced thing I started doing recently is using path effects to get rounded corners only on specific corners of a rectangle. This is handy compared to having the rounding baked into the geometry, because it keeps the rounding flexible, so the object can be scaled without affecting the rounded corners.

The feature is quite hidden and looks very complex, but once you know where it is it’s not that scary. You can find it in Path > Path Effects... > + > Corners (Fillet/Chamfer).

You’ll find that the mockup templates use this path effect technique for e.g. rounded bottom corners on windows.

Color Palette

It’s not as important for mockups as it is for app icons, but still nice to have: The GNOME color palette. Inkscape 1.0+ includes it by default, so you can just choose it from the arrow menu on the right.

Otherwise you can also get it via the dedicated color palette app, or download the .gpl from Gitlab and put it in ~/.var/app/org.inkscape.Inkscape/config/inkscape/palettes for Flatpak Inkscape or ~/.config/inkscape/palettes if it’s on the host.

Page Setup

With that out of the way, let’s get started making our mockup! First, we need a basic page to start from. We can start from the empty-page.svg file, by opening that file and saving it under the name we’ll ultimately want for our mockup, e.g. read-later.svg.

GNOME mockups usually consist of one or more views laid out on a “page” of whatever size is needed to make the content fit. There’s usually a title and description at the top, plus additional captions to explain things about the individual screens where it’s needed (example).

The page dimensions can be adjusted in Document Properties (Ctrl + Shift + D). One useful shortcut here is Ctrl+Shift+R, which resizes to the bounding box of the current selection (Note: Only use this shortcut if the thing you’re resizing to is e.g. a rectangle you set up for that purpose. Resizing the page to things off the pixel grid will break it, because it moves the document origin).

Let’s do that, and tweak the title and description for our case:

Desktop View

Now that the page is set up we can add our first screen. This is the sketch we’re going to be drawing first:

As a first step, let’s bring in the window template with view switcher from pattern-templates.svg. Open that file in Inkscape, select the top leftmost screen, copy it, and paste it into your mockup file.

After roughly positioning the window, check the exact position using the numeric position entries in the bar at the top, and make sure both X and Y positions are full integers (if they’re not integers it means your mockup is off the pixel grid and will look blurry).

It’s worth pointing out that while in this case we’re just starting from the plain default templates, it’s often faster to start from an existing mockup for another app. The app-mockups repository on GNOME Gitlab is full of existing mockups to borrow elements from or use as a starting point for a new mockup. That said, depending on their age those mockups might be using outdated patterns, so it’s good to check when a particular mockup was last updated :)


Let’s start by adjusting the headerbar to what we have in our sketch. Conveniently, several of the buttons don’t need to change at all from the template. All we need to do here is move the search button to the left, delete the add button, and adjust the switcher.

As a general rule, spacing between elements is a multiple of 6 pixels (so 6, 12, 18, 24…). For example, there are 6px of padding around buttons inside a headerbar, and 6px between the individual buttons.

When moving/placing elements, always make sure that snapping to bounding box is active (topmost group of snapping controls). As with the placement of the window earlier, it’s good to verify that sizes and positions are even integers in the toolbar up top.

Next up: The view switcher. You can start by changing the three labels, and then re-centering the icon + label groups on their respective containers. The inactive items have invisible containers, so they’re a bit fiddly to select.

For the icons, you can fire up Icon Library and search for the following icons:

  • Unread: view-paged
  • Archive: drawer *
  • Favorites: star-outline-thick

* This icon isn’t included yet, but will be in a future version

You can paste icons directly into Inkscape using the “Copy to Clipboard” feature. Before pasting, navigate into the relevant icon group (each of the icons is grouped with an invisible 16px rectangle, because that’s the icon canvas size). When placing an icon, make sure to center it on the invisible canvas rectangle, and place them on the pixel grid. You may want to turn on outline mode (Ctrl+5 cycles through display modes) to deal with the invisible rectangles more easily.

Once the icons are replaced, change the label strings with the text tool, and move the icon+label blocks horizontally so they look centered within their containers. I personally just do this manually by eye rather than using the alignment tool, so I don’t lose the icon’s alignment to the pixel grid, but you can also align and then manually move it to the closest position on the pixel grid.

With this, the headerbar is complete now:


Let’s look at the content inside the window next. We can keep the basic structure of the listbox from the template, but obviously we want to change what’s in the list.

I’ve prepared some example content we can just copy and paste that into our mockup. Each article consists of three labels, one using the regular font size (10.5pt), and two using the small one (9pt). The metadata label also uses a lighter gray, to distinguish it from the body copy (you can get the color from the labels on the right side of the list in the template using the color picker tool).

The list from the template is grouped and has a clipping mask to cut off scrolling content at the bottom. Since our layout is different anyway we can remove the clipping by simply ungrouping (Ctrl + Shift + G). Next, we can delete all content except the first row, and change that to our first article.

In order to accommodate multiple pieces of content inline as part of the same label, a common pattern is to use middle dots (“·”) as dividers. Pro tip: The Typography app makes it easy to copy and paste typographic symbols like this one into your mockups.

After adding the articles in the first listbox, we need the “Show more” button at the bottom. For that we can just resize the list so it extends past the last article, and add a centered label on that area.

Some of the articles also have images associated with them (there are download URLs for the images in the example content file). Images that aren’t already square need to be clipped to a square shape for our layout. To do that in Inkscape

  • Pull in the image via drag and drop from the file manager (or paste it directly from a website via “Copy Image”)
  • Place a rectangle with the right size/proportions where we want the image to go in the layout. In this case, images should be 80px squares, with 12px spacing all around.
  • Move and scale the image to cover the rectangle on all sides
  • Select both the image and the rectangle, and do Object > Clip > Set. Note that the clipping object (the rectangle) needs to be above the target (the image).
Left: semi-transparent clipping rectangle overlaying image, right: clipped image

With the first list done, we can move it down a bit and add a title above it. You can re-use one of the titles from the list for that, and just horizontally align it with the list container. Spacing between title baseline and list should also be 12px, and above the title 18px to the edge of the view.

After that, our first list is complete:

For the second list we can duplicate our first list, move it down below, and just change the content:

In order to have it cut off nicely at the bottom we can bring back a clipping mask. Group the list, duplicate the window background rectangle, and use that as a clipping mask for the list. To make the last article a little more visible I’m also resizing the window background to be a little bit taller first.

For the primary menu we can pretty much just re-use the menu from the template (top left, outside the canvas). Copy over the template popover and button, and vertically align it so the button is at the same height as the one in the window.

Since we don’t need many actions other than the default ones, all we need to do here is change a few labels, add another divider, and put in the name of the app.

That leaves us with a bunch of whitespace in the popover though, so let’s move up the actions and then resize the popover by selecting the bottom nodes and moving them up using the arrow keys:

And with that, our menu looks pretty good:

That completes our desktop view, and we can move on to mobile.

Mobile View

Now that we have a desktop view, let’s do a mobile version of it. Let’s have another look at our sketch:

We can re-use all the elements from the desktop view here, but we need to resize and move around a few things.

As a starting point for the layout, let’s bring in the mobile template. I like to align the mobile headerbar with the desktop one vertically, so all the headerbar buttons have the same vertical position.

Headerbar & Navigation

On mobile sizes there’s not enough horizontal space to keep the view switcher in the headerbar, so instead there’s just a title. The view switcher is in a separate bar at the bottom.

For the headerbar we can duplicate the buttons from the desktop mockup and use them to replace the placeholder buttons on the mobile template.

For the switcher, start by deleting all items except one. Then center the remaining one, and resize the background rectangle to a bit less than a third of the width of the view.

After that you can duplicate the item twice, and move the two additional switcher items to the sides:

Now you can change the icons and strings, and delete the backgrounds on two of the items, and we have a complete mobile switcher:


Adapting the content is easy in this case. Duplicate the lists from the desktop mockup, left-align it with the lists from the template and delete the original lists.

Then we just need to resize the text boxes by moving the handle on the right of the baseline, truncate the text to two lines, move the images to 12px from the right edge, and center the “Show More” label.

Repeat the same thing for the second list, and we have a complete mobile layout!

Page Size & Export

If we zoom out and look at the whole thing together, we can see that this looks pretty much done now:

However, the canvas size is too big for the content we have. Open Document Properties (Ctrl + Shift + D) and change the size to about 1780×1100px.

This is what it looks like with the new page size:

Now that the mockup is ready, let’s export a PNG for easy sharing. Open the Export dialog (Ctrl+Shift+E), choose “Page” at the very top, make sure the DPI is 96, and set the file path. Then press Export, and try opening the PNG in an image viewer to check if there are any issues you missed.

On the design team we usually then push the finished mockup to a git repository, but that’s out of scope for this tutorial.


Congratulation, you made it all the way to the end! I hope this was useful, and you’ll go on to make many great mockups using what you learned :)

You can download the SVG for the mockup I created for this tutorial from GNOME Gitlab. It might come in handy if you have problems with a specific part of the tutorial and want to see how I did it. By the way: The inkscape-tutorial-resources repository contains snapshots of all templates and resources used in this tutorial, in case the original ones change in the future.

Obviously for a real mockup we’d do additional screens (e.g. the article page, various other menus, settings screens, and the like), but I think we’ve covered most of the basics with just this one screen. If you have questions feel free to comment or get in touch!

There is No “Linux” Platform (Part 2)

This is Part 2 of a series on what’s wrong with the free desktop app ecosystem and how we can fix it, based on the talk Jordan Petridis and I gave at LAS 2019 in Barcelona.

In Part 1 we looked at all the different elements making up a platform, and found that there is only one “complete” platform in the free software desktop world at the moment. This is because desktops control the developer platforms, while packaging and system integration is managed by separate communities, the distributions, for historical reasons. This additional layer of middlemen is a key reason why we don’t have real platforms.

Power to the Makers

The problems outlined in Part 1 are of course not new, and people have been working on solutions to them for a long time. Some of these solutions have really started to come together over the last few years, empowering the people making the software to distribute it directly to the people using it.

Thanks to the work of many amazing people in our community you can now develop an app in GNOME Builder, submit it to Flathub, get it reviewed, and have it available for people to install right away. Once it’s on there you can also update it on a schedule you control. No more waiting 6 months for the next distribution release!

Thanks to GNOME Builder’s Flatpak integration, “works on my machine” is largely a thing of the past now!

But though this is all very awesome, Flatpak is unfortunately not a complete solution to the platform conundrum discussed earlier in this series.

Flatpak is Not Enough

Flatpak does solve a number of the issues around app distribution very elegantly, because app developers do their own packaging, and control their release schedule. It’s also a unified package format that works across different host systems, and the Flatpak runtimes are clearly defined development targets to do QA against.

But that doesn’t magically fix all our problems. The two elephants in the room are

  1. The Host still matters: Flatpak only solves part of the issues with distro packaged apps
  2. Downstream drama: Flatpak does not address the conflicts between desktops and distributions

1. The Host Still Matters

Even with Flatpak there are still some unpredictable variables on the host system which affect app developers. On the technical side a number of things can go wrong, from an outdated Flatpak version (which can mean some Portals apps rely on may be missing), to missing/incompatible system APIs such as password storage, calendar, or address book.

These things can lead to applications not working properly, or at all. For example, this is why new versions of GNOME Contacts cannot access any contacts on Debian 10, why recent GNOME Calendar cannot access any calendars on Ubuntu 18.04, or why Fractal doesn’t remember your password across restarts on some non-GNOME environments.

There are also user-facing integration points where applications interface with the system. These include things like notifications, the application menu, search providers, the old systray, and the design patterns used in individual apps.

For example, when the system UI or design guidelines change, applications follow the platform and change their UI accordingly. This means if you install newer apps on an older system, there are going to be weird edge cases. For example, if you install new apps on Debian 10 you get a confusing mix of the old and new application menu paradigms because the design guidelines were changed with GNOME 3.32 (early 2019).

Before GNOME 3.32 applications had global menu items in the application menu in the Shell top bar, but now they are in the primary menu, inside the app window.

Flatpak also applies the host GTK stylesheet and icon set to apps. This means that if the host distribution overrides the system stylesheet, Flatpak will happily apply random, never-tested CSS to every app. Obviously this leads to lots of issues, ranging from ugly but relatively harmless glitches to real usability issues, such as illegible text on buttons. For more background on this particular issue, see this blog post.

Some of these issues could be fixed with more standardization, changes to Flatpak, or new portals. However, fundamentally, in order to be a real platform you need a clearly defined environment to develop and test for. Flatpak alone is not enough to achieve that.

Just like “write once, run everywhere” is always an illusion, it’s never going to be possible to completely split apps from the OS. You always need app developers to do some extra work to support different environments, and currently every distribution represents yet another extra environment to support.

2. Downstream Drama

Flatpak does not completely solve the issues app developers face in shipping their software, because these can not be isolated from the ones desktop developers face. In order to fix the app developer story we need real platforms. In order to get those we need to resolve the desktop/distribution dilemma.

The issues here roughly match the ones with traditional distribution packaging mentioned in Part 1, and can be grouped into three broad categories:

  • Structural issues inherent to having distributions and desktops be separate projects.
  • Fragmentation issues because we have multiple of everything so there’s duplication and/or bad abstraction layers.
  • Configuration issues, primarily around settings and other defaults, which have to be set at the distribution level but affect the user experience.

Structural Issues

One of the biggest structural issues is distribution release schedules not being aligned with the upstream one (or between different distributions). GNOME releases every 6 months, but distributions can take anywhere from a few weeks to several years to ship these releases.

This category also includes distributions overriding upstream decisions around system UX, as well as theming/branding issues, due to problematic downstream incentives. This means there is no clear platform visual identity developers can target.

For example, Ubuntu 18.04 (the current LTS) ships with GNOME 3.28 (from March 2018), includes significant changes to system UX and APIs (e.g. Unity-style dock, desktop icons, systray extension), and ships a branded stylesheet that breaks even in core applications.

Ubuntu 18.04 overrides the GTK system stylesheet, which results in the “Create” button on the new folder dialog in Files being invisible (among many many other issues, especially in third party apps).

Fragmentation Issues

Having multiple implementations of everything means we either need do tons of duplicate work, or try to abstract over the different implementations.

On one end of the spectrum there are OS installers: There is no GNOME installer, so every distribution builds their own. Unfortunately, most of these installers are not very good, and don’t integrate well with the rest of the desktop experience (e.g. they use different design patterns than the OS itself). This can be either due to a lack of resources (e.g. not every downstream has their own GNOME designers), or because different distributions have specific downstream goals and motivations (e.g. Fedora and RHEL share an installer, which introduces lots of complexity).

The famously awkward Fedora installer is a good example of why such core parts of the experience should be designed and developed upstream. Unfortunately this isn’t really feasible due to distribution fragmentation.

In other areas we have the opposite problem, because we’re trying to abstract over the fragmentation with a single component. For example, PackageKit is meant to abstract over different package formats, but in practice it only works for a handful of them, and even for those it’s often buggy. The PackageKit maintainers have officially given up on this approach.

Configuration Issues

This includes the default apps, the fonts shipped with the system by default, the terminal shell and prompt, and the UX around things like Plymouth. All of these things are usually configured at the distribution level and are therefore often not great, because these choices need to be made in concert with the rest of the platform UX.

Forging Platforms

Given the constraint of there being multiple different desktops projects and technology stacks (and the host still mattering), we’ll never have a single “Linux” or “FreeDesktop” platform. We could have one platform per desktop though.

From an app developer point of view, testing for GNOME, KDE, and elementary isn’t as nice as testing only for a single platform, but it’s not impossible. However, testing for Debian, Fedora, multiple Ubuntu releases, OpenSUSE, Arch, Endless, and dozens more is not and never will be feasible, even with Flatpak. Multiple different distributions, even ones that ship the same desktop environment, don’t add up to a platform. But exactly that is what we need, one way or another.

The question is, how do we get there?

The Nuclear Option

When we look at it from a Flatpak context, the solution seems obvious. Flatpak is solving the middleman problem for app developers by circumventing the distributions and providing a direct channel between developers and end users. What if we could do the same thing for the OS itself?

Of course the situation isn’t exactly the same, so what would that mean in practice?

With Flatpak runtimes there is no extra “distribution” abstraction layer. There are no Debian or Fedora runtimes, just GNOME and KDE, because those are the technology stacks app developers target.

These runtimes are already more or less full-fledged distributions which are controlled by the desktops, we’re just not using them as such. The Freedesktop SDK (which most runtimes are based on) is not based in any distro, but built directly from upstream sources using Buildstream as the build tool, and it already has most of the things you need to make a basic operating system.

There is an early-stage effort to make bootable nightly GNOME OS images for development/testing, built on top of the Freedesktop SDK. From there it wouldn’t be a huge leap to actually make an independent, consumer-facing platform OS for GNOME (and KDE, and other platforms).

However, though this is likely to become a very attractive solution in the future, there are a number of hurdles to be overcome:

  • An OS needs an installer, OS updates, a Plymouth theme, etc. All of these are being worked on for the nightly GNOME OS images, but are not quite there yet.
  • A “real” OS needs a dedicated group of people doing things like release management, security tracking, and QA. These are being done to some degree for the Flatpak runtimes, but a consumer OS would need more manpower.
  • It’s an OSTree-based immutable system, which means there is no traditional package management. Apps are installed via Flatpak, and server/developer workflows need to happen in containers. Though projects like Silverblue’s toolbox have come a long way over the past few years, there’s still work to be done before immutable OSes can painlessly replace systems with old-school package managers for all use cases.

It takes time to start a new operating system from scratch, especially when it’s using cutting-edge technology. So while things like GNOME OS could be amazing in the longer-term future, it’s likely going to take a few more years before this becomes a viable alternative.

Squaring the Circle

What could we do within the constraints of the technology, ecosystem, and communities we have today, then? If we can’t go around distributions with a platform OS, the only alternative is to meld the distributions into a meta platform OS.

Technically there’s nothing stopping a group of separate distributions from acting more or less like a unified platform OS together. It would require extraordinary discipline and compromise on all sides (admittedly not things our communities are usually known for), but given how important it is that we fix this problem, it’s at least worth thinking about.

To get an idea what this could look like in practice, let’s think through some of the specific issues mentioned earlier:

Release Schedule: This is probably among the thorniest issues since release cycles vary wildly in length and structure, and changing them is very difficult. It’s not unimaginable that at least some progress could be made here though. For example, GNOME could have long term support releases every 2-3 years for “stable” distributions like RHEL and Ubuntu LTS. Distributions could then agree to either be on the regular 6 month schedule, or the 2 year “LTS” schedule. Alternatively, all distributions could find a single compromise schedule that can work for everyone (e.g. maybe one release per year, like mobile operating systems do).

Theming/Branding: Some distributions want ways to customize the OS experience such that their system looks recognizably different from others. This is not necessarily a problem, as long as this is done using APIs that are supported and intended to be used in this way (which unfortunately is currently not happening in many cases).

Creating more branding opportunities which do not break APIs which apps rely upon (especially third party apps shipped via Flatpak), is certainly possible and there have been discussions in this direction (e.g. GTK accent colors). Whether distributions would limit themselves to these APIs once they exist is of course an open question, but at least there is a ongoing dialog about this.

System UX/API Changes: Some distributions make significant changes to the core system, which fragments the visual identity of the platform at best, and severely damages the app ecosystem at worst. This includes things like adding a permanent dock, icons on the desktop, re-enabling the systray, or a “dark mode” setting which just changes the system stylesheet from under apps.

The solution here is simple in theory: If you think a change to the system UX is needed to fix a specific problem, don’t just patch it downstream, but instead help to address the actual underlying issue (We already touched on this in Part 1). For example, if you find that new users are confused by the empty desktop at startup, don’t just ship an extension that completely breaks the structure of the shell. Bring the problem to the upstream designers and developers, figure out a solution together, and help implement it upstream.

In practice it’s not always that easy, but a lot can be done by simply adopting an upstream-first UX mindset. It can take a while to get used to, especially for companies with more, uh, “traditional” internal processes, but it’s definitely possible seeing as it’s working well for Red Hat and Purism, for example.

OS Installer: It may not be doable to have a single code base, but we could definitely share at least the design (and possibly some UI code) for the installers used across distributions. A cross-distribution initiative for nice, native GNOME installers across the major distributions would probably not be easy logistically, but is not unimaginable.

Software Installation & Updates: GNOME Software and PackageKit’s “abstract across distros” strategy has clearly failed, and we need a new approach here. For applications there is a relatively easy solution: Distributions stop packaging apps, and work together on a common repository of developer-submitted Flatpaks (e.g. something like Flathub). We’d need to work out how this common solution can accommodate various distribution policies around e.g. proprietary software, but this seems very doable and most of it already exists in Flathub.

The resources currently going into repackaging every app for every distribution could be pooled to review the apps submitted by developers to the common Flatpak repository.

Seeing as most distributions are not (yet) image-based like e.g. Silverblue or Endless, we would still also need a way to update the packages that make up the core system. For this there’s probably no way around backend duplication.

System Default Configuration: Making progress in this area is likely not too difficult comparatively. The main thing we’d need is better coordination between the various parties needed to synchronize these things better (which is of course easier said than done). Having some kind of common forum where the upstream design and release team, as well as people in charge of major distributions can discuss and standardize defaults across the entire ecosystem might work for that.

The Bottom Line

If we want a future with real platforms we can either go around the distributions or have them all work together (or potentially both), but one way or another we need to vertically integrate.

Neither path is straightforward or easy, and there’s a huge amount of work ahead either way. However, the first and most important step is acknowledging that this problem exists, and that we need to radically change our approach if we’re serious about building attractive app ecosystems.

The good news is that many people across different projects are already working towards enabling this future. We hope that you’ll join us.

Happy hacking :)

Designing an Icon for Your App

You’ve designed your app’s interface, and found the perfect name for it. But of course a great app also needs a great icon before you can release it to the world.

After the name, the app icon is the most important part of an app’s brand. The icon can help explain at a glance what the app does, and serves as an entry point to the rest of the experience. A high quality icon can make people want to use an app more, because it’s a stand-in for the quality of the entire app.

Think of the app icon like an album cover for your app. Yes, technically the music is the same even if you have a terrible cover, but a great cover can capture the spirit of the album and elevate the quality of the thing as a whole.


The first thing you need is a metaphor, i.e. some kind of physical object, symbol, or other visual artifact that symbolizes your application.

Finding a good metaphor is a fuzzy and sometimes difficult process, as it’s often hard to find a physical object many people will recognize as related to the domain of your app. There are no hard and fast rules for this, but ideally your icon metaphor should fall into one of these categories:

  • Physical objects directly related to what the app does (e.g. a speaker for Music)
  • Physical objects vaguely related to the app’s domain or an older analog version of it (e.g. a cassette tape for Podcasts)
  • Symbols related to the domain (e.g. the “play” triangle for Videos)
  • A simplified/stylized version of the app’s user interface (e.g. Peek)

The Music, Podcasts, Videos, and Peek icons

There are also anti-patterns for metaphors which should be avoided, if possible:

  • Heavily stylized symbols or logos (e.g. Fondo)
  • Completely random objects or symbols (e.g. Dino)
  • Mascots (e.g. GIMP)

These kinds of metaphors can work, but they make it harder to see at a glance what the app does, and don’t fit in as well with the rest of the system.

Let’s try an example: Remember the Reading List app we designed in a previous tutorial? Let’s make an icon for that!

My process for brainstorming metaphors is quite similar to the one I use to brainstorm names: I come up with a few ideas for physical objects, put them in a thesaurus to find more related ones, and repeat that until I have a list with at least a few viable candidates.

Let’s start with related physical objects:

  • Reading List
  • List
  • Book
  • Bookshelf
  • Article
  • Newspaper
  • Bookmark

How about related non-objects? Maybe we can find some more interesting objects that way:

  • Reading, the activity: Couch, reading light, tea/coffee, glasses
  • Later (as in, “read later”): Clock, timer
  • Collecting things: Folder, clipboard

Now that we have a few options, let’s see which ones are viable. Ideally, the metaphor you choose should have these attributes:

  • Somewhat specific to the app’s domain (e.g. a book is probably too generic in our case)
  • Recognizable at small sizes
  • Can be drawn in a simple, geometric style (this can save you a lot of work later on)

In this case, the most viable options are probably

  • Stack of books
  • Bookshelf
  • Bookmark
  • One of the above + a clock


Now that we have some metaphors, let’s try to sketch them to see if they make for good icons. I usually use pencil and paper for this, but you can also use a whiteboard, digital drawing tablet, or whatever else works for you to quickly visualize some concepts.

There are also official sketching templates with the base icon shapes available for download.

While sketching it’s good to think about the overall shape your icon will have. If it makes sense for your metaphor, try to make the icon not just a simple square or circle, but something more unique and interesting. If it doesn’t make sense in your case don’t force it though, there are other ways to make the icon visually unique and interesting, such as color and structure.

In this case, it looks like there are a number of viable concepts among our sketches, though nothing jumps out as the obvious best option. I kind of like the bookshelf, so let’s try going forward with that one.

Start from a Template

We now have a concept we like, so we can move to vector. This is where we can start using the shiny new icon design tools!

The first step is to install App Icon Preview from Flathub. We’ll also need a vector editor that works well with SVG, such as Inkscape.

Open App Icon Preview, and hit the “New App Icon” button on the welcome screen. We’re asked for the Reverse Domain Name Notation name of the app (e.g. org.mozilla.Firefox), and where to store the icon project file.

In most cases you’ll want to keep this file in your app’s git repository. Think of it as your icon’s source file, which the final icon assets are later exported from.

After that, the icon will open in preview mode in App Icon Preview. Now we open the same file in a vector drawing app, and edit it from there. Every time we save the source file, the preview will automatically update.

Now we have our icon source file open in both App Icon Preview and Inkscape. Icon Preview shows just the icon grid:

In Inkscape, open the Layers panel (Ctrl + Shift + L) and check out the layer structure. The icons layer is where the actual icon goes. The grid and baseplate layers contain the icon grid and the canvas respectively.

Behind everything else is the template layer, which doesn’t contain anything visible and is only needed so App Icon Preview can get the canvas size for preview and export. Don’t change, hide, rename, or delete this layer, because the icon might not show up in App Icon Preview anymore.

When previewing the icon in App Icon Preview you’ll want to hide the grid and baseplate layers (using the little eye icon next to the layers).

Make sure you have the GNOME HIG Colors palette in Inkscape. Inkscape 1.0 Beta has it by default, otherwise you can download it from the HIG App Icons repository and put it in ~/.var/app/org.inkscape.Inkscape/config/inkscape/palettes for Flatpak Inkscape or ~/.config/inkscape/palettes if it’s on the host. There’s also a color palette app, which you can get on Flathub.

Inkscape Tips

Once you’re familiar with the template, you can start drawing your icon idea as vector. If you’re using Inkscape and aren’t very familiar with the app yet, here’s a quick overview of the things you’ll likely need.

Toolbox (the toolbar on the left edge)

  • Selection/movement/scaling tool (S)
  • Rectangle tool (R)
  • Ellipse tool (E)

And if you’re doing something a little more advanced:

  • Bezier path drawing tool (B)
  • Path & node editor (N)
  • Gradient editor

Dialogs Sidebar (configuration dialogs docked to the right side)

  • Fill & Stroke (Ctrl + Shift + F)
  • Align & Distribute (Ctrl + Shift + A)
  • Layers (Ctrl + Shift + L)

Snap Controls (the toolbar on the right edge) Inkscape has very fine-grained snapping controls, where you can configure what should be snapped to when you move items on the canvas (e.g. path nodes, object center, path intersections). It’s a bit fiddly, but very useful for making sure things are aligned to the grid. The icon tooltips are your friends :)

Of course, teaching Inkscape is a bit out of scope for this guide. If you’re just getting started with it, I recommend doing a few beginner tutorials first to familiarize yourself with the basic workflows (especially around the tools listed here).

The GNOME Icon Style

Traditionally, GNOME app icons were very complex, with lots of photorealistic detail and many different sizes which had to be drawn separately. This changed when we revamped the style in 2018, with the explicit goal of making it easier to produce, and more approachable for third party icon designers.

The new style is very geometric, so in many cases you can draw an entire icon with just basic shapes.

These icons consist of rectangles (some with rounded corners) and circles exclusively


One important attribute of the style is the abstract perspective. Even though the style is simple and geometric, it’s not “flat”: It makes use of material, depth, and perspective, but in a way that is optimized for easy production as vector.

The perspective works by “folding” horizontal and vertical layers into one dimension, so you can see the object orthogonally from both the top and the front.

This results in a kind of “chin” an the bottom of the object, which is shaded darker than the top surface, since light comes evenly from the top/back.

The perspective is achieved by folding the top and front views together

In practice, this usually doesn’t have a huge impact, since it’s also suggested to make objects not too tall, when possible. A lot of icons are just a simple 2D shape with a small chin at the bottom.

That said, it can look very weird when you get the perspective wrong, e.g. by folding the layers from the top/back instead of the front, so it’s important to keep this in mind.

Material & Lighting

Icons can make use of skeuomorphic materials (e.g. wood, metal, or glass) if it’s needed for the physical metaphor, but outside of those special cases it’s recommended to keep things simple.

Examples of icons with realistic materials

Straight surfaces have flat colors (instead of e.g. slight vertical gradients), but curved surfaces can/should have gradients. The corners on the chin on rounded base shapes should have a highlight gradient.

The highlight on the corners of the chin is done with a horizontal gradient.

Shadows inside the icon should be avoided if possible, but can be used if necessary (e.g. for contrast reasons). Do not use drop shadows that affect the app outline though, because GTK renders such a shadow automatically.

Icon Grid & Standard Shapes

In order to make sure icons are somewhat similar in size, alignment, etc. we have a grid system.

The canvas is 128x128px (for legacy reasons), but you’re designing for 64×64, while also taking 32×32 into account where possible. In general, it’s good to make sure you’re putting as many lines as possible on grid lines, so they’re sharp even at 32. Testing in App Icon Preview helps a lot with this.

Each of the grid squares is 8×8 pixels. In order to be pixel-perfect at 64 and 32, orthogonal lines/edges should be on these grid lines (or fractions of them).

The icon grid also has some standard shapes for wide, tall, square, and circular icons, which can be used as a basis for the structure of the icon if it makes sense for the metaphor (e.g. if the object is more or less square, use the square standard shape).

Protip: Great Artists Steal Reuse

There are lots of apps with icons in the GNOME style out there, and they’re all free software. If there’s something you like about another app’s icon you can get the source from GNOME Gitlab or Github, look at how a certain object is drawn, or just take (parts of) other icons and adapt them to your needs.

This is especially useful for common objects needed in many icons, e.g. pencils, books, or screens. The icon template in App Icon Preview comes with a few of these common objects on the canvas, which can be a good starting point for new icons.

Draw, Preview, Repeat!

Armed with this knowledge about the style and tooling, we can finally jump in and start drawing! In this case I re-did the sketch at a slightly larger size to get a better feel for it:

Book shelf sketch

Now let’s try vectorizing it. Since the overall shape is a tall rectangle, we can start with the tall rectangle standard shape. If we change the color to brown, and make the chin at the bottom thicker (by resizing the top layer vertically), we have the basic frame for the shelf.

After that we can add the actual shelves, by simply adding two slightly darker brown rectangles (the back of the shelf), and two wide rectangles at the top of these (the bottom of the horizontal shelf).

Changing the color of the chin is a bit tricky, because it has a horizontal gradient. It requires selecting the bottom rectangle with the gradient tool, clicking each gradient stop manually and changing it to brown by clicking one of the colors in the color palette at the bottom edge of the window.

If you use e.g. Brown 3 from the palette for the top surface, you can use the Brown 4 or 5 for the chin, and Brown 2 or 3 for the highlights in the corners.

Let’s see what this looks like in Icon Preview now:

Getting there, right? Now let’s add some books. Lucky for us, book spines can also be drawn as rectangles, so this shouldn’t be too hard. We don’t want too much detail, because we’re designing for 64px first and foremost. Something like 10 books per row should work.

100% rectangles :)

If we want to get fancy we can also round the top of the spine on some of the books by adding an ellipse of the same color, but it’s not really needed at this size.

Finished full-color icon in App Icon Preview

Looking good! I think we’re done with the full-color icon.

If at this point in the process you feel like the concept or metaphor isn’t working out (for example because it doesn’t look interesting enough, or because it’s too complicated to work at small sizes) you can always go back a few steps and try vectorizing a different one of your sketches. The nice thing about the simplicity of this style is that you can do this without losing weeks of work, making iteration on concepts much more feasible.


Now that the full-color icon is done, we can start thinking about the symbolic icon for our app. Ideally this is a simplified, one-color version of the app icon, designed for a 16×16 px canvas. It’s used in notifications and some other places in the Shell where a colorful icon would not be appropriate.

I won’t go into too much detail on this here since drawing good symbolics is a big topic, and this post is too long already. I might expand on this in a future post, but for now here are a few quick tips:

  • Alignment to the pixel grid is very important here if you don’t want the icon to end up a blurry mess
  • Stick to the original metaphor if at all possible, go for something else if not
  • Test in App Icon Preview to make sure the icon is actually recognizable at 16px
  • If possible leave the outermost 1px empty on all sides
  • Most strokes should be 2px, but they can be 1px in some cases
  • Don’t overthink it for the first version. This icon is a secondary thing, and it’s relatively little effort to fix/redo it later :)

Our bookshelf example looks tricky at first glance, because we have all these tiny books, and only 16 pixels to work with. However, if we simplify it enough it’s not too hard to get something decent. We can just use a two tall and two wide rectangles to draw the shelf, and three smaller rectangles as books on each shelf:

This one is literally just rectangles :)

And that’s it! We have a real app icon now, with everything that entails. If you want to have a look at the source for the icon we made in this tutorial, you can download the SVG here. It includes the final icon and some of the intermediate steps.

Color and Symbolic in App Icon Preview


Now that we’re happy with the icon, we can press the “Export” button in App Icon Preview and save the final icon assets. The app will automatically optimize the SVGs for size, and if you have nightly builds of your app, you also get an automatically generated nightly icon without any extra work!Export Popover in App Icon Preview

Congratulations for making it all the way to the end! I hope you found this tutorial useful, and will go on to make great icons for your apps. If there’s anything you found unclear while following along, please let me know in the comments.

If you’re looking for more resources on the topic, check out the Icons and Artwork HIG page, the official guide on making GNOME App Icons, and the Icon Design Workflow wiki page.

Happy hacking :)

There is no “Linux” Platform (Part 1)

This is part 1 of a series of blog posts based on the talk Jordan Petridis and I gave at LAS 2019 in Barcelona. Read part 2 here.

In our community there is this idea that “Linux” is the third platform next to Windows and macOS. It’s closely connected to things like the “year of the Linux desktop”, and can be seen in the language around things like Flatpak, which bills itself as “The Future of Apps on Linux” and the Linux App Summit, which is “designed to accelerate the growth of the Linux application ecosystem”.

But what does that actually mean? What does a healthy app ecosystem look like? And why don’t we have one?

I think the core of the problem is actually the layer below that: Before we can have healthy ecosystems, we need healthy platforms to build them on.

What is a Platform?

The word “platform” is often used without a clear definition of what exactly that entails. If we look at other successful platforms there are a ton of different things enabling their success, which are easy to miss when you just look at the surface.

On the developer side you need an operating system developers can use to make apps. You also need a developer SDK and tooling which are integrated with the operating system. You need developer documentation, tutorials, etc. so people can learn how to develop for the platform. And of course once the apps are built there needs to be an app store to submit them to.

Developers can’t make great apps all by themselves, for that you also need designers. Designers need tools to mock up and prototype apps, platform UI patterns for things like layout and navigation, so every app doesn’t have to reinvent the wheel, and a visual design language so designers can make their app fit in with the rest of the system visually. You also need Human Interface Guidelines documenting all of the above, as well as tutorials and other educational resources to help people learn to design for the platform.

On the end user side you need a consumer OS with an integrated app store, where people can get the great apps developers make. The consumer OS can be the same as the developer OS, but doesn’t have to be (e.g. it isn’t for Android or iOS).  You also need a way for people to get help/support when they have problems with their system (whether that’s physical stores, a help website, or just easily google-able Stackoverflow questions).

That’s a lot of different things, but we can group them into four major pieces which are needed in order for something to be a real platform:

  • Operating System
  • Developer Platform
  • Design Language
  • App Store

So if we look at the free software world, where are the platforms?


Linux is a kernel, which can be used to build OSes, which can be used to build platforms. Some people (e.g. Google with Android) have done so, but a kernel by itself doesn’t have any of the four things outlined above, and therefore is not a platform.


What about “Desktop Linux”, which is what people usually mean when they say “Linux”? The problem is that this term doesn’t have a clear definition. You could take it to mean “FreeDesktop.org”, but that also doesn’t come close to being a platform. FreeDesktop is a set of standards that can be used to build platforms (and/or ensure some level of compatibility between different platforms). Endorsement of a single platform or set of technologies goes directly against FreeDesktop’s aims, and as such it should only be thought of as the common building blocks platforms might share.


What about distributions? Ubuntu is one of the most popular ones, and unlike others it has its own app store. It still isn’t a platform though, because it doesn’t have the most critical pieces: a developer SDK/technology stack, and a design language.

Other distributions are in a similar but worse position because they don’t have an app store.


GNOME is the most popular desktop stack, and it does have an SDK and design language. However, it only sort of has an app store (because GNOME people work on Flathub), and it doesn’t have an OS. Many distributions ship GNOME, but they are all different in various ways (more on this later), so they don’t provide a unified development target.


Despite being a relatively small project, elementary is attracting third party developers making apps specifically for their platform

Interestingly, the only project which currently has all the pieces is elementary. It has an OS, an SDK, a HIG, and an app store to submit apps to. The OS is largely Ubuntu and the technology stack largely GNOME, but it develops its own desktop and apps on top of that, and does the integration work to make it into a complete consumer product.

This begs the question, why is elementary the only one?

The Means of Distribution

The reasons for this are largely historical. In the early days, free software desktops were a bunch of independently developed components. They were not necessarily designed for each other, or well integrated. This meant in order to have a usable system, someone needed to curate these components and assemble them into an operating system: The first distributions were born.

Over the last decades this landscape has changed drastically, however. While GNOME 1 was a set of loosely coupled components, GNOME 2 was already much more cohesive and GNOME 3 is now essentially an integrated product. The shell, core apps, and underlying technologies are all designed with each other in mind, and provide a complete OS experience.

Desktops like GNOME have expanded their scope to cover most of the responsibilities of platforms, and are in effect platforms now, minus the OS part. They have a very clear vision of how the system should work, and app developers target them directly.

The elementary project has taken this development to its logical end point, and made its own vertically integrated OS and app store. This is why it’s the only “real” platform in the free software space at the moment.

GNOME has a relatively vibrant ecosystem of nice third party apps now, despite not being a complete platform (yet). This gives us a glimpse of the potential of this ecosystem.

Distributions, on the other hand, have not really changed since the 90s. They still do integration work on desktop components, package system and applications, set defaults, and make UX decisions. They still operate as if they’re making a product from independent components, even though the actual product work is happening at the desktop layer now.

This disconnect has led to tensions in many areas, which affect both the quality of the system user experience, and the health of the app ecosystem.

What’s interesting about this situation is that desktop developers are now in the same situation app developers have always been in. Unlike desktops, apps have always been complete products. Because of this they have always suffered from the fragmentation and disconnect between developers and users introduced by distribution packaging.

Grievances with the distribution model, which affect both app and desktop developers, include:

  • Release schedule: Developers don’t have control over the pace at which people get updates to their software. For apps this can mean people still get old versions of software with issues that were fixed upstream years ago. For desktops it’s even worse, because it means app developers don’t know what version of the platform to target, especially since this can vary wildly (some distributions release every 6 months, others every 2+ years).
  • Packaging errors: Distribution packaging is prone to errors because individual packagers are overloaded (often maintaining dozens or hundreds of packages), and don’t know the software as well as the developers.
  • Overriding upstream decisions: When distributions disagree with upstream decisions, they sometimes keep old version of software, or apply downstream patches that override the author’s intentions. This is very frustrating if you’re an app developer, because users never experience your app as you intended it to be. However, similar to the release schedule issue, it’s even worse when it happens to the core system, because it fragments the platform for app developers.
  • Distro Theming: App developers test with the platform stylesheet and icons, so when distributions change these it can break applications in highly visible ways (invisible widgets, unreadable text, wrong icon metaphors). This is especially bad for third party apps, which get little or no testing from the downstream stylesheet developers. This blog post explains the issue in more detail.

The Wrong Incentives

The reason for a lot of these issues is the incentives on the distribution side. Distributions are shipping software directly to end users, so it’s very tempting to solve any issues they find downstream and just ship them directly. But because the distributions don’t actually develop the software this leads to a number of other problems:

  • Perpetual rebasing: Any change that isn’t upstreamed needs to be rebased on every future version of the upstream software.
  • Incoherent user experience: Downstream solutions to UX problems are often simplistic and don’t fix the entire issue, because they don’t have the development resources for a proper fix. This leads to awkward half-redesigns, which aren’t as polished or thought-through as the original design.
  • Ecosystem fragmentation: Every downstream change adds yet another variable app developers need to test for. The more distributions do it, the worse it gets.

The Endless OS shell is a great example of this. They started out with vanilla GNOME Shell, but then added ever more downstream patches in order to address issues found in in-house usability tests. This means that they end up having to do huge rebases every release, which is a lot of work. At the same time, the issues that prompted the changes do not get fixed upstream (Endless have recently changed their strategy and are working upstream much more now, so hopefully this will get better in the future).

This situation is clearly bad for everyone involved: Distributions spend a ton of resources rebasing their patches forever, app developers don’t have a clear target, and end users get a sub-par experience.

So, what could we do to improve this? We’ll discuss that in part 2 of this series :)