Skip to content

Conversation

@thasmo
Copy link
Contributor

@thasmo thasmo commented Feb 8, 2026

Adapts anchor scrolling across the application.

  • Enable smooth scrolling.
  • Optimize scroll offsets.
  • Use LinkBase or NuxtLink.
  • Remove unneeded JS.

PR title should maybe be: fix: improve anchor scrolling or something.

@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 8, 2026 5:28pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Feb 8, 2026 5:28pm
npmx-lunaria Ignored Ignored Feb 8, 2026 5:28pm

Request Review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 8, 2026

📝 Walkthrough

Walkthrough

Removed the scrollToAnchor utility and all explicit scroll-to-heading logic. Simplified useActiveTocItem to return only { activeId } and removed its scroll-related APIs and cleanup. Updated README/TOC components and section headings to use NuxtLink/LinkBase for in-page anchors; Readme.vue now intercepts relative # hrefs and routes via the router. Replaced explicit CSS scroll-padding/margin with Tailwind utilities (scroll-pt-20 / scroll-mt-20). Added router.options.scrollBehaviorType = 'smooth' to nuxt.config.ts. Tests updated to reflect section id changes.

Possibly related PRs

Suggested reviewers

  • danielroe
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description directly relates to the changeset, describing improvements to anchor scrolling including smooth scrolling, scroll offsets, LinkBase/NuxtLink usage, and removal of unnecessary JavaScript.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 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/ReadmeTocDropdown.vue (1)

128-135: ⚠️ Potential issue | 🟠 Major

Keyboard navigation may not navigate to the selected item.

When pressing Enter, select() is called which closes the dropdown and focuses the trigger, but the actual navigation to the highlighted TOC item doesn't occur. The NuxtLink components handle click-based navigation, but keyboard selection bypasses this.

Consider programmatically navigating to the highlighted item's anchor when Enter is pressed.

🐛 Proposed fix
     case 'Enter': {
       event.preventDefault()
       const item = props.toc[highlightedIndex.value]
       if (item) {
+        navigateTo(`#${item.id}`)
         select()
       }
       break
     }
🧹 Nitpick comments (1)
app/components/CollapsibleSection.vue (1)

85-92: Button focus-visible styling may conflict with global rule.

The button uses inline focus-visible:outline-accent/70 (line 88), but the project has a global focus-visible rule for buttons in main.css. Per the project's coding conventions, buttons should rely on the global rule rather than per-element utilities.

♻️ Suggested fix to remove inline focus-visible utility
         <button
           :id="buttonId"
           type="button"
-          class="w-4 h-4 flex items-center justify-center text-fg-subtle hover:text-fg-muted transition-colors duration-200 shrink-0 focus-visible:outline-accent/70 rounded"
+          class="w-4 h-4 flex items-center justify-center text-fg-subtle hover:text-fg-muted transition-colors duration-200 shrink-0 rounded"
           :aria-expanded="isOpen"

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."

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

@thasmo thasmo force-pushed the native-scroll-anchoring branch 2 times, most recently from 186cc2b to cbceb47 Compare February 8, 2026 16:24
@autofix-ci
Copy link
Contributor

autofix-ci bot commented Feb 8, 2026

Hi! I'm autofix logoautofix.ci, a bot that automatically fixes trivial issues such as code formatting in pull requests.

I would like to apply some automated changes to this pull request, but it looks like I don't have the necessary permissions to do so. To get this pull request into a mergeable state, please do one of the following two things:

  1. Allow edits by maintainers for your pull request, and then re-trigger CI (for example by pushing a new commit).
  2. Manually fix the issues identified for your pull request (see the GitHub Actions output for details on what I would like to change).

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

99-134: ⚠️ Potential issue | 🟠 Major

Enter key no longer activates the highlighted TOC link.

On Line 128, the Enter handler only closes the dropdown, so keyboard users cannot navigate to the highlighted heading. Trigger the corresponding link when Enter is pressed.

Suggested fix
     case 'Enter': {
       event.preventDefault()
       const item = props.toc[highlightedIndex.value]
       if (item) {
-        select()
+        const el = document.getElementById(
+          `${listboxId}-${item.id}`,
+        ) as HTMLAnchorElement | null
+        if (el) {
+          el.click()
+          break
+        }
       }
+      select()
       break
     }

@danielroe
Copy link
Member

would you check the Error: Cannot call text on an empty DOMWrapper. error in component tests? 🙏

@codecov
Copy link

codecov bot commented Feb 8, 2026

Codecov Report

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

