Skip to content

Conversation

@MatteoGabriele
Copy link
Contributor

Adding avatars for each connected service has several problems:

  • Visually, when a user has the same avatar everywhere, it creates multiple copies of the same image to be rendered
  • The feature won't scale if we add more services

My proposal is to retain only the single cloud icon and add a green dot to indicate to the user that it is connected.

before
Screenshot 2026-02-08 at 17 00 12

after
Screenshot 2026-02-08 at 17 24 37

@vercel
Copy link

vercel bot commented Feb 8, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Feb 12, 2026 9:12pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Feb 12, 2026 9:12pm
npmx-lunaria Ignored Ignored Feb 12, 2026 9:12pm

Request Review

@codecov
Copy link

codecov bot commented Feb 8, 2026

Codecov Report

❌ Patch coverage is 46.15385% with 7 lines in your changes missing coverage. Please review.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
app/components/Header/AccountMenu.client.vue 46.15% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 8, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR refactors the AccountMenu component by removing the hasBothConnections computation and simplifying avatar rendering logic. The avatar selection now follows a linear flow: display npm avatar if available, otherwise display atproto avatar, otherwise show a placeholder. The authentication dropdown has been restructured to always render a single npm CLI section alongside an Atmosphere section, eliminating previous conditional blocks and dividers. The public API surface of useConnector() has been updated to remove the avatar property from its return value. The overall change reduces code complexity from 67 lines to 35 lines net.

Possibly related PRs

Suggested reviewers

  • danielroe
  • zeucapua
🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The PR description clearly explains the motivation for the changes, identifies specific problems with the current implementation, and proposes a concrete solution with before/after screenshots.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
app/components/Header/AccountMenu.client.vue (1)

68-76: Consider adding accessible text for connection status.

The green dot visually indicates that the user is connected to services, but screen reader users have no equivalent indication. Consider adding visually hidden text to convey this status.

Proposed fix
       <div v-if="hasAnyConnection" class="flex items-center relative">
         <span
           class="w-6 h-6 rounded-full bg-bg-muted ring-2 ring-bg flex items-center justify-center"
           :class="hasBothConnections ? 'relative z-10' : ''"
         >
-          <span class="size-1.5 rounded-full bg-green-600 absolute top-0 -inset-ie-0"></span>
+          <span class="size-1.5 rounded-full bg-green-600 absolute top-0 -inset-ie-0" aria-hidden="true"></span>
           <span class="i-carbon-cloud w-3 h-3 text-fg-muted" aria-hidden="true" />
         </span>
+        <span class="sr-only">{{ $t('account_menu.connected') }}</span>
       </div>

This requires adding a connected translation key to your i18n files.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/components/Header/AccountMenu.client.vue (1)

60-79: ⚠️ Potential issue | 🟡 Minor

Connection status is not accessible to screen readers.

The green dot indicator conveys the connected state visually but provides no accessible alternative. Screen reader users cannot perceive this status change. Consider adding an aria-label to the button that reflects the current connection state.

Suggested fix
     <ButtonBase
       type="button"
       :aria-expanded="isOpen"
       aria-haspopup="true"
+      :aria-label="hasAnyConnection ? $t('account_menu.connected') : $t('account_menu.connect')"
       `@click`="isOpen = !isOpen"
       class="border-none"
     >

This assumes the appropriate translation keys exist. Adjust key names as needed.

🧹 Nitpick comments (1)
app/components/Header/AccountMenu.client.vue (1)

69-75: Consider removing vestigial z-10 conditional class.

The conditional hasBothConnections ? 'relative z-10' : '' appears to be leftover from the previous multi-avatar stacking design. Since the UI now renders a single cloud icon regardless of how many services are connected, the z-index logic no longer serves a purpose. The parent div at line 68 already provides the relative positioning context needed for the status dot.

