Skip to content

feat: accessible rings, accent colors & visual changes#904

Merged
danielroe merged 21 commits intonpmx-dev:mainfrom
jellydeck:visual-adjustments
Feb 4, 2026
Merged

feat: accessible rings, accent colors & visual changes#904
danielroe merged 21 commits intonpmx-dev:mainfrom
jellydeck:visual-adjustments

Conversation

@jellydeck
Copy link
Contributor

@jellydeck jellydeck commented Feb 4, 2026

closes #611
closes #633

  • use accent colors for focus rings in navbar
  • AppLogo component which changes color as per accent color
  • fixes in spacing for visual alignment
  • compare page searchbar icon aligned
  • copy package name button updates itself rather than calling popover for better UX

@vercel
Copy link

vercel bot commented Feb 4, 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 4, 2026 9:53am
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Feb 4, 2026 9:53am
npmx-lunaria Ignored Ignored Feb 4, 2026 9:53am

Request Review

@jellydeck jellydeck changed the title Visual adjustments feat: accessible rings, accent colors & visual changes Feb 4, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

Restructures accent colours into theme-aware light/dark maps and updates lookups in prehydrate and the settings composable to select colours per current theme (including a type change for AccentColorId). Adds a new AppLogo Vue component and exposes it for tests. Replaces many focus-visible:ring-* utilities with focus-visible:outline-accent/70 or focus-visible:(outline-2 outline-accent/70), plus minor spacing/padding tweaks and two fixes to iterate reactive .value collections. Changes are primarily presentational; the only new public component prop is AppLogo's optional class prop.

Possibly related PRs

Suggested labels

idea

Suggested reviewers

  • danielroe
  • serhalp
  • wojtekmaj
🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description check ✅ Passed The PR description directly addresses the linked issues by describing changes to focus rings, accent colors, spacing fixes, and component updates.
Linked Issues check ✅ Passed The PR addresses both linked issues: #611 is resolved through systematic updates of focus styling across components to use accent-coloured outlines instead of rings, and #633 is addressed via accent colour adjustments in DownloadAnalytics.vue.
Out of Scope Changes check ✅ Passed All changes are directly related to focus styling improvements, accent colour implementation, and visual spacing adjustments. Minor scope additions like AppLogo component and button behaviour updates directly support the core objectives.

✏️ 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: 2

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/ConnectorModal.vue (1)

165-173: ⚠️ Potential issue | 🟡 Minor

Avoid cancelling the accent outline on inputs.

focus-visible:outline-none suppresses the accent outline you just added, so the focus indication falls back to the default ring. Consider removing outline-none (and the ring if the outline is the intended cue).

Proposed adjustment
-            class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg placeholder:text-fg-subtle transition-colors duration-200 focus-visible:outline-none focus-visible:ring-2 focus-visible:outline-accent/70"
+            class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg placeholder:text-fg-subtle transition-colors duration-200 focus-visible:outline-accent/70"
-              class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg transition-colors duration-200 focus:border-border-hover focus-visible:outline-none focus-visible:ring-2 focus-visible:outline-accent/70"
+              class="w-full px-3 py-2 font-mono text-sm bg-bg-subtle border border-border rounded-md text-fg transition-colors duration-200 focus:border-border-hover focus-visible:outline-accent/70"

Also applies to: 189-197

🧹 Nitpick comments (1)
app/components/Package/ListToolbar.vue (1)

184-205: Consider updating the sort direction toggle button's focus styling for consistency.

The sort direction toggle button still uses ring-based focus styling (focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-2), whilst the rest of this PR standardises on focus-visible:outline-accent/70. This creates a visual inconsistency.

♻️ Suggested fix
           <button
             v-if="!searchContext"
             type="button"
-            class="p-1.5 rounded border border-border bg-bg-subtle text-fg-muted hover:text-fg hover:border-border-hover transition-colors duration-200 focus-visible:ring-2 focus-visible:ring-fg focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
+            class="p-1.5 rounded border border-border bg-bg-subtle text-fg-muted hover:text-fg hover:border-border-hover transition-colors duration-200 focus-visible:outline-accent/70"
             :aria-label="$t('filters.sort.toggle_direction')"

@github-actions
Copy link

github-actions bot commented Feb 4, 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/ar-EG.json Localization changed, will be marked as complete.
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/es-419.json Localization changed, will be marked as complete.
lunaria/files/es-ES.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.

@codecov
Copy link

codecov bot commented Feb 4, 2026

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: 0

Caution

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

⚠️ Outside diff range comments (1)
app/components/Package/InstallScripts.vue (1)

