From 9cf4c9873fa4f2ec0ce45601115f6a4e7ebf59d0 Mon Sep 17 00:00:00 2001 From: cesnimda Date: Sat, 4 Jul 2026 16:50:19 +0200 Subject: [PATCH] feat(ui): email row polish + custom checkbox - Add onOpen prop to EmailRow (calls parent handler when provided, falls back to opening in Gmail); keep explicit Open-in-Gmail action. - New accessible Checkbox primitive (Radix + design tokens, focus ring), exported from ui barrel; replaces bare input in the select cell. - Row polish: consistent height, vertical rhythm, single hover state, clearer hierarchy (prominent subject, muted sender/snippet), one-line ellipsis snippet. Co-Authored-By: Claude Opus 4.8 --- frontend/src/components/EmailRow.jsx | 20 +++++++--- frontend/src/components/ui/checkbox.jsx | 43 ++++++++++++++++++++ frontend/src/components/ui/index.js | 1 + frontend/src/styles.css | 52 ++++++++++++++++++------- 4 files changed, 97 insertions(+), 19 deletions(-) create mode 100644 frontend/src/components/ui/checkbox.jsx diff --git a/frontend/src/components/EmailRow.jsx b/frontend/src/components/EmailRow.jsx index eb00ca8..868d0da 100644 --- a/frontend/src/components/EmailRow.jsx +++ b/frontend/src/components/EmailRow.jsx @@ -1,5 +1,6 @@ import { useState } from 'react'; import { EmailApi } from '../api/client.js'; +import { Checkbox } from './ui/checkbox.jsx'; const fmtDate = (iso) => { const d = new Date(iso); @@ -16,7 +17,7 @@ const fmtSize = (b) => { return `${(b / 1048576).toFixed(1)} MB`; }; -export default function EmailRow({ email: initial, onRemove, selected, onToggleSelect, focused }) { +export default function EmailRow({ email: initial, onRemove, selected, onToggleSelect, focused, onOpen }) { const [email, setEmail] = useState(initial); const [acting, setActing] = useState(false); @@ -69,12 +70,16 @@ export default function EmailRow({ email: initial, onRemove, selected, onToggleS return ( onOpen ? onOpen(email) : openInGmail()} + title={onOpen ? 'Open' : 'Open in Gmail'} > {onToggleSelect && ( e.stopPropagation()}> - onToggleSelect(email.id)} /> + onToggleSelect(email.id)} + aria-label={selected ? 'Deselect email' : 'Select email'} + /> )} {email.isUnread && } @@ -83,7 +88,7 @@ export default function EmailRow({ email: initial, onRemove, selected, onToggleS {email.subject || '(no subject)'} - {email.snippet && — {email.snippet}} + {email.snippet && {email.snippet}} {email.hasAttachments && 📎} @@ -113,6 +118,11 @@ export default function EmailRow({ email: initial, onRemove, selected, onToggleS disabled={email._unsubDone} >{email._unsubDone ? '✓' : '✉✕'} )} +