From b1df7b2581796d8eabcf4f40df5409ef63f8f829 Mon Sep 17 00:00:00 2001 From: Lucas Winther Date: Sat, 15 Aug 2026 21:16:25 +0200 Subject: [PATCH] refactor(ui): make an event row a single open target MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The row already opened the event; the tick sat on top of it, so the two smallest targets on the page did different things a few pixels apart. "Done" was never the only thing a reader wants to say about an event either — status, effort and notes all live in the detail sheet. The tick becomes a chevron: decorative, aria-hidden, and leaning towards where the row is about to take you on hover. The full-bleed button is the only control, so there is no second stop for anyone tabbing or using a screen reader. Undo on a revealed ignored row stays a real button — it is a real action with nowhere better to sit. Co-Authored-By: Claude Opus 5 (1M context) --- src/client/App.tsx | 2 - src/client/components/EventRow.tsx | 71 ++++++++++++++++++------------ src/client/styles.css | 8 ++++ 3 files changed, 50 insertions(+), 31 deletions(-) diff --git a/src/client/App.tsx b/src/client/App.tsx index 64f7550..3d5e717 100644 --- a/src/client/App.tsx +++ b/src/client/App.tsx @@ -228,7 +228,6 @@ export function App() { status={prog.progress[row.event.id]?.status} effort={prog.progress[row.event.id]?.effort} ignored={ignored.marks[row.event.id] !== undefined} - onToggle={toggle} onRestore={(id) => ignored.toggle(id)} onOpen={setOpenId} /> @@ -246,7 +245,6 @@ export function App() { status={prog.progress[row.event.id]?.status} effort={prog.progress[row.event.id]?.effort} ignored={ignored.marks[row.event.id] !== undefined} - onToggle={toggle} onRestore={(id) => ignored.toggle(id)} onOpen={setOpenId} /> diff --git a/src/client/components/EventRow.tsx b/src/client/components/EventRow.tsx index 758e201..15c25ed 100644 --- a/src/client/components/EventRow.tsx +++ b/src/client/components/EventRow.tsx @@ -21,7 +21,6 @@ interface EventRowProps { effort?: Effort | undefined; /** Only ever true when the reader has chosen to reveal ignored events. */ ignored?: boolean | undefined; - onToggle: (id: string) => void; onRestore?: ((id: string) => void) | undefined; onOpen: (id: string) => void; } @@ -32,7 +31,6 @@ export function EventRow({ status, effort, ignored = false, - onToggle, onRestore, onOpen, }: EventRowProps) { @@ -156,9 +154,15 @@ export function EventRow({ - {/* Sits above the row target so ticking done never opens the sheet. - On a revealed ignored row this becomes the undo, which is the most - direct place to put it. */} + {/* The trailing control used to tick an event done from the list. It + does not any more: "done" was never the only thing a reader wants + to say about an event, and a tick they can hit by accident on the + way to opening it is a bad trade. The row opens the sheet, where + status, effort and notes all live; this is just the affordance + saying so. + + The one exception is a revealed ignored row, where undo is a real + action with nowhere better to sit. */} {ignored && onRestore !== undefined ? ( ) : ( - + // Decorative: the full-bleed button behind the row is already the + // control, and a second one with the same effect would just be an + // extra stop for anyone tabbing or using a screen reader. + + {completed ? ( + + + + ) : ( + + + + )} + )} diff --git a/src/client/styles.css b/src/client/styles.css index e489056..a224251 100644 --- a/src/client/styles.css +++ b/src/client/styles.css @@ -176,6 +176,13 @@ body { border-color: var(--color-faint); color: var(--color-muted); } + /* The chevron leans towards where the row is about to take you. It is the + only thing left on the right of a row, so it has to carry "this opens" + on its own. */ + .event-row:hover .row-open { + transform: translateX(2px); + color: var(--color-ink); + } .row-check:hover { transform: scale(1.08); border-color: var(--color-muted); @@ -212,6 +219,7 @@ body { } .row-check:hover, .row-check:active, + .event-row:hover .row-open, .event-row:hover .row-rail { transform: none; }