Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
4167fb2
fix(header): lift avatar out of the stats pill
tsahimatsliah May 27, 2026
fbae06c
feat(header): add a subtle border and bump the reputation icon
tsahimatsliah May 27, 2026
bcb3d8f
feat(header): bump border opacity and avatar size
tsahimatsliah May 27, 2026
60a96f8
fix(header): make stat-button hover state fill the pill
tsahimatsliah May 27, 2026
f315ad6
fix(header): align inner profile gap with parent header gap
tsahimatsliah May 27, 2026
07c8f7e
fix(header): bump stat-button padding and rebalance visible inset
tsahimatsliah May 27, 2026
7d0f798
Merge branch 'main' into claude/polish-header-stats-gap
tsahimatsliah May 27, 2026
459a4f4
Merge branch 'main' into claude/polish-header-stats-gap
tsahimatsliah May 27, 2026
6ea61f2
fix(header): bring the avatar back inside the stats pill
tsahimatsliah May 27, 2026
6d23694
fix(header): merge reputation and avatar into one button
tsahimatsliah May 27, 2026
4478322
fix(header): tighten stat-button paddings and grow avatar to fill
tsahimatsliah May 27, 2026
d53c610
Merge branch 'main' into claude/polish-header-stats-gap
tsahimatsliah May 27, 2026
c9ffd36
fix(header): shrink avatar to fit a 2px inset and match pill curvature
tsahimatsliah May 27, 2026
b9abb70
chore: re-trigger CI after flaky webapp test
tsahimatsliah May 27, 2026
e142b2c
fix(header): drop right padding on combined profile button
tsahimatsliah May 27, 2026
d78cb18
chore: re-trigger CircleCI
tsahimatsliah May 27, 2026
2f6b64a
chore: document profile pill layout
tsahimatsliah May 27, 2026
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
8 changes: 6 additions & 2 deletions packages/shared/src/components/profile/ProfileButton.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,14 @@ it('should show "Profile settings" tooltip on the profile picture', () => {
).toBeInTheDocument();
});

it('should show "Reputation" tooltip on the reputation badge', () => {
it('should render the reputation reward target inside the profile button', () => {
renderComponent();

expect(screen.getByLabelText('Reputation')).toBeInTheDocument();
expect(
screen
.getByRole('button', { name: 'Profile settings' })
.querySelector('[data-reward-target="reputation"]'),
).not.toBeNull();
});

it('should show settings option that opens modal', async () => {
Expand Down
59 changes: 33 additions & 26 deletions packages/shared/src/components/profile/ProfileButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ import classNames from 'classnames';
import dynamic from 'next/dynamic';
import { useAuthContext } from '../../contexts/AuthContext';
import { ProfilePictureWithIndicator } from './ProfilePictureWithIndicator';
import { ProfileImageSize } from '../ProfilePicture';
import { CoreIcon, ReputationIcon, SettingsIcon } from '../icons';
import { Button, ButtonSize, ButtonVariant } from '../buttons/Button';
import { IconSize } from '../Icon';
import { useInteractivePopup } from '../../hooks/utils/useInteractivePopup';
import { ReadingStreakButton } from '../streak/ReadingStreakButton';
import { useReadingStreak } from '../../hooks/streaks';
Expand Down Expand Up @@ -48,7 +50,7 @@ export default function ProfileButton({
Partial<Record<QuestRewardType.Reputation | QuestRewardType.Cores, string>>
>({});
const coresCounterRef = useRef<HTMLDivElement | null>(null);
const reputationCounterRef = useRef<HTMLDivElement | null>(null);
const reputationCounterRef = useRef<HTMLSpanElement | null>(null);
const displayedBalance =
typeof animatedCores === 'number'
? animatedCores
Expand Down Expand Up @@ -195,6 +197,8 @@ export default function ProfileButton({
return <></>;
}

// The pill groups streak / cores / reputation / avatar into one
// bordered control with edge-to-edge hover slots.
return (
<>
{settingsIconOnly ? (
Expand All @@ -204,12 +208,13 @@ export default function ProfileButton({
icon={<SettingsIcon />}
/>
) : (
<div className="flex h-10 items-center rounded-12 bg-surface-float px-1">
<div className="flex h-10 items-stretch overflow-hidden rounded-12 border border-border-subtlest-tertiary bg-surface-float">
{isStreaksEnabled && streak && (
<ReadingStreakButton
streak={streak}
isLoading={isLoading}
compact
className="!h-full !rounded-none !pl-1.5 !pr-2"
/>
)}
{hasCoresAccess && (
Expand All @@ -233,44 +238,46 @@ export default function ProfileButton({
tag="a"
variant={ButtonVariant.Tertiary}
size={ButtonSize.Small}
className="!px-1.5"
className="!h-full !rounded-none !pl-1.5 !pr-2"
>
{largeNumberFormat(displayedBalance)}
</Button>
</Link>
</div>
</Tooltip>
)}
<Tooltip content="Reputation">
<div
ref={reputationCounterRef}
className="flex origin-center justify-center will-change-transform"
>
<Button
type="button"
data-reward-target={QuestRewardType.Reputation}
icon={<ReputationIcon className="text-accent-onion-default" />}
variant={ButtonVariant.Tertiary}
size={ButtonSize.Small}
className="!pl-0.5 !pr-1.5"
onClick={wrapHandler(() => onUpdate(!isOpen))}
>
{largeNumberFormat(displayedReputation ?? 0)}
</Button>
</div>
</Tooltip>
<Tooltip side="bottom" content="Profile settings">
<button
<Button
type="button"
aria-label="Profile settings"
icon={
<ReputationIcon
className="text-accent-onion-default"
size={IconSize.Medium}
/>
}
variant={ButtonVariant.Tertiary}
size={ButtonSize.Small}
className={classNames(
'focus-outline cursor-pointer items-center border-none bg-transparent p-0',
className ?? 'flex',
'!h-full !gap-0 !rounded-none !pl-0.5 !pr-0',
className,
)}
onClick={wrapHandler(() => onUpdate(!isOpen))}
>
<ProfilePictureWithIndicator user={user} />
</button>
<span
ref={reputationCounterRef}
data-reward-target={QuestRewardType.Reputation}
className="inline-flex origin-center items-center will-change-transform"
>
{largeNumberFormat(displayedReputation ?? 0)}
</span>
<ProfilePictureWithIndicator
user={user}
size={ProfileImageSize.Large}
className="!h-9 !w-9 !rounded-10"
wrapperClassName="relative ml-2"
/>
</Button>
</Tooltip>
</div>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ export function ReadingStreakButton({
: ButtonVariant.Float
}
onClick={handleToggle}
className={classnames('gap-1', compact && '!px-1.5', className)}
className={classnames('gap-1', className)}
size={!compact && !isMobile ? ButtonSize.Medium : ButtonSize.Small}
>
{streak?.current}
Expand Down
Loading