Suggested simplification
         <span
           class="w-6 h-6 rounded-full bg-bg-muted ring-2 ring-bg flex items-center justify-center"
-          :class="hasBothConnections ? 'relative z-10' : ''"
         >

@github-actions
Copy link

github-actions bot commented Feb 8, 2026

Lunaria Status Overview

🌕 This pull request will trigger status changes.

Learn more

By default, every PR changing files present in the Lunaria configuration's files property will be considered and trigger status changes accordingly.

You can change this by adding one of the keywords present in the ignoreKeywords property in your Lunaria configuration file in the PR's title (ignoring all files) or by including a tracker directive in the merged commit's description.

Tracked Files

File Note
lunaria/files/en-GB.json Localization changed, will be marked as complete.
lunaria/files/en-US.json Source changed, localizations will be marked as outdated.
lunaria/files/it-IT.json Localization changed, will be marked as complete.
Warnings reference
Icon Description
🔄️ The source for this localization has been updated since the creation of this pull request, make sure all changes in the source have been applied.

@danielroe
Copy link
Member

what about instead displaying the logo of the connected service? (npm, bluesky or atproto) and we revisit when we get lots more... (could display a +4 or something)

@MatteoGabriele
Copy link
Contributor Author

That could indeed be a valid alternative. I will use the logos

@Tobbe
Copy link

Tobbe commented Feb 8, 2026

what about instead displaying the logo of the connected service? (npm, bluesky or atproto) and we revisit when we get lots more... (could display a +4 or something)

How do you pick what logo to display though, if the user is connected to more than one thing? (assuming they can be)

Could also have overlapping avatars. (But then again it gets tricky - which one do we show on top?)

@MatteoGabriele
Copy link
Contributor Author

There's also an "issue" with the ATProto icon: it kinda looks like I want to connect my email.

Screenshot 2026-02-08 at 18 31 04

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/components/Header/AccountMenu.client.vue (1)

60-88: ⚠️ Potential issue | 🟠 Major

Add an explicit accessible label for the icon-only button.

When connected, the button contains only aria-hidden icons, so it has no accessible name. Please add an aria-label (you already added account_menu.connected in i18n).

✅ Proposed fix
     <ButtonBase
       type="button"
       :aria-expanded="isOpen"
       aria-haspopup="true"
+      :aria-label="hasAnyConnection ? $t('account_menu.connected') : $t('account_menu.connect')"
       `@click`="isOpen = !isOpen"
       class="border-none"
     >

@MatteoGabriele
Copy link
Contributor Author

MatteoGabriele commented Feb 8, 2026

@danielroe I've updated the PR with icons.
The npm icon is fine, but the one I picked for the ATProto might be misunderstood. Let me know what you think. I could use the Bluesky icon, but that would technically not be correct.

@danielroe
Copy link
Member

we need to update the mobile menu as well:

SCR-20260208-pqwx

I think the icon looks good.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/components/Header/MobileMenu.client.vue (1)

125-135: ⚠️ Potential issue | 🟡 Minor

Missing green status indicator for visual consistency.

The npm CLI connection (line 121) displays a green status dot to indicate active connection, but the Atmosphere connection lacks this indicator. For consistent UX, consider adding the same visual cue here.

💡 Suggested fix to add status indicator
               <span class="flex-1 truncate">@{{ atprotoUser.handle }}</span>
+              <span class="w-2 h-2 rounded-full bg-green-500" aria-hidden="true" />
             </button>

isConnected: isNpmConnected,
isConnecting: isNpmConnecting,
npmUser,
avatar: npmAvatar,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

still wondering if it makes sense to show avatars in the dropdown, even if not in the top level .... 🤔

wdyt?

Copy link
Contributor Author

@MatteoGabriele MatteoGabriele Feb 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it may look a bit confusing and inconsistent.
If I have to go with avatars, which I do like because they give the impression that I've actually connected, I prefer the initial proposal.

