A Day of UX Polish
Listening for where it stutters, fixing one small decision at a time. A record of small changes adding up.
A Day of UX Polish
Today wasn’t a day for shipping a big feature. It was a day of fixing many small decisions. “This feels off here,” “this is confusing” — I listened, and polished one at a time. Looking at them together, they were all heading in the same direction, so I’m writing them down together.
Even One Photo Deserves Context
You can now attach a title and description to photos. Tap a photo and you get a large preview; tap the edit icon at the top right and a small editor panel slides in beside it. Most of the time you’re just looking at the photo. Only when there’s something worth writing down do you go a step deeper.
This matches the direction Fecit has always taken. A task isn’t just a checkbox — it’s a unit where you can also write down intent and reflection. An attached photo shouldn’t just be a file either; it should hold “why I took this here” alongside it.
In the same spirit, the drag-and-drop area is no longer limited to the photo slot — you can drop anywhere on the task detail screen. Even on top of the rich text editor. The catch was that ProseMirror inside the editor was eating the drop event before React’s bubble handler could see it. I worked around it by attaching a native listener at the capture phase. It looks small, but “you can drop here but not there” feels like a bug to the user.
Don’t Block — Tell
Photos cap at three per task. Dropping a fourth used to silently do nothing. To the user, that looked broken — they couldn’t tell what went wrong.
Now if you drop a fourth, the drop is rejected but a toast appears: “You can attach up to 3 photos.” Same outcome, but conveying intent is different from staying silent.
In the same spirit, dropping a photo now auto-expands the attachments section. If a user drops a photo while the section is collapsed, the photo can’t just disappear into the void.
Pasting a URL Just Works
Paste a URL into an empty area of the task detail and it gets registered as a web link automatically. No more clicking the ”+” button, opening a modal, pasting the URL, and saving.
But pasting inside an input should still go into that input. If you’re writing a description and paste a URL, it shouldn’t suddenly fly off and become a web link. The paste handler now checks whether focus is inside an editable area and only auto-registers when it isn’t.
Labels, Faster
Attaching a label used to mean: open the picker, scroll to find it, and if it’s not there, navigate elsewhere to create one and come back. Too many steps.
Now there’s a search bar at the top of the picker, and if no result matches, a “Create '
I started with client-side filtering, but mobile loads labels in pages of 12, so labels not yet fetched wouldn’t show up in search. That meant “no results” with a misleading “Create” option, which could create duplicates. So I added a keyword parameter to the API and unified everything to server-side search. Don’t fake search in a cache; do it at the source.
Don’t Show an Empty Card
When you complete a task that came from a template, a card appears showing the session number and comparisons against the average. “3rd session. Satisfaction is higher than the 3.8 average.”
The card used to fade in immediately, then once template stats arrived, the session row and comparison rows would pop into place. Empty card → filled card. A small flicker, but it kept catching the eye.
The fix turned out to be simple. Don’t mount the card until the data is ready, and trigger the fade-in at the moment data arrives. A one-line change (if (!stats) return null) and the flicker was gone.
Same Flow, Same Skeleton
Project and label have different domains, but the user flow is essentially the same: pick one from a list, drill in, edit. Yet somehow one was a right-side panel and the other was a modal. There was no reason for them to be different.
I aligned label to follow project: list on the left, detail panel on the right, sticky title bar, ⋮ menu at the top right. Same with the web link add/edit modals — they now share the same horizontal layout (label on the left, input on the right) as the photo edit panel, and all of them use the same rich text editor for descriptions.
Same flow should look the same. If it looks different every time, the user has to relearn it every time.
Risky Actions Live One Step In
The Project detail body had a bare “Delete” button. Too prominent. Mobile already kept it in a separate inform menu screen, so I moved desktop to follow that pattern.
Now it lives inside the ⋮ menu at the top right of the title bar. One more click, then a confirmation. Risky actions you rarely take should sit one layer in. I also replaced the vague “Are you sure?” confirmation with a clearer “Delete this?”.
Filling In Collaboration on Desktop
Desktop projects were essentially a single-user editing screen. Member invites, role changes, joining via token, accepting invites — all of it was mobile-only. That was the strange part: collaboration is the heart of a project.
I ported it all to desktop today. Click a member row to open a detail modal where you can change roles or remove them. Use the invite button in the header to invite a friend with one click or issue an invite code to share. Received invites are handled from a button on the project page top bar.
This is the start. To grow Project into a real “goal-achievement tool,” collaboration had to stop being half-built. Goal definition and progress tracking come next, but that’s another post.
Satisfaction Is a Face, Not a Number
Project satisfaction was a 0–100 number input. On mobile it was a 1–5 face icon picker — desktop was the odd one out. Aligned desktop to use the same picker.
A few smaller polish moves while I was at it: tapping the dim background of the session card now dismisses it; long-pressing a state icon on a mobile task list opens the state picker directly. Patterns that were already common, just missing in places.
A single big change can shift the user experience, but it’s the accumulation of small decisions that shapes the impression. Listening for where it stutters, where it goes silent, where the same flow ends up wearing a different face — that’s most of what making things actually is.
When a user says “something feels off,” it usually means a small decision didn’t quite land.