Trying to add a tiny review marker — a yellow palette rewrite and an API bug
What started as 'show which tasks have a retrospect written' became a full SECONDARY palette overhaul and a backend sync bug fix.
Trying to add a tiny review marker
A modest idea
“It would be nice to see which tasks have had a retrospect written on them, at a glance.”
When you complete a task in Fecit, you can set a satisfaction score and write a retrospect. But when you come back to the list, every completed task looks the same. No way to tell which ones you’ve actually reviewed.
One little marker would do it. Or so I thought.
Where to put it, how to draw it
First attempt: a small dot next to the state icon, reusing a pattern from SectionToggleBar.
“Yellow feels reflective” → tried SECONDARY500.
“Muted. Hard to see.” → switched to WARNING500 (orange).
“That doesn’t feel like review.” Accurate critique. A warning-ish orange clashes with the positive “I reflected on this” vibe.
”Just say REVIEWED”
So I tried an explicit REVIEWED badge next to the project abbreviation.
“Doesn’t belong there.”
A single letter R under the state icon? That made the item taller. Absolute positioning fixed the height, but then R only showed up on COMPLETED tasks during testing.
The real bug was on the server
“I set satisfaction but the R isn’t showing.”
I hunted this one with logs. The culprit wasn’t the client — it was the API.
Most of Fecit’s task mutation endpoints bump revision, revised_at, and revised_by together. Those fields are the cursor for incremental sync. But satisfaction/set was missing them:
# retrospect/set — correct
'$set': {
'retrospect': ticket.retrospect,
'revised_at': datetime.now(timezone.utc),
'revised_by': achiever.id,
'revision': new_revision,
}
# satisfaction/set — missing
'$set': {
'satisfaction': ticket.satisfaction,
}
The same omission existed in task/template’s satisfaction endpoint. Fixed both. Now setting satisfaction actually propagates through sync to other devices.
Back to the bar
The R kept fighting with the layout. I went back to a right-side bar — the mirror of the STARTED pulse bar on the left, on the opposite edge, in a different color.
{hasReviewContent && (
<View style={{
position: "absolute", inset: 0,
borderRadius: 8,
borderRightWidth: 3,
borderRightColor: palette.SECONDARY500,
}} />
)}
No layout impact. Symmetrical with the STARTED bar. Distinct domain via color.
”Wait — is our yellow a little green?”
At this point the user (=me) gets suspicious of the palette.
SECONDARY500 original: #E0D147 = rgb(224, 209, 71). The G channel sits suspiciously close to R. A pure yellow has R ≫ G (mid), B ≈ 0. This value carried a strong olive tint.
Swapped the whole scale for Tailwind’s yellow:
SECONDARY50 = "#FEFCE8"
SECONDARY100 = "#FEF9C3"
SECONDARY200 = "#FEF08A"
SECONDARY300 = "#FDE047"
SECONDARY400 = "#FACC15"
SECONDARY500 = "#EAB308"
SECONDARY600 = "#CA8A04"
SECONDARY700 = "#A16207"
SECONDARY800 = "#854D0E"
SECONDARY900 = "#713F12"
Dark mode got the scale inverted, same as PRIMARY. 50 is a dark brown, 900 is a light cream. Good contrast on dark backgrounds, same semantic meaning as light mode.
Does dark yellow become orange?
Asked mid-refactor.
Not really. If you keep yellow’s hue and just lower lightness, RGB walks from #EAB308 → #A16207 → #713F12 — which reads as olive, then brown. Dark yellow is physically closest to brown, not orange.
Orange is a separate hue, sitting closer to red. Darkening yellow won’t turn it orange. To get a brown with orange warmth, you have to deliberately keep more of the red channel while darkening.
Lessons
-
A tiny UI element can shake a whole palette. I only noticed SECONDARY’s olive cast because the review bar looked too much like WARNING.
-
Don’t guess — reproduce. I suspected a layout bug for why R only showed on COMPLETED. The actual cause was data (satisfaction not bumping revision). Should have logged first.
-
Use symmetry. Mirroring the STARTED left-bar on the right with a different color lets users intuit “this means something” without a single word of explanation.
-
Color constants name meaning — values can betray it. SECONDARY had olive hiding behind its name. Trust the name less; look at the actual pixels occasionally.
To add one marker, I touched two backend routes, rewrote 40 lines of palette, edited several client files. The end result is a 3-pixel yellow line. That line quietly says: “I finished reviewing this one.”