@whitep4nth3r whitep4nth3r marked this pull request as draft February 12, 2026 16:00
@whitep4nth3r
Copy link
Contributor

whitep4nth3r commented Feb 12, 2026

Personal opinion, I want to see my face if I am just logged in via Bluesky.

So maybe for the first service, you show the avatar, and for subsequent, you do the other thing.

This is my preference:

  1. Show the first logged in service avatar in the header
  2. Show all other avatars next to the items in the dropdown list

e.g.

If you're logged into Bluesky only, show the Bluesky avatar in the header.
If you're logged into npm only, show the npm avatar in the header.

If you're logged into both, show the FIRST ONE in the list in the header, which at this point is Bluesky.

Additionally, the list items of available services should be in the same order all the time to avoid confusion.

e.g.

Bluesky first
npm second
etc

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/components/Header/AccountMenu.client.vue (1)

64-88: ⚠️ Potential issue | 🟠 Major

Add an accessible label for the icon-only account button.
When connected but no avatar is available, the button renders only an aria-hidden icon, leaving no accessible name for screen readers. Add a hidden label (or an aria-label) so the menu remains discoverable.

Proposed fix
       <span v-if="hasAnyConnection" class="flex items-center">
         <img
           v-if="isNpmConnected && npmAvatar"
           :src="npmAvatar"
           :alt="npmUser || $t('account_menu.npm_cli')"
           width="24"
           height="24"
           class="w-6 h-6 rounded-full ring-2 ring-bg object-cover"
         />
         <img
           v-else-if="atprotoUser?.avatar"
           :src="atprotoUser.avatar"
           :alt="atprotoUser.handle"
           width="24"
           height="24"
           class="w-6 h-6 rounded-full ring-2 ring-bg object-cover"
         />
         <span
           v-else
           class="w-6 h-6 rounded-full bg-bg-muted ring-2 ring-bg flex items-center justify-center"
         >
           <span class="i-carbon-cloud w-3 h-3 text-fg-muted" aria-hidden="true" />
         </span>
+        <span class="sr-only">
+          {{ npmUser || atprotoUser?.handle || $t('account_menu.connect') }}
+        </span>
       </span>

Comment on lines +164 to +178
<button
v-else
type="button"
role="menuitem"
class="w-full px-3 py-2.5 flex items-center gap-3 hover:bg-bg-muted transition-colors text-start rounded-md"
@click="openConnectorModal"
>
<span class="w-8 h-8 rounded-full bg-bg-muted flex items-center justify-center">
<span
v-if="isNpmConnecting"
class="i-carbon-circle-dash w-4 h-4 text-yellow-500 animate-spin"
aria-hidden="true"
/>
<span v-else class="i-carbon-terminal w-4 h-4 text-fg-muted" aria-hidden="true" />
</span>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

# Search for UnoCSS icon class usage patterns in Vue components
rg 'class=".*i-carbon' app/components --type vue -A 1 -B 1 | head -50

Repository: npmx-dev/npmx.dev

Length of output: 88


🏁 Script executed:

# Specifically check AccountMenu.client.vue for icon usage
cat -n app/components/Header/AccountMenu.client.vue | grep -E '(i-carbon|class=)' | head -20

Repository: npmx-dev/npmx.dev

Length of output: 1661


🏁 Script executed:

# Check for both colon and dash syntax usage across the codebase
rg 'i-carbon[:|-]' app/ -o | sort | uniq -c

Repository: npmx-dev/npmx.dev

Length of output: 4817


Use UnoCSS colon syntax for the Carbon icon classes.
The colon form provides better performance for icons with dashes. Use i-carbon:circle-dash and i-carbon:terminal instead of the dash-separated form.

Proposed fix
-                <span
-                  v-if="isNpmConnecting"
-                  class="i-carbon-circle-dash w-4 h-4 text-yellow-500 animate-spin"
-                  aria-hidden="true"
-                />
-                <span v-else class="i-carbon-terminal w-4 h-4 text-fg-muted" aria-hidden="true" />
+                <span
+                  v-if="isNpmConnecting"
+                  class="i-carbon:circle-dash w-4 h-4 text-yellow-500 animate-spin"
+                  aria-hidden="true"
+                />
+                <span v-else class="i-carbon:terminal w-4 h-4 text-fg-muted" aria-hidden="true" />
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<button
v-else
type="button"
role="menuitem"
class="w-full px-3 py-2.5 flex items-center gap-3 hover:bg-bg-muted transition-colors text-start rounded-md"
@click="openConnectorModal"
>
<span class="w-8 h-8 rounded-full bg-bg-muted flex items-center justify-center">
<span
v-if="isNpmConnecting"
class="i-carbon-circle-dash w-4 h-4 text-yellow-500 animate-spin"
aria-hidden="true"
/>
<span v-else class="i-carbon-terminal w-4 h-4 text-fg-muted" aria-hidden="true" />
</span>
<button
v-else
type="button"
role="menuitem"
class="w-full px-3 py-2.5 flex items-center gap-3 hover:bg-bg-muted transition-colors text-start rounded-md"
`@click`="openConnectorModal"
>
<span class="w-8 h-8 rounded-full bg-bg-muted flex items-center justify-center">
<span
v-if="isNpmConnecting"
class="i-carbon:circle-dash w-4 h-4 text-yellow-500 animate-spin"
aria-hidden="true"
/>
<span v-else class="i-carbon:terminal w-4 h-4 text-fg-muted" aria-hidden="true" />
</span>

@MatteoGabriele
Copy link
Contributor Author

@whitep4nth3r @danielroe based on both your ideas, I have made the changes

when nothing is connected
Screenshot 2026-02-12 at 21 56 13

when atproto is connected
Screenshot 2026-02-12 at 21 56 33

when npm is connected
Screenshot 2026-02-12 at 22 13 44

when both are connected
Screenshot 2026-02-12 at 22 14 05

@MatteoGabriele MatteoGabriele changed the title feat(ui): use icon-only when connected to services feat(ui): use icon-only when connected to both services Feb 12, 2026
@Tobbe
Copy link

Tobbe commented Feb 12, 2026

I think users are so used to seeing their own avatar in that spot, so I think we should have one there even if the user is connected to multiple services.

The question then is - which one do we show? The first one the user connected? The most recent one?
Have a setting for it on the settings page, where the user can pick the one they want to use? (maybe including a generic cloud icon?)

Always using the first one connected, as @whitep4nth3r suggested, does have the benefit of being less of a surprise, and potentially confusing when the avatar they're used to happens to change just because they connected some other service

@MatteoGabriele
Copy link
Contributor Author

MatteoGabriele commented Feb 12, 2026

@Tobbe I do agree with you.

I proposed using the same connected icon (with a green dot) for every service to maintain consistency. People usually see their own avatar, but they rarely expect to see all avatars piled together when connected to multiple services.

Maybe we could do progressive enhancement.
Remove multiple avatars in the dropdown button, then add the possibility to set one of them as the main avatar.

@Tobbe
Copy link

Tobbe commented Feb 12, 2026

but they rarely expect to see all avatars piled together when connected to multiple services.

I agree. That's why I suggested just showing one of them in the menu toggle button.

I'm fine with going with Salma's suggestion of always just using the avatar from the service the user first connected.
But having it configurable (like you say, the possibility to set one of them as the main avatar) is also a valid option 👍

@MatteoGabriele
Copy link
Contributor Author

How do we know which one is picked first tho? We probably need to save it somewhere, right? 🤔

@Tobbe
Copy link

Tobbe commented Feb 12, 2026

How do we know which one is picked first tho? We probably need to save it somewhere, right? 🤔

We don't already store the timestamp for when a service was connected, do we?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants