Every Linux desktop app carries a bet inside it that most users never see, which is the toolkit the authors picked, because that choice decides whose desktop the app looks at home on and whose desktop it merely tolerates, and for twenty years the honest answer was that you could not win this bet: pick GTK and KDE users got a foreigner, pick Qt and GNOME users got a foreigner, and the only way out was to write the interface twice and watch the maintenance bill double.
Three of our apps, LinSight, LinSync, and Grexa, ship the same Qt 6 / Kirigami QML interface on KDE Plasma, GNOME, and XFCE with no fork in the codebase and no per-desktop UI branches, so this is the post about why that stopped being a gamble, what the stack actually buys us, and what it still costs, because “cross-desktop Linux is a solved problem” is the kind of sentence nobody bothers saying out loud anymore, which is exactly the tell that it’s true.
The fragmentation tax, and who used to pay it
The old shape of this problem will be familiar to anyone who shipped Linux software in the 2000s, because the options were all bad in distinct ways: you could write two native front ends and spend your budget keeping them in sync, you could reach for wxWidgets or a similar abstraction layer and get a UI that looked slightly wrong everywhere, or you could accept that your app was a citizen of one desktop and a tourist on the other, and most projects quietly chose door number three.
What changed is not that the desktops merged or that anyone won, it’s that Qt 6 grew up underneath the argument, because the QPA platform-theme plugins and qt6ct mean a Qt app now picks up the host desktop’s fonts, icons, and color scheme through whatever platform theme the distro ships, and Qt Quick Controls 2 styles follow the same path, so the toolkit-level translation work that used to be the app’s problem became the platform’s problem, and the platform solved it years ago while everyone was busy arguing about Wayland.
What Kirigami actually buys you
Kirigami sits on top of that as KDE’s convergent UI framework, and the word “convergent” undersells it, because the practical content is a set of QML primitives, page rows, navigation, form cards, action toolbars, that adapt between wide and narrow layouts on their own, so the same page definition renders as a spacious multi-column view on a monitor and a stacked drill-down on a small window without you writing a second layout.
The theme story is the other half, and it’s the half people assume is broken: Kirigami follows the active Qt Quick Controls style rather than forcing Breeze, so on Plasma you get full Breeze integration through qqc2-desktop-style, and on GNOME or XFCE you get whatever style the host platform theme provides, which means the app looks considered rather than transplanted, and the theming hooks we do use, like the attached Kirigami.Theme properties on the window or an individual page, are there for deliberate accents rather than damage control.
A real LinSight page is not much more than this, and that’s the point:
import QtQuick
import org.kde.kirigami as Kirigami
Kirigami.ScrollablePage {
title: i18n("GPUs")
actions: [
Kirigami.Action {
text: i18n("Refresh")
icon.name: "view-refresh"
onTriggered: gpuModel.poll()
}
]
Kirigami.CardsGridView {
model: gpuModel
delegate: GpuCard {}
}
}
Three apps, one UI stack
The reason this compounds for us is that the three apps share more than a toolkit, because each one is the same architectural sentence said in a different domain: a synchronous Rust core that does the actual work, a thin cxx-qt bridge that exposes Rust QObjects to QML, and a Kirigami interface that consumes those objects without knowing or caring that the logic underneath is Rust.
LinSight is a system-monitoring dashboard, so its UI is live tiles, sparklines, and charts fed by a daemon that samples the sensors on a fixed cadence and serves the snapshots to clients on request; LinSync is a diff and merge workbench, so its UI is synchronized side-by-side compare views and three-way merge pages; Grexa is a file-content search workbench, so its UI is tabs of results with match highlighting and a preview pane. Three very different apps, and yet a contributor who learns the page structure of one can navigate the QML of the other two without a map, which is the kind of consistency that never shows up in a feature list and shows up constantly in how fast fixes land.
The Grex lesson
The decision has a concrete origin story, and it’s the one that settled the argument internally, because Grex, our Windows file-search tool, is a XAML app, and when the “what about Linux” question came up we had the classic fork in the road: maintain a second native toolkit codebase for Linux, or port the app to a stack that could hold both, and Grexa exists because we chose the port.
The thing that made Qt 6 / Kirigami the answer rather than GTK was not aesthetics, it was that the Rust core was already synchronous and CLI-driven and the bridge story through cxx-qt let us keep every line of the search engine while replacing only the glass on top, and the day Grexa first rendered its results page on GNOME looking like it belonged there, the two-codebase future died and nobody mourned it.
The honest costs
None of this is free, and the bill arrives in predictable places. QML carries a JavaScript island in the middle of your otherwise strongly-typed app, and while you can keep it thin by pushing logic down into the Rust QObjects, the boundary is a discipline you have to enforce rather than a property you get by default. Packaging is the other recurring cost, because distros ship Kirigami 6 at different cadences, so our .deb depends on the host providing compatible Qt and Kirigami libraries, and tarball users occasionally meet QML2_IMPORT_PATH before they meet the app, which is a support burden that a fully bundled toolkit would not have. And the GNOME result is good rather than perfect: it follows the host style faithfully, but a pixel-counting GNOME native will still notice it’s not GTK, and we’ve decided that’s a tradeoff worth making rather than a bug worth chasing, because the alternative is writing the app twice.
The licensing angle, for anyone wondering, is a non-issue in our direction: Qt is LGPL, all three apps are GPL-3.0-only, and dynamic linking against the system Qt keeps everyone inside their obligations without a lawyer ever getting involved.
The boring answer is the tell
Twenty years ago the correct move was wxWidgets, or two codebases, or picking a desktop and apologizing to the other one, and each of those was a visible compromise that users could feel in the first five minutes. The modern equivalent is picking Qt 6 with Kirigami and then never thinking about it again, which is why nobody writes breathless posts about cross-desktop Linux anymore: the problem got solved quietly, underneath the discourse, and the apps that benefit from it just look like they belong wherever they happen to be running. We picked the stack because the Grex port forced the question, we kept it because three apps later it has never once made us regret it, and that’s the whole review.
