Appopolis

Your UI doesn't need another colour pass. It needs the arithmetic done first.

26 August 2026designiosuicraft

Roundtrip is a packing app. One screen shows your bags on a shelf, and we wanted each bag to show little icons of the things inside it — a rolled shirt, a shoe, a wash bag. You would glance at the shelf and know what was where.

It looked wrong.

Not broken. Not ugly, exactly. Just muddy and cheap in a way none of us could name. So we did what everyone does: we changed the colours. Then we changed the spacing. Then we changed the colours again, in the other direction.

Three rounds. It still looked wrong.

Then somebody finally did the arithmetic.

bags on shelfbag widthinteriorcellicon
1310pt236pt47pt29pt
2147pt112pt22pt14pt
393pt70pt24pt15pt
554pt40pt20pt12pt

Apple's documented minimum for anything you tap is 44×44pt. For a glyph to stay readable we work to about 16pt — that floor is ours, not Apple's, but it has never once been too generous.

Look at the last column. With a single bag on screen we had 29pt and it was fine. At two bags: 14pt. At five: 12pt.

Past one bag, every single configuration was already below the floor. The screen was never going to work. There was no colour that fixed it, because colour was never the problem — we were asking a reader to identify a shoe from twelve points of shoe, and no palette in the world makes twelve points into sixteen.

The rule this bought us

Work backwards from the floor, through the layout arithmetic, before you draw anything.

Start at 16pt for the glyph. Multiply up through the padding, the cell, the interior inset, the container. If the number you land on is wider than the screen, the layout is wrong — and you know that in about ninety seconds, before a single asset exists.

If the numbers don't reach the floor, stop tuning and change the layout. That is the whole lesson, and the expensive part is that "stop tuning" is exactly what a designer's instincts will not let them do. A screen that looks wrong feels like a taste problem. Taste problems invite another pass. Another pass feels like progress. It is possible to spend a very long time this way.

Turning the suitcase on its side bought 2.6x

Here is the part that was genuinely fun.

A suitcase is wider than it is tall. So is a phone screen. We had been drawing the case upright, standing on its wheels like it does in an airport — which looks right, and spends the screen's entire width on nothing.

Lay the case open, flat, the way it actually sits when you pack it, and the same vertical budget bought 2.6× the interior area and icons at around 30pt. Comfortably over the floor. Same screen, same content, one decision about orientation.

Ask what shape the subject actually is, then ask whether you are drawing it in that shape or in the shape you have seen in photographs. Orientation is not decoration. It is a layout lever, and it is usually the biggest one available.

Variety of shape does what colour cannot

The other thing three rounds of recolouring never fixed: a grid of identical rounded chips reads as a spreadsheet, no matter how beautifully it is tinted. Tint a spreadsheet and you get a tinted spreadsheet.

What fixed it, in one pass, was drawing each thing as its own form. A rolled shirt as a roll. A laptop as a slab. A wash bag as a zipped pouch. A shoe with an actual sole.

The unexpected bonus: once the shapes carried the difference, the palette was free to get quieter. We had been turning the colour up to do a job colour is bad at. Give the work to form, and the colour can relax — which is how you end up with something calm rather than something loud.

If your interface needs louder colours to be legible, the colours are covering for a shape problem.

Carve the space, don't drop things into it

Filling the open case took four attempts. Every failed one used a gravity packer — items fell to the bottom of a box and stacked up. It always looked dumped, never packed, and again we spent a while assuming it was a shading problem.

What works is the opposite instinct. Don't drop things in; carve the space up. Split the list in two as near the halfway weight as you can, cut the rectangle across its longer side, and recurse.

Cutting the long way is the whole trick — a plain left-to-right slice turns the tail of a list into slivers. Cutting the long side keeps every piece a sane shape. It fills 100% of the container, so nothing floats in space, and it costs about fifteen lines.

Two details that turned "filled" into "packed":

Order the list by where things belong. Keep the split order-preserving and the first half always takes the bottom, so putting shoes and laptops first in the list is literally what puts them under the clothes. That single ordering is the difference between arranged and dumped. Each kind of thing has a home it returns to every trip.

Butt things together. Padding each item individually is what made the old case look sparse. Subtract a fixed gutter inside the carve instead — 3pt between compartments, 1.5pt between things sharing one. A hairline seam reads as packed; generous padding reads as a shop display.

The question worth stealing: which number is the user watching?

This one is philosophy, and it is the best thing on this page.

We filled the case against its capacity — its litres. Perfectly logical. It also meant that a sensibly packed bag was still two-thirds empty when you ticked off the last item. The payoff moment, the one where you are finally done packing, landed on a picture of a half-empty case.

We filled it against the list instead. Now the case is exactly as full as you are done, and finishing leaves it packed wall to wall.

Capacity kept a different job — going over it is what raises the warning. But it stopped being the thing the picture was about.

Ask which number the user is actually watching. Not which number is most accurate, or most available, or most impressive to compute. Progress bars, dashboards, rings, streaks, storage meters — a great many of them are honestly reporting a quantity nobody in the room cares about, and it is worth checking yours.

Springs read as toys

Last one, and it is a brand decision disguised as a config value.

withSpring({ damping: 13 }) overshoots. It goes past its target and comes back. That overshoot is playful — it is the physics of a bouncy thing, and your eye reads it as a toy.

Fine for a game. Wrong for a calm utility. We use withTiming with an ease-out and no overshoot, because things settling into a bag settle. They don't bounce.

Your motion curve is making a claim about what kind of product this is, whether you chose it or accepted a default.

Also: honest emptiness still needs a material

Four small items in a carry-on genuinely is mostly air, and the view should say so. But a blank cream rectangle doesn't read as empty — it reads as broken.

Quilted lining at 7% opacity, and the same emptiness reads as lining.

When a view is honestly sparse, texture the empty state. Truthfulness is not an excuse to render nothing; the user cannot tell the difference between "there is nothing here" and "this failed to load" unless you draw it.

The short version

before the fourth colour pass.**

through every layer of padding, before you draw anything.

are showing them.

None of this is taste. Every one of those was a number we could have worked out before opening the drawing tool, and the only reason each of them cost us days is that a wrong-looking screen is so much more inviting to tweak than to measure.

Measure first. The palette will still be there afterwards, and it will need less work.

Comments

No account needed — pick any name and say your piece.

    No account, no email. Be decent — anything vile gets removed.