Bringing an existing GTK app
How an unmodified GTK 4 app looks on Luma, how to move it onto LumaUI one step at a time, and what must leave your stylesheet when you do.
You do not have to rewrite a GTK 4 app to run it on Luma. There are two levels, and you can move from the first to the second one piece at a time.
| Level | What you do | What people see |
|---|---|---|
| Runs on Luma | Nothing | Your app’s own GTK look, with standard integration: Luma’s accent color, the Figtree font, the Prairie icons and the person’s light or dark choice |
| Built on LumaUI | Use LumaUI’s window, components, commands and adaptive layouts | A Luma app, which can be reviewed for the Luma tier |
Running unchanged
A GTK app that does not use LumaUI is meant to keep its own look on Luma, with
standard integration only: the Luma accent color, the Figtree interface font,
the Prairie icon theme and dark mode. LumaUI does not restyle it, and nothing
is injected at run time: there is no GTK_THEME override, user stylesheet or
wrapper. Some of Luma’s styling still lives in the system GTK and libadwaita
rather than in LumaUI, so for now an unmodified app can show some Luma
styling, and those details can change between Luma releases. Do not design
around them.
Qt, Electron and games keep their own look too.
Moving onto LumaUI
LumaUI draws the whole visible Luma app: the frame, the title row, the identity and window controls, the islands and every component. Your app keeps its logic and its content; you replace the pieces around them.
- The window. Use
AppWindowin Python orLumaApplicationWindowin C and C++. Both draw the Luma frame and identity and handle presentation;AppWindowalso remembers the window’s size. A C app with an existingAdwApplicationWindowsubclass can callluma_application_window_frame_adopt()first, which gives it the Luma frame and keeps its content, actions and template children in place. - Islands. Put each area of work in an island (
Island,LumaIsland) inside the window body. See windows and islands. - Components. Replace hand-built pieces one at a time: the sidebar with
NavigationSidebar, empty panes withEmptyState, confirmations withDestructiveDialog, notices withToast, and fixed layouts with a split view and breakpoints. The Python reference and LumaUI for C list every component. - Commands. In Python, move your actions into a
CommandRegistry, so the identity menu, context menus, shortcuts and buttons share one list. See commands and menus.
Give the identity a real menu
The identity menu is your app’s best home for app-wide commands. In Python it is
built from the CommandRegistry you pass to AppWindow. In C, build a
GMenu whose items target your app. actions and pass it to
luma_application_window_set_menu_model().
What must leave your stylesheet
When your app moves onto LumaUI, remove everything LumaUI already owns:
- rules for
windowcontrols,headerbar,.luma-titlebaror.luma-identity-*; - radius, ring, shadow, margin or padding on
.luma-window-body,.luma-islandor.luma-pane; - a
border-radiuson the window, or abox-shadowon an island; - any
@define-color; - any literal hex or
rgba()color. Use the color tokens.