32-46: ⚠️ Potential issue | 🟡 Minor

Add explicit outline width and offset to ensure visible focus indicators on both elements.

The global CSS in app/assets/main.css defines outline width for button:focus-visible and a:focus-visible, but the <dd> element on line 34 is not covered by those rules. Using focus-visible:outline-accent/70 alone sets only the outline colour without explicit width or offset, which risks a missing or imperceptible focus indicator. For consistency and to ensure accessibility across browsers, add explicit outline utilities:

Suggested fix
-          class="font-mono text-sm text-fg-subtle m-0 truncate focus:whitespace-normal focus:overflow-visible cursor-help rounded focus-visible:outline-accent/70"
+          class="font-mono text-sm text-fg-subtle m-0 truncate focus:whitespace-normal focus:overflow-visible cursor-help rounded focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent/70"
-        class="flex items-center gap-1.5 text-xs text-fg-muted hover:text-fg transition-colors duration-200 focus-visible:outline-accent/70 rounded"
+        class="flex items-center gap-1.5 text-xs text-fg-muted hover:text-fg transition-colors duration-200 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-accent/70 rounded"

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: 2

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

102-102: ring-offset-* classes have no effect when used with outline.

The focus-visible:ring-offset-2 and focus-visible:ring-offset-bg utilities are designed to work with Tailwind's ring utilities (which use box-shadow), not with outline. These classes will be ignored since you're using outline-accent/70 instead of a ring.

For consistency with the disconnect button (line 37) and handle input (line 64), consider removing the ineffective ring-offset classes:

🧹 Proposed cleanup
       <button
         type="submit"
         :disabled="!handleInput.trim()"
-        class="w-full px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-accent/70 focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
+        class="w-full px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-accent/70"
       >

Apply the same change to the create-account button (line 108) and Bluesky button (line 116).

Also applies to: 108-108, 116-116

app/components/Package/ListControls.vue (1)

78-86: Consider updating the select's focus styling for consistency.

The search input above uses the new accent-based focus styling (focus-visible:outline-accent/70), but the sort select still uses focus:(border-border-hover outline-none). While the global CSS in main.css provides select:focus-visible styling, the inline outline-none may override it.

Consider updating the select to match the input's focus styling pattern for visual consistency within this component.

♻️ Suggested change
         <select
           id="package-sort"
           v-model="sortValue"
-          class="appearance-none bg-bg-subtle border border-border rounded-lg ps-3 pe-8 py-2 font-mono text-sm text-fg cursor-pointer transition-colors duration-200 focus:(border-border-hover outline-none) hover:border-border-hover"
+          class="appearance-none bg-bg-subtle border border-border rounded-lg ps-3 pe-8 py-2 font-mono text-sm text-fg cursor-pointer transition-colors duration-200 focus:border-accent focus-visible:(outline-2 outline-accent/70) hover:border-border-hover"
         >
app/components/Package/MetricsBadges.vue (1)

68-71: Consider adding explicit outline width.

The focus styling uses focus-visible:outline-accent/70 which sets the colour, but unlike the previous ring-2, there's no explicit outline width. Depending on the UnoCSS configuration, this may rely on browser defaults or inherited values.

For consistency with other components in this PR (e.g., focus-visible:(outline-2 outline-accent/70) in ListControls.vue), consider adding an explicit width.

♻️ Suggested change
           typesHref
-              ? 'hover:text-fg hover:border-border-hover focus-visible:outline-accent/70'
+              ? 'hover:text-fg hover:border-border-hover focus-visible:(outline-2 outline-accent/70)'
               : '',
app/components/Package/Versions.vue (1)

365-365: Consider using outline instead of text colour for focus indication.

The version link uses focus-visible:text-accent which only changes text colour on focus. This may be less perceivable than an outline, especially for users with colour vision deficiencies. Consider using focus-visible:outline-accent/70 for consistency with other elements in this PR, or keep both for enhanced visibility.

app/composables/useSettings.ts (1)

9-9: Type assumption relies on light/dark key parity.

The type AccentColorId is derived solely from ACCENT_COLORS.light. This works correctly as long as both light and dark objects in ACCENT_COLORS have identical keys. Consider adding a compile-time check or a comment to document this invariant, so future maintainers don't accidentally break it.

app/utils/prehydrate.ts (1)

17-35: Hardcoded colour values must stay synchronised with ACCENT_COLORS.

The prehydrate callback is stringified, so hardcoding is necessary. However, this creates a maintenance burden: any change to ACCENT_COLORS in shared/utils/constants.ts must be manually mirrored here. Consider adding a comment referencing the source of truth, or a build-time/test-time check to detect drift.

