A UX improvement proposal for GrapheneOS
What GrapheneOS could look like
without forking itself.
Android lets you theme exactly ten things, and the list stops at colour, type and icons. Everything past it is a code fork that gets re-ported on every release. So I designed inside the limit, and priced the parts that fall outside it.
I use GrapheneOS. This is not a complaint about it. It started with a narrower question than the one people usually ask about open source interfaces. Not why does this look plain, but how much of an Android interface can actually be changed, and what does each kind of change cost the people maintaining it.
The answer turned out to be more interesting than the redesign.
Android lets you theme exactly ten things
Android ships a theming system. It is called ThemeOverlayApplier, it lives in SystemUI, and the categories it will actually apply are held in one array called THEME_CATEGORIES. There are ten entries in it. The class defines an eleventh constant, theme_style, and deliberately leaves it out of that array.
android.theme.customization.system_palette android.theme.customization.accent_color android.theme.customization.dynamic_color android.theme.customization.font android.theme.customization.adaptive_icon_shape android.theme.customization.icon_pack.android android.theme.customization.icon_pack.systemui android.theme.customization.icon_pack.settings android.theme.customization.icon_pack.launcher android.theme.customization.icon_pack.themepicker no category for type scale no category for spacing no category for elevation no category for motion component geometry only as far as two dialog corner radii
The one that catches people out is adaptive_icon_shape. It sounds like it governs the shape language of the interface. What the AOSP shape overlays actually set is four resources: config_icon_mask, config_useRoundIcon, and the corner radius of a dialog and a bottom sheet dialog. The mask is read by AdaptiveIconDrawable in the framework, so it applies wherever an adaptive icon is drawn rather than only on the launcher.
That is the entire shape surface. An icon mask and two dialog radii. Cards, rows, sheets, controls and every other piece of geometry in the system are outside it.
So the supported theming surface of Android is four things. Colour palette, typeface, icon shape, and icon packs. Anything past that is not theming, it is a code fork.
Upstream is shrinking that surface rather than growing it. The frameworks/base/packages/overlays directory held 44 packages on Android 11. On main, as of September 2026, it holds 16, with every accent colour, icon pack and icon shape family deleted.
Why that costs more here than anywhere else
GrapheneOS maintains its changes as a clean patch set on top of the latest stable AOSP release, and re-ports that patch set on every yearly and every quarterly platform release. Their own branch list is the cost made visible: 13, 14, 15-qpr2, 16, 16-qpr1, 16-qpr2, 17.
They fork frameworks/base, Launcher3, Settings, ThemePicker and WallpaperPicker2. SystemUI itself lives inside frameworks/base rather than in its own repository, so every system interface change they make rides the same patch set that gets rebased each cycle.
Every change we make to Android Open Source Project repositories is maintained as a clean patch set on top of the latest stable release of AOSP. […] Ease of porting helps us when we port to new quarterly and yearly major releases of AOSP.GrapheneOS, on its own method, grapheneos.org/faq
That sentence is the constraint any interface proposal has to survive. It is not a taste argument, it is a porting speed argument, and porting speed is a security property when the job is shipping patches fast.
So I designed inside the limit
The result is called Bone. It is a light interface, which is the one direction this category does not go. Security and privacy tooling reaches for near black by convention, until a dark interface reads as seriousness on its own. Bone does not take that shortcut, and the choice is the argument rather than a side effect of it.
It takes three positions that were unoccupied. A fixed palette that never comes from the wallpaper, so a personalisation setting can never recolour a trust signal. One typeface across every register, from a 100 point clock down to a 10 point label, with monospace used only where it is functionally correct, on hashes, hostnames and timestamps. And light, deliberately, because warm paper is the one ground nobody else in this category has taken, and it costs nothing, since AOSP already ships both themes.
It also refuses two ideas that were more original than anything it does. A closed set of icon masks encoding attestation state would be genuinely unoccupied territory, and it means patching the adaptive icon pipeline in frameworks/base permanently. A reserved motion register, critically damped and non interruptible, for irreversible actions, would be the other open lane, and motion has no overlay category at all, which makes it the most expensive thing here. Both refused on cost.
What one notification says, and what it leaves out
GrapheneOS ships a notification string of its own, notif_memtag_crash_title, reading Memory tagging detected an error in %1$s. It is precise and it is correct. It sits in core/res/res/values/string_ext.xml in the project’s frameworks/base fork, alongside the other exploit protection notification strings.
What is missing is the sentence in between. Not what the feature is called, but what it just did, and whether the thing it caught was dangerous. A tombstone answers that for someone who can already read one.
Nothing about that needs a fork. The event, the trigger and the delivery are unchanged. What changes is what the notification says, and that lives in a string resource the project already maintains.
The same components, in the dark
Every fill, stroke and text colour on the screens and in the component library is bound to a variable rather than typed in. Switching the collection to its dark mode re-themes all 25 screens with nothing redrawn, which is the same mechanism AOSP uses and the reason a light default costs nothing.
What it would take to ship
Three layers, three very different prices.
The palette and the typeface overlay package
Ships as Runtime Resource Overlay packages against system_palette, accent_color and font. An overlay package is not a patch. It never edits a file upstream also edits, so it produces no merge conflicts, only a rebuild against the new platform.
Two things keep this from being free. A theming overlay has to be installed and signed on the system image, so it rides the build configuration rather than being something a user drops in. And holding the palette fixed means the wallpaper driven colour path stops running, which is a SystemUI decision rather than a resource. Light as a default is genuinely a config change, since both themes already ship.
What a control says, and where it sits resource work
Changing what a control says is string resource work. Moving one into a different group is preference XML plus the controller that registers it, a small amount of Kotlin rather than none. Neither goes near the framework. The project already maintains its own strings_ext.xml in Settings and string_ext.xml in the framework, so this layer uses files it has already chosen to own.
Card geometry, the capability strip, attestation in smartspace platform patch
None of these are reachable by any overlay category. They are a SystemUI and Settings patch, re-ported every release, exactly like the lock screen patches the project already carries.
One cost sits outside all three layers. The wording here is English, and longer than the strings it replaces. Every card is drawn at one width and one text size. German runs longer again, and Android has to survive a 200 percent font scale and right to left layouts. Translating and reflowing all of it is real work this proposal does not price.
The third layer is the part I would drop first. It is what makes Bone look designed rather than merely relabelled, and it is what would cost a small team porting weeks it does not have. If any of this were ever useful, the useful part is the second one. Naming what a control protects against, and what stops working if you turn it off, is nearly free, and it is the cheapest way to make a protection legible to someone who has not read the source.
Reasons to say no
Capacity is the real constraint rather than willingness. Every hour spent re-porting a capability strip is an hour not spent on a hardening patch.
Novelty has a genuine cost in a security product, because users arriving from stock Android already know how to operate the current interface, and an unfamiliar one is a support burden before it is an improvement.
Some of these screens also ask for something the platform does not keep. The network view lists the hosts an app actually reached, and Android records that nowhere Settings can read. That is instrumentation rather than layout, and it carries its own storage and battery cost. An updater that remembers why its last check failed is the same kind of ask.
None of it is AOSP native and I am not presenting it as though it were. It belongs in the nice to have column. It is also a nice to have that fits a hardened system better than it fits a stock one, because knowing which hosts an app reached is worth collecting on a device whose whole argument is what it stops an app from doing.
And a maintainer could accept every piece of evidence here and still reject the design, which would be a coherent position rather than a failure of the argument. A proposal that cannot survive being turned down was never an argument in the first place.
Why now
In March 2026 GrapheneOS announced a long term, non exclusive partnership with Motorola Mobility. The first devices are expected in 2027, flagships first. A hardware partner means the operating system will reach people who have never configured a threat model and never will. That is the moment when interface comprehension stops being a philosophical question and becomes a commercial one.
The project made a deliberate engineering decision not to fork the interface. This takes that decision seriously enough to price it rather than wish it away.
Not affiliated with GrapheneOS or Motorola.
The whole file is open.
The technical foundation, the ten before and after pairs, all 25 screens, the component library and the implementation breakdown.