# Designing in strict monochrome > Why instrument apps stay black and white, and how we fight SwiftUI blue and Material purple without sacrificing readability. 2026-03-22 · 18 min read · design · by ntan (ntan) for uranashel Canonical HTML: https://uranashel.com/blog/monochrome-ui.html --- uranashel apps get read in bad places. 11 PM in a parking garage, one hand still on the steering wheel. At 6 AM the same phone is a sleep timer being poked by someone half-asleep. On a factory floor the operator wears gloves and has no patience for decorative chrome. Each of those scenes contains exactly one number the user came for, and every other pixel is either helping them find it or standing in the road. Color usually stands in the road. Every hue competes for attention with the reading you opened the app to see, so we treat color as cognitive load and spend it the way we spend battery. The rule is strict: **black, white, and gray only**, plus 3 semantic chromatic exceptions. Green marks success and acceptable states. Yellow marks caution and degraded confidence. Red marks danger and over-limit conditions. That is the entire palette across all 5 apps. No brand purple, no SwiftUI default blue tint. And no Material You dynamic accent that restyles itself from your wallpaper; the wallpaper does not get a vote in an instrument. ## The reason is instrument design The constraint is not aesthetic minimalism for Instagram screenshots. It is instrument design. A decent multimeter has one visual accent, the needle, and everything else on it is matte. Sonarish displays LAeq trending toward a legal noise threshold, and that decibel number should command the whole screen; a gradient hero banner around it would be decoration stealing glances from data. LAeq is an [A-weighted](https://uranashel.com/blog/a-weighting-decibels.html) equivalent level, so the digits already encode a model of human hearing. Dark is the default theme. Partly that is the garage at 11 PM. Partly it is Estua: full dark adaptation of the eye takes 20–30 min to build, and one white screen at 2 AM erases most of it, which means a sleep app that blinds you while you set the timer has failed at its main job. Light theme exists as a pure inversion for daylight, sharing identical structure with dark and differing only in luminance. ## Color that carries data The 3 exceptions work hard. Wheria's confidence badge uses green, yellow, and red, tied directly to the estimation standard deviation coming out of the [fusion filter](https://uranashel.com/blog/kalman-filter-parking.html). Yellow there is a statistical claim about position uncertainty. A decorative mood indicator would not survive review in this studio. When covariance grows past the first gate the badge steps from green to yellow; past the second gate it goes red and the app tells you plainly to trust the arrow less. Sonarish applies the same mapping to noise dose. A shift log trending toward the NIOSH 8-hour limit renders the accumulating percentage in plain gray while the projection stays under the limit, then red once it crosses. Green never appears on that screen. A dose at 79% of the limit is merely tolerable, and gray covers tolerable. Phyzix keeps the discipline inside its graphs. Accelerometer, gyroscope, and magnetometer traces are distinguished by line weight and dashing. An early build tried rainbow colormaps and they died the first time a teacher projected a lesson, because on a washed-out classroom projector nobody could tell which axis was which. Line style survives projection, grayscale printing, and every form of color blindness at once. We also rejected accent-color pickers entirely. A theming menu lets every user make their instrument look different from every screenshot in the manual, and then support tickets arrive with images we cannot match to our own builds. One palette. Two themes. That discussion was short. ## Grays, tokens, one lint rule Between black and white sit 5 named grays, each defined by role: primary text, secondary text, hairline borders, card surfaces, disabled states. No line of code refers to a raw hex value; every color routes through one Theme file per platform, and a CI grep fails the build when a color literal appears anywhere else. The rule sounds petty. It is also the only reason the palette is still monochrome after 5 apps and 2 platforms' worth of pull requests. Pure black backgrounds turned out to be a mistake on OLED. On 2 of our test phones, light text scrolling over #000 left a brief smear, because OLED pixels switching on from fully off respond slower than pixels already lit. Lifting the dark background to a near-black gray removed the ghosting. Bench note: 2 OLED devices, 1 evening, a scrolling test page, slow-motion footage at 240 fps. Depth needed a rethink as well. Material Design expresses elevation with drop shadows and SwiftUI leans on translucent layering, and both go muddy under strict monochrome. We use 1 px hairline borders plus a 1-step surface lift: a card in dark theme sits on a gray one step lighter than the background, and in light theme one step darker. ## SwiftUI fights back Enforcing monochrome on iOS meant fighting the framework's defaults at every layer. The root view gets `.tint(Theme.primaryText)`, which kills system blue on buttons, links, and toggles. Delete that single modifier and every interactive element reverts to Apple's accent by the next build. The text-insertion cursor held out longest; tint only reaches it on recent iOS versions, so for 2 minor releases we shipped a monochrome app with 1 blinking blue survivor. Fonts fought harder. Navigation titles ignore UINavigationBar appearance font settings on iOS 16 and later, so we inject Nunito through a principal toolbar item using our custom nunitoTitle modifier. Toolbar buttons need explicit font assignment or they silently revert to SF Pro, and silent is the expensive part: nothing logs, nothing crashes, the title is simply wrong 3 screens deep. Form and List screens default to 17 pt system font while the application body runs 15 pt Nunito, so every Settings, AddEditVehicle, and BrandPicker screen sets its subheadline font explicitly. Miss one screen and its rows look like they belong to a different application. ## Compose mostly cooperates Android was gentler. Compose routes nearly everything through MaterialTheme, and our AppTypography maps all text roles to Nunito in one declaration. Third-party composables occasionally leak Roboto, so screens hosting them get a defensive ProvideTextStyle wrapper. Some surfaces we cannot own at all: TimePickerDialog, Toast messages, and system permission sheets stay in the system font because the operating system draws those popups itself. We stopped arguing with it. Corner radius is 12 px everywhere: cards, buttons, bottom sheets. Not 8, not 16. Across 5 applications the sameness is the point, because users subconsciously trust instruments that look like they came from the same laboratory. A radius that drifts between apps is a small tell that nobody is checking details, and a user who notices one loose detail starts doubting the numbers next. ## Light in a garage, measured Contrast targets came from the field before they came from any spec sheet. We walked our usual test garage with a lux meter, 1 phone running a Phyzix build cross-checked against a cheap standalone meter, 1 evening plus 1 noon visit. Roughly 38 lux on the B2 ramp, 12 lux near pillar E9, and 31,000 lux in the open-air lot at 1 PM. The same screen has to survive a bit over 3 orders of magnitude of ambient light. So primary text targets WCAG AA contrast in both themes, and secondary text has to stay readable without squinting on a dim garage ramp and in a bright outdoor parking lot alike. In the dark theme, primary text sits near 15:1 against the near-black surface and secondary text near 7:1. The light theme lands on the same ratios by construction, because it is the same token set with luminance inverted. ## State never rides on color alone Roughly 8% of male users have some form of color vision deficiency. A yellow warning invisible to a deuteranope is a lying instrument, so we never rely on color alone to convey state. Sonarish pairs a red threshold breach with haptic feedback and an icon change, which reaches you even with the phone face-down in a shirt pocket. Wheria's compass confidence indicator carries a text label such as "Low compass trust" alongside the yellow chip. The label costs a little width. The lie would cost more. ## What we refuse to ship Gradient hero sections. Glassmorphism overlays, neon augmented-reality decorations, anything that treats the camera feed as a canvas. Wheria's AR direction arrow renders white with a thin black stroke over the feed, a pairing that stays readable on concrete, asphalt, and the painted lines of open-air lots, which is close to every background a parked car generates. The feed itself stays unmodified. Heat-map tints and color-coded distance rings would turn a navigation aid into a video game. The cost of all this restraint is real and we accept it. On a store page our screenshots sit quietly next to neon competitors, and quiet tends to lose the first-glance comparison. What it wins is the 400th session, when the user stops seeing the interface and just reads the number. Estua's sleep timer and scene picker follow the same visual vocabulary as Wheria's Find screen and Sonarish's meter readout, so a user switching between uranashel apps at 2 AM never has to relearn where the important number lives on screen. That consistency only exists because no single app is allowed personality at the expense of the set. Typography carries the hierarchy that color would otherwise carry; the details live in [Nunito on every pixel](https://uranashel.com/blog/nunito-on-every-pixel.html), and the release discipline that keeps the iOS and Android versions visually aligned is covered in [shipping iOS and Android at the same time](https://uranashel.com/blog/cross-platform-two-codebases.html). Underneath sits the same philosophy that drives our sensor work: an interface must stay honest even when honesty looks plain, the thread that also runs through [indoor navigation when GPS dies](https://uranashel.com/blog/building-wheria-indoor.html). Monochrome belongs to instrument integrity as one more component, next to the filter tuning and the calibration routine, and it gets maintained with the same seriousness. --- uranashel · [Home](https://uranashel.com/) · [Apps](https://uranashel.com/apps.html) · [Lab](https://uranashel.com/lab.html) · [About](https://uranashel.com/about.html) · [Blog](https://uranashel.com/blog/) · [Developers](https://uranashel.com/developers/) · [API docs](https://uranashel.com/docs/) · [Privacy](https://uranashel.com/privacy.html) Machine-readable: [llms.txt](https://uranashel.com/llms.txt) · [sitemap.xml](https://uranashel.com/sitemap.xml) · [openapi.json](https://uranashel.com/openapi.json) · [API](https://uranashel.com/api/v1/)