Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions components/email/thread-email-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,14 @@ export function ThreadEmailItem({
</button>
)}

{/* Unread indicator */}
{/* Unread indicator — anchored on the first line (top padding + half a
small avatar), not on the row's midpoint, so it stays in line with
the checkbox and avatar when the row wraps to a second line. */}
{isUnread && (
<div className="absolute start-7 top-1/2 -translate-y-1/2">
<div
className="absolute start-7 -translate-y-1/2"
style={{ top: `calc(var(--density-item-py) + ${density === 'extra-compact' ? '0.625rem' : '1rem'})` }}
>
<Circle className="w-1.5 h-1.5 fill-unread text-unread" />
</div>
)}
Expand Down
29 changes: 23 additions & 6 deletions components/email/thread-list-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,27 @@ import { ThreadEmailItem } from "./thread-email-item";
import { EmailHoverActions } from "./email-hover-actions";
import { useTranslations } from "next-intl";

/**
* Unread bullet in the row gutter.
*
* It is absolutely positioned so it never widens the row, which means it has
* to be told where the row's *first* line sits: centring it on the row (the
* old `top-1/2`) drifts down as soon as the row grows a second and third line,
* leaving it visibly out of line with the checkbox and the avatar it reads as
* a column with. Anchor = top padding + half an avatar.
*/
function UnreadDot({ density, compactAvatar }: { density: string; compactAvatar: boolean }) {
const halfFirstLine = density === 'extra-compact' ? '0.625rem' : compactAvatar ? '1rem' : '1.25rem';
Comment on lines +32 to +33
return (
<div
className="absolute start-0.5 -translate-y-1/2"
style={{ top: `calc(var(--density-item-py) + ${halfFirstLine})` }}
>
<Circle className="w-2 h-2 fill-unread text-unread" />
</div>
);
}

/**
* Small chip showing the originating folder of a message, rendered in the
* aggregate "All …" views (All Mail / unified / cross-account) where rows come
Expand Down Expand Up @@ -244,9 +265,7 @@ const SingleEmailItem = React.forwardRef<HTMLDivElement, SingleEmailItemProps>(
)}

{isUnread && (
<div className="absolute start-0.5 top-1/2 -translate-y-1/2">
<Circle className="w-2 h-2 fill-unread text-unread" />
</div>
<UnreadDot density={density} compactAvatar={isFocusedMailLayout} />
)}

{density !== 'extra-compact' && (
Expand Down Expand Up @@ -683,9 +702,7 @@ export const ThreadListItem = React.forwardRef<HTMLDivElement, ThreadListItemPro
)}

{hasUnread && (
<div className="absolute start-0.5 top-1/2 -translate-y-1/2">
<Circle className="w-2 h-2 fill-unread text-unread" />
</div>
<UnreadDot density={density} compactAvatar={isFocusedMailLayout} />
)}

{density !== 'extra-compact' && (
Expand Down