📝 Suggested comment
     // Accent colors - hardcoded since ACCENT_COLORS can't be referenced
+    // IMPORTANT: Keep in sync with ACCENT_COLORS in shared/utils/constants.ts

     const colors = {

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: 3

Caution

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

⚠️ Outside diff range comments (1)
app/pages/compare.vue (1)

86-104: ⚠️ Potential issue | 🟡 Minor

Avoid per-button focus-visible utilities; use the global rule instead.

These buttons should inherit the global focus-visible outline. If underline-on-focus is still desired, move it to a global or shared CSS rule rather than inline utilities.

Suggested diff
-            class="text-[10px] transition-colors focus-visible:outline-none focus-visible:underline focus-visible:underline-accent"
+            class="text-[10px] transition-colors"
...
-            class="text-[10px] transition-colors focus-visible:outline-none focus-visible:underline focus-visible:underline-accent"
+            class="text-[10px] transition-colors"

Based on learnings: In the npmx.dev project, focus-visible styling for buttons and selects is applied globally via main.css using button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }. Therefore, individual buttons or selects should not add per-element focus-visible utilities.

🧹 Nitpick comments (6)
app/components/Terminal/Install.vue (1)

124-131: Inline focus-visible:outline-accent/70 is redundant on buttons.

Per project conventions, focus-visible styling for buttons is applied globally via main.css (button:focus-visible { outline: 2px solid var(--accent); ... }). The focus-visible:opacity-100 is correctly needed to make the hidden button visible on keyboard focus, but focus-visible:outline-accent/70 duplicates the global rule with a different opacity value.

This same pattern appears on the run command button (line 188) and create command button (line 236).

♻️ Suggested fix to remove redundant outline class
-            class="px-2 py-0.5 font-mono text-xs text-fg-muted bg-bg-subtle/80 border border-border rounded transition-colors duration-200 opacity-0 group-hover/installcmd:opacity-100 hover:(text-fg border-border-hover) active:scale-95 focus-visible:opacity-100 focus-visible:outline-accent/70"
+            class="px-2 py-0.5 font-mono text-xs text-fg-muted bg-bg-subtle/80 border border-border rounded transition-colors duration-200 opacity-0 group-hover/installcmd:opacity-100 hover:(text-fg border-border-hover) active:scale-95 focus-visible:opacity-100"

Apply similar changes to lines 188 and 236.

Based on learnings: "In the npmx.dev project, focus-visible styling for button and select elements is applied globally via app/assets/main.css... Individual inline utility classes like focus-visible:outline-accent/70 are not needed on these elements."

app/components/ReadmeTocDropdown.vue (1)

148-157: Consider relying on global focus-visible styling for buttons.

This button has an inline focus-visible:outline-accent/70 utility class. Per the project's established pattern, focus-visible styling for buttons is applied globally via main.css with outline: 2px solid var(--accent). The inline utility may conflict with or duplicate the global rule.

If the 70% opacity is intentional for this specific case, consider documenting why it differs from the global standard.

Based on learnings: "In the npmx.dev project, focus-visible styling for button and select elements is applied globally via app/assets/main.css... Do not apply per-element inline utility classes like focus-visible:outline-accent/70 on these elements."

app/pages/search.vue (1)

641-647: Inline focus-visible styling on button may be redundant.

This button and the one at Line 739 have inline focus-visible:outline-accent/70 utilities. Per project conventions, buttons should rely on the global focus-visible rule in main.css rather than per-element inline classes.

Based on learnings: "In the npmx.dev project, focus-visible styling for buttons and selects is applied globally via main.css... individual buttons or selects in Vue components should not rely on inline focus-visible utility classes."

app/components/Org/OperationsQueue.vue (2)

257-263: Inconsistent focus-visible styling on OTP submit button.

This button uses focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/50, which differs from all other buttons in this file that use focus-visible:outline-accent/70. This creates visual inconsistency in focus indicators.

Consider aligning with the other buttons, or if the amber colour is intentional for the OTP context, document why this button requires different treatment.

♻️ Suggested fix for consistency
         <button
           type="submit"
           :disabled="!otpInput.trim() || isExecuting"
-          class="px-3 py-1.5 font-mono text-xs text-bg bg-amber-500 rounded transition-all duration-200 hover:bg-amber-400 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-amber-500/50"
+          class="px-3 py-1.5 font-mono text-xs text-bg bg-amber-500 rounded transition-all duration-200 hover:bg-amber-400 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-accent/70"
         >

277-289: Leftover ring classes on execute button.