Files with missing lines Patch % Lines
app/components/ReadmeTocDropdown.vue 0.00% 3 Missing and 3 partials ⚠️
app/components/Readme.vue 0.00% 3 Missing and 1 partial ⚠️
app/composables/useActiveTocItem.ts 0.00% 2 Missing ⚠️
app/pages/package/[[org]]/[name].vue 0.00% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

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

@thasmo thasmo force-pushed the native-scroll-anchoring branch from 2babad4 to 6584d73 Compare February 8, 2026 17:18
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/ReadmeTocDropdown.vue (1)

99-133: ⚠️ Potential issue | 🟠 Major

Keyboard activation regression + empty TOC guard missing.

Pressing Enter only closes the dropdown, so keyboard users can’t navigate to the highlighted item. Also, when toc is empty, the Arrow key logic can hit modulo-by-zero and produce NaN. Consider routing the Enter action to the highlighted link and short‑circuit when there are no items.

🛠️ Suggested fix
 function handleKeydown(event: KeyboardEvent) {
   if (!isOpen.value) return
 
   const itemCount = props.toc.length
+  if (itemCount === 0) return
 
   switch (event.key) {
     case 'ArrowDown':
       event.preventDefault()
       highlightedIndex.value = (highlightedIndex.value + 1) % itemCount
       break
     case 'ArrowUp':
       event.preventDefault()
       highlightedIndex.value =
         highlightedIndex.value <= 0 ? itemCount - 1 : highlightedIndex.value - 1
       break
     case 'Enter': {
       event.preventDefault()
-      const item = props.toc[highlightedIndex.value]
-      if (item) {
-        select()
-      }
+      if (highlightedIndex.value < 0 || highlightedIndex.value >= itemCount) return
+      const item = props.toc[highlightedIndex.value]
+      if (!item) return
+      const link = document.getElementById(`${listboxId}-${item.id}`)
+      if (link instanceof HTMLElement) {
+        link.click()
+      } else {
+        select()
+      }
       break
     }

@thasmo
Copy link
Contributor Author

thasmo commented Feb 8, 2026

@danielroe, fixed it.

@thasmo thasmo force-pushed the native-scroll-anchoring branch from 6584d73 to d3460dc Compare February 8, 2026 17:26
@danielroe
Copy link
Member

this seems to have broken the mobile styling of the docs/code/compare buttons

@thasmo
Copy link
Contributor Author

thasmo commented Feb 8, 2026

this seems to have broken the mobile styling of the docs/code/compare buttons

Unsure, I'm redeploying because the whole layout is broken again after the last deployment.

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/composables/useActiveTocItem.ts (1)

44-79: ⚠️ Potential issue | 🟠 Major

Track all currently intersecting headings to prevent erratic TOC selection.

The callback only processes entries (changed intersections), which misses headings already visible. When heading B becomes visible closer to the viewport top than already-visible heading A, the code sets activeId to B without checking A's position. Maintain state of all intersecting headings in a Map, updating it on each callback, and derive the active heading from the complete set.

Proposed fix
  let observer: IntersectionObserver | null = null
  const headingElements = new Map<string, Element>()
+  const intersecting = new Map<string, number>()
     // Create observer that triggers when headings cross the top 20% of viewport
     observer = new IntersectionObserver(
       entries => {
-        // Get all visible headings sorted by their position
-        const visibleHeadings: { id: string; top: number }[] = []
-
         for (const entry of entries) {
           if (entry.isIntersecting) {
-            visibleHeadings.push({
-              id: entry.target.id,
-              top: entry.boundingClientRect.top,
-            })
+            intersecting.set(entry.target.id, entry.boundingClientRect.top)
+          } else {
+            intersecting.delete(entry.target.id)
           }
         }
+
+        const visibleHeadings = Array.from(intersecting, ([id, top]) => ({ id, top }))
🧹 Nitpick comments (1)
app/composables/useActiveTocItem.ts (1)

12-111: Consider splitting this composable into smaller helpers.
The function is ~100 lines, making it harder to maintain and reason about. Extracting helpers (e.g., collectHeadingElements, computeActiveId) would keep it within the recommended size and improve clarity.

As per coding guidelines, "Keep functions focused and manageable (generally under 50 lines)".

@thasmo
Copy link
Contributor Author

thasmo commented Feb 8, 2026

@danielroe, a redeployment fixed it?!

@danielroe danielroe added this pull request to the merge queue Feb 8, 2026
Merged via the queue into npmx-dev:main with commit 0c035c8 Feb 8, 2026
17 checks passed
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.

3 participants