LumaUI principles
Why LumaUI exists, the semantic-API rule, who owns what between GTK, LumaUI and apps, and how the API is versioned.
LumaUI is Luma’s application toolkit: the standard way a Luma app is made. It is
the C library LumaUI-1 plus its Python package, luma_appkit. It is Luma’s
own layer on stock GTK 4 and libadwaita, not a fork of either. LumaUI’s
stylesheets load above libadwaita’s, so a LumaUI window looks the same on a
stock system as it does on Luma.
Why
- Instant upgrades. When the Luma design improves, every app built with LumaUI improves at once, with no app changes. The design lives in one place: LumaUI and its design tokens.
- Portability. Luma apps run on other Linux distributions inside the Luma runtime, with their dependencies bundled. LumaUI therefore finds its stylesheets and icons beside its own package first and in system paths last, and needs nothing that exists only on a Luma system.
- New primitives where none exist. GTK has no standard for a bar that grows
into an editor, a details pane that closes itself, a drawer, or a behaviour
and motion system. LumaUI designs the right primitive (
LayerHost,Toast,DestructiveDialog, the action center) instead of a workaround, even when that is more work.
The semantic-API rule
Apps say what; LumaUI owns how.
CountBadge(3, attention=True) # not: a label with a blue background
Toast.show(button, "Copied", kind="copied") # not: a toast you style and time yourself
DestructiveDialog.ask(button, title="Uninstall Kiln?", body=…, action="Uninstall", on_confirm=…)
StackedButtons([StackedButton("log-out", "Leave"), StackedButton("trash-2", "Delete", danger=True)])- No knobs that restyle a part: no colour, radius, size or duration arguments.
If a design needs a new variant, it becomes a named variant in LumaUI
(
small=True,kind="place") and a token. - App stylesheets never select a LumaUI part (
.lumaui-*). - Parts refuse misuse where it is cheap to detect, by raising an error: stacked buttons come in pairs or trios, a destructive dialog never says "Are you sure?" or "OK", a toast kind must be one LumaUI knows.
- Icons are Lucide names (
"share-2"), resolved to stablelumaui-<name>-symbolictheme names. Share isshare-2, Export isshare, Upload isupload.
Who owns what
| Layer | Owns | Never |
|---|---|---|
| GTK 4 (stock) | Rendering, input, accessibility, Wayland, text | Luma’s look |
| LumaUI | The window frame, title row, identity pill, window controls and islands; every component; tokens; motion; phone behaviour | App content |
| Apps | Their content (a document, a canvas, a list of songs) and which parts they compose | Restating or restyling a LumaUI part |
Everything a part looks like is a token: colours as @luma_* roles in the
generated token sheets, metrics and motion as --lumaui-* CSS custom
properties, and the same numbers for Python in luma_appkit.lumaui_tokens.
The token sheets reload when the appearance changes, so light, dark, Frost,
Glass and high contrast are a token change, not an app change.
API level, compatibility and deprecation
luma_appkit.LUMAUI_API_LEVELis an integer (2 today). It rises by one whenever a public name, a keyword argument or a toast kind is added. Apps that need a newer part check it (if LUMAUI_API_LEVEL >= 2:). The C library has its ownLUMA_UI_API_LEVEL.- Within a level nothing public is removed, renamed or changes meaning.
- A name that is going away is first deprecated: it keeps working, warns once, and is documented with its replacement. It is removed no sooner than one API level and one monthly release later.
- Looks and motion are not API: they change whenever the design does, which is the point.
- Deprecated now:
NavigationSidebar(compact=True): useNavigationSidebar(variant="files"), which is whatcompact=Truealready draws.
- Private names start with an underscore. Module internals (
action_toast,structure_layersand so on) are importable, but only names exported from the package root are covered by this promise.
Rules every part follows
Every public part has keyboard, accessibility, reduced-motion, high-contrast, long-text and phone-width behaviour (see behaviour) and tests.