Line 281 has both focus-visible:outline-accent/70 and focus-visible:ring-offset-2 focus-visible:ring-offset-bg. The ring-offset classes appear to be remnants from the old ring-based focus styling and have no effect without a corresponding ring utility.

♻️ Remove unused ring-offset classes
         <button
           v-if="hasApprovedOperations && !hasOtpFailures"
           type="button"
           :disabled="isExecuting"
-          class="flex-1 px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-accent/70 focus-visible:ring-offset-2 focus-visible:ring-offset-bg"
+          class="flex-1 px-4 py-2 font-mono text-sm text-bg bg-fg rounded-md transition-all duration-200 hover:bg-fg/90 disabled:opacity-50 disabled:cursor-not-allowed focus-visible:outline-accent/70"
           `@click`="handleExecute()"
         >
app/utils/prehydrate.ts (1)

17-35: Hardcoded colours must stay synchronised with constants.ts.

The colors object here is intentionally hardcoded (as Nuxt stringifies the onPrehydrate callback). Ensure this remains synchronised with ACCENT_COLORS in shared/utils/constants.ts when colours are added or modified.

Consider adding a comment referencing the source of truth to aid future maintenance.

📝 Add synchronisation comment
     // Accent colors - hardcoded since ACCENT_COLORS can't be referenced
+    // Keep in sync with ACCENT_COLORS in shared/utils/constants.ts

     const colors = {

type="button"
class="px-2 py-0.5 font-mono text-xs text-fg-muted bg-bg-subtle/80 border border-border rounded transition-colors duration-200 opacity-0 group-hover/executecmd:opacity-100 hover:(text-fg border-border-hover) active:scale-95 focus-visible:opacity-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-fg/50"
class="px-2 py-0.5 font-mono text-xs text-fg-muted bg-bg-subtle/80 border border-border rounded transition-colors duration-200 opacity-0 group-hover/executecmd:opacity-100 hover:(text-fg border-border-hover) active:scale-95 focus-visible:opacity-100 focus-visible:outline-accent/70"
:aria-label="$t('package.get_started.copy_command')"
Copy link
Member

Choose a reason for hiding this comment

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

I think these type of buttons should announce when copied with an aria-live section somewhere.... (probably beyond the scope of this PR, but just mentioning it as I saw your last commit)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

so that'd be something like this?

  <div aria-live="polite" aria-atomic="true" class="sr-only">
    {{ copiedPkgName ? $t('common.copied') : '' }}
  </div>

my naive guess with quick ai check.

@danielroe
Copy link
Member

just pushed a few changes to apply what you've done to more components, as well as address some clipping issues in the sidebar

would you check to make sure that everything still looks good to you? 🙏❤️

@jellydeck
Copy link
Contributor Author

just pushed a few changes to apply what you've done to more components, as well as address some clipping issues in the sidebar

would you check to make sure that everything still looks good to you? 🙏❤️

all clear 🫡

@danielroe danielroe added this pull request to the merge queue Feb 4, 2026
Merged via the queue into npmx-dev:main with commit ac1171a Feb 4, 2026
16 checks passed
@jellydeck jellydeck deleted the visual-adjustments branch February 4, 2026 09:59
<span
class="relative inline-flex h-6 w-11 shrink-0 items-center rounded-full border-2 transition-colors duration-200 ease-in-out motion-reduce:transition-none cursor-pointer"
:class="checked ? 'bg-accent border-transparent shadow-sm' : 'bg-bg border border-border'"
:class="checked ? 'bg-accent border-transparent shadow-sm' : 'bg-bg border border-border/50'"
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you explain this change? It seems to have just reduced the contrast for this border and I can't work out the upside from it?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

on light mode borders came out looking really harsh, so for a good visual effect modified the opacity. also, It passes accessibility tests.

Copy link
Contributor

Choose a reason for hiding this comment

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

Hmm, on dark mode its pretty borderline to my eyes. Perhaps we can remove the opacity when prefers-contrast: more

Copy link
Contributor Author

Choose a reason for hiding this comment

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

then i suppose it's better without opacity, since there are plans for redesign. what do you think?

Copy link
Contributor

Choose a reason for hiding this comment

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

To my eyes it's better without opacity but I use high contrast mode so I mainly care about what it looks like in that mode. I'm happy to defer on standard mode if it's too contrasty for some, then a contrast-more:border-border should fix override the standard one with opacity.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

have a look at #1011

@garthdw
Copy link
Contributor

garthdw commented Feb 5, 2026

This PR seems to have reverted #881

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.

Download chart tooltip is not visible in light mode Missing focus indicators in banner connect menu [Safari/a11y]

4 participants