Skip to content

fix: refine compare grid header layout#2072

Open
mihaizaurus wants to merge 1 commit intonpmx-dev:mainfrom
mihaizaurus:compare-grid-header-layout
Open

fix: refine compare grid header layout#2072
mihaizaurus wants to merge 1 commit intonpmx-dev:mainfrom
mihaizaurus:compare-grid-header-layout

Conversation

@mihaizaurus
Copy link

🔗 Linked issue

Resolves #1627

🧭 Context

This change updates the compare grid header layout so long package names and versions behave more predictably in 2/3/4-package comparisons, ensuring coherent visuals across all columns.

📚 Description

With this change I have:

  • replaced the hardcoded columns-2/3/4 layout classes with a single generic grid definition using --package-count
  • removed the old hardcoded min-width classes from the grid container
  • updated the header cell markup so package name and version render on separate lines, each with clamping
  • kept the full name@version in the link title attribute
  • added min-w-0 to the header cell chain so long labels can shrink instead of distorting the layout
  • updated compare grid tests to match the new layout contract

@vercel
Copy link

vercel bot commented Mar 13, 2026

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

Project Deployment Actions Updated (UTC)
npmx.dev Ready Ready Preview, Comment Mar 13, 2026 11:43pm
2 Skipped Deployments
Project Deployment Actions Updated (UTC)
docs.npmx.dev Ignored Ignored Preview Mar 13, 2026 11:43pm
npmx-lunaria Ignored Ignored Mar 13, 2026 11:43pm

Request Review

@codecov
Copy link

codecov bot commented Mar 13, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ All tests successful. No failed tests found.

📢 Thoughts on this report? Let us know!

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 13, 2026

📝 Walkthrough

Walkthrough

The ComparisonGrid component was refactored to use a single CSS variable (--package-count) on the grid container to define package columns via grid-template-columns instead of conditional width and per-column utility classes. Header rendering changed from inline text to a vertical layout: header link now contains separate stacked elements for package name and optional version (each independently clamped), with a tooltip icon aligned to the bottom. Tests were updated to assert the presence of the title attribute, the separate name/version elements, and that --package-count matches the number of package columns.

Possibly related PRs

Suggested labels

front

Suggested reviewers

  • graphieros
  • danielroe
  • alexdln
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR addresses the header layout aspects of issue #1627 by constraining long package versions and preventing overlaps through separate line rendering with clamping. However, wider viewport and complete layout consistency requirements remain unaddressed. Verify whether the separate line clamping for package names and versions fully resolves the horizontal scrolling concerns in 4-package comparisons, and confirm if viewport width changes from issue #1627 are addressed elsewhere or in follow-up PRs.
✅ Passed checks (2 passed)
Check name Status Explanation
Description check ✅ Passed The pull request description accurately describes the implemented changes to the compare grid header layout, including CSS variable usage, markup restructuring, and test updates.
Out of Scope Changes check ✅ Passed All changes directly support the header layout refactoring objective. The CSS variable approach, line-separated rendering, test updates, and selector modifications are all within scope of addressing #1627's header layout concerns.

✏️ 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
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Tip

CodeRabbit can use Trivy to scan for security misconfigurations and secrets in Infrastructure as Code files.

Add a .trivyignore file to your project to customize which findings Trivy reports.

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)
test/nuxt/components/compare/ComparisonGrid.spec.ts (1)

63-103: Consider adding a direct no-overflow assertion for the 4-column case.

Current checks confirm --package-count, but they do not directly guard the issue goal (preventing horizontal scroll). A browser-layout assertion (scrollWidth <= clientWidth) would make this regression-proof.

Possible test addition
+    it('does not horizontally overflow with four package columns', async () => {
+      const component = await mountSuspended(ComparisonGrid, {
+        props: {
+          columns: cols('very-long-package-name-a@1.0.0', 'very-long-package-name-b@1.0.0', 'very-long-package-name-c@1.0.0', 'very-long-package-name-d@1.0.0'),
+        },
+      })
+
+      const gridEl = component.find('.comparison-grid').element as HTMLElement
+      expect(gridEl.scrollWidth).toBeLessThanOrEqual(gridEl.clientWidth)
+    })

Based on learnings: in test/nuxt/, tests run in Vitest browser mode with Playwright, so scrollWidth/clientWidth assertions are reliable for layout checks.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b691b81d-3839-46ba-ac26-59668f1bf8df

📥 Commits

Reviewing files that changed from the base of the PR and between 9e8c805 and 4171f59.

📒 Files selected for processing (2)
  • app/components/Compare/ComparisonGrid.vue
  • test/nuxt/components/compare/ComparisonGrid.spec.ts

Comment on lines +38 to +40
v-for="col in columns"
:key="col.name"
class="comparison-cell comparison-cell-header min-w-0"
Copy link
Contributor

@coderabbitai coderabbitai bot Mar 13, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Use a unique v-for key for versioned comparisons.

:key="col.name" can collide when two columns share the same package name but differ by version, causing unstable DOM reuse on updates.

Suggested fix
-          :key="col.name"
+          :key="`${col.name}@${col.version ?? ''}`"
📝 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
v-for="col in columns"
:key="col.name"
class="comparison-cell comparison-cell-header min-w-0"
v-for="col in columns"
:key="`${col.name}@${col.version ?? ''}`"
class="comparison-cell comparison-cell-header min-w-0"

Copy link
Author

Choose a reason for hiding this comment

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

My understanding is that the PackageSelector.vue already prevents duplicate package names before it gets to the Comparison Grid.

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find PackageSelector.vue
fd -t f 'PackageSelector.vue'

Length of output: 42


🏁 Script executed:

#!/bin/bash
# Look for duplicate prevention logic in PackageSelector.vue
rg -n "duplicate|already|exist|includes|has\b|filter|unique|Set" app/components/Compare/PackageSelector.vue -A2 -B2

Length of output: 2175


@mihaizaurus, you're correct. Looking at app/components/Compare/PackageSelector.vue, there are two independent guards that make duplicate names impossible:

  1. filteredResults computed — already-selected package names are filtered out of the typeahead dropdown before they can be chosen again (Line 59: .filter(r => !packages.value.includes(r.name))).
  2. addPackage guard — an explicit early-return (Line 82: if (packages.value.includes(name)) return) prevents adding a name that's already present.

Since columns can never contain two entries with the same col.name, :key="col.name" is safe and my suggestion was unwarranted here. Apologies for the noise!


✏️ Learnings added
Learnt from: mihaizaurus
Repo: npmx-dev/npmx.dev PR: 2072
File: app/components/Compare/ComparisonGrid.vue:38-40
Timestamp: 2026-03-14T08:57:22.084Z
Learning: In `app/components/Compare/ComparisonGrid.vue`, using `:key="col.name"` in the `v-for` over columns is safe and correct. `app/components/Compare/PackageSelector.vue` enforces two independent deduplication guards that make it impossible for duplicate package names to reach the grid: (1) `filteredResults` filters out already-selected names from the typeahead, and (2) `addPackage` has an explicit `if (packages.value.includes(name)) return` guard. Do not flag `:key="col.name"` as a potential collision risk.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: skaldebane
Repo: npmx-dev/npmx.dev PR: 760
File: app/components/Compare/FacetCard.vue:134-136
Timestamp: 2026-02-06T07:14:04.242Z
Learning: In `app/components/Compare/FacetCard.vue`, the facet values use `dir="auto"` rather than `dir="ltr"` because the values are not always numbers/sizes and can contain mixed content types.

Learnt from: graphieros
Repo: npmx-dev/npmx.dev PR: 2032
File: app/components/Package/TrendsChart.vue:1356-1361
Timestamp: 2026-03-13T12:43:11.121Z
Learning: In `app/components/Package/TrendsChart.vue`, the asymmetry between `chartHeight` and the wrapper height class is intentional. `chartHeight` only reduces (from 600 to 494) when `showCorrectionControls.value && props.inModal` is true, but the wrapper height class (Lines ~1857-1863) compacts from `h-[567px]` to `h-[491px]` whenever `showCorrectionControls` is true regardless of `props.inModal`. Do not flag this as a bug or suggest aligning both conditions to `shouldCompactChart`.

Learnt from: jellydeck
Repo: npmx-dev/npmx.dev PR: 904
File: app/components/Package/AccessControls.vue:253-253
Timestamp: 2026-02-04T05:34:20.527Z
Learning: In the npmx.dev project, ensure that focus-visible styling for button and select elements is implemented globally in app/assets/main.css with the rule: button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }. Do not apply per-element inline utility classes like focus-visible:outline-accent/70 on these elements in Vue templates or components (e.g., AccessControls.vue). Rely on the global rule for consistency and maintainability; only use inline focus-visible utilities when styling non-button/select elements or in exceptional cases outside the global scope.

Learnt from: jellydeck
Repo: npmx-dev/npmx.dev PR: 904
File: app/components/Package/Versions.vue:332-332
Timestamp: 2026-02-04T05:34:54.335Z
Learning: In the npmx.dev project, focus-visible styling for buttons and selects is applied globally via main.css with: button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }. Therefore, individual buttons or selects in Vue components should not rely on inline focus-visible utility classes like focus-visible:outline-accent/70. Ensure components do not add per-element focus-visible utilities; rely on the global rule. If a component seems to require a different focus state, adjust only through global CSS or maintain accessibility with native focus styles, rather than inserting inline utility classes.

Learnt from: userquin
Repo: npmx-dev/npmx.dev PR: 1170
File: app/components/Package/MetricsBadges.vue:66-66
Timestamp: 2026-02-08T15:02:02.232Z
Learning: In Vue components that use UnoCSS with the preset-icons collection, prefer colon-syntax for icons (e.g., i-carbon:checkmark) over the dash-separated form (i-carbon-checkmark). This aids UnoCSS in resolving the correct collection directly, which improves performance for long icon names. Apply this pattern to all Vue components (e.g., app/components/**/*.vue) where UnoCSS icons are used; ensure UnoCSS is configured with the preset-icons collection.

Learnt from: userquin
Repo: npmx-dev/npmx.dev PR: 1335
File: app/components/Compare/FacetSelector.vue:72-78
Timestamp: 2026-02-10T15:47:33.467Z
Learning: In the npmx.dev project, ButtonBase (used via app/components/ButtonBase.vue or similar) provides default classes: border border-border. When styling ButtonBase instances in Vue components (e.g., app/components/Compare/FacetSelector.vue and other files under app/components), avoid duplicating the border class to prevent the HTML validator error no-dup-class and CI failures. If styling overrides are needed, ensure only unique classes are applied (remove redundant border classes or adjust via props) so the default border remains intact without duplication.

Learnt from: abbeyperini
Repo: npmx-dev/npmx.dev PR: 1049
File: app/components/Settings/Toggle.client.vue:22-29
Timestamp: 2026-02-11T00:01:33.121Z
Learning: In Vue 3.4 and later, you can use same-name shorthand for attribute bindings: use :attributeName instead of :attributeName="attributeName" when binding to a variable with the same name in scope. For example, :id is equivalent to :id="id" when an id variable exists. Apply this shorthand in .vue components (notably in Settings/Toggle.client.vue) to simplify templates. Ensure the bound variable exists and that you are using a Vue version that supports this shorthand.

Learnt from: alexdln
Repo: npmx-dev/npmx.dev PR: 1845
File: app/components/InstantSearch.vue:6-11
Timestamp: 2026-03-03T09:42:52.533Z
Learning: Maintain the established prehydration pattern across the project: use JSON.parse(localStorage.getItem('npmx-settings') || '{}') without per-call try-catch blocks. Do not introduce try-catch error handling for this pattern unless a coordinated, project-wide refactor of all onPrehydrate readers is planned and executed.

Learnt from: graphieros
Repo: npmx-dev/npmx.dev PR: 1974
File: app/components/Compare/FacetBarChart.vue:174-178
Timestamp: 2026-03-07T12:00:18.120Z
Learning: When reviewing Compare/FacetBarChart.vue (or other Vue components in app/components), treat config.datapoint as a runtime object that can include extra properties from the dataset item (e.g., formattedValue, custom metadata). The TypeScript typings for config.datapoint may be incomplete, but that should not be flagged as a runtime error. Base reviews on actual runtime behavior and data presence, not on missing type checks; focus on correct usage and data-driven effects.

@mihaizaurus mihaizaurus force-pushed the compare-grid-header-layout branch from 4171f59 to e784d3a Compare March 13, 2026 23:41
@mihaizaurus mihaizaurus changed the title Refine compare grid header layout fix: Refine compare grid header layout Mar 13, 2026
@mihaizaurus mihaizaurus changed the title fix: Refine compare grid header layout fix: refine compare grid header layout Mar 13, 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: 1

♻️ Duplicate comments (1)
app/components/Compare/ComparisonGrid.vue (1)

37-40: ⚠️ Potential issue | 🟡 Minor

Use a version-aware v-for key.

Line 39 uses :key="col.name", which can collide when the same package is compared at different versions, causing unstable node reuse.

Proposed fix
-          :key="col.name"
+          :key="`${col.name}@${col.version ?? ''}`"
🧹 Nitpick comments (1)
test/nuxt/components/compare/ComparisonGrid.spec.ts (1)

95-103: Remove the duplicate --package-count test.

This case overlaps with the earlier --package-count assertions and doesn’t add new coverage.

Suggested simplification
-    it('sets --package-count CSS variable', async () => {
-      const component = await mountSuspended(ComparisonGrid, {
-        props: {
-          columns: cols('a', 'b', 'c'),
-        },
-      })
-      const grid = component.find('.comparison-grid')
-      expect(grid.attributes('style')).toContain('--package-count: 3')
-    })

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 47d1a78a-9dc4-4622-9a70-ce322356192b

📥 Commits

Reviewing files that changed from the base of the PR and between 4171f59 and e784d3a.

📒 Files selected for processing (3)
  • app/components/Compare/ComparisonGrid.vue
  • test/e2e/interactions.spec.ts
  • test/nuxt/components/compare/ComparisonGrid.spec.ts

Comment on lines 43 to 47
<LinkBase
:to="packageRoute(col.name, col.version)"
class="text-sm truncate"
block
class="flex min-w-0 flex-col items-center text-center text-sm"
:title="col.version ? `${col.name}@${col.version}` : col.name"
>
Copy link
Contributor

@coderabbitai coderabbitai bot Mar 13, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

title may not be forwarded to the rendered anchor.

Line 46 passes :title to LinkBase, but app/components/Link/Base.vue currently forwards only declared props via v-bind="props" and does not declare title. That can drop the attribute and break this PR’s tooltip/title contract.

Proposed fix (in app/components/Link/Base.vue)
 const props = withDefaults(
   defineProps<{
+    /** Native title attribute forwarded to the underlying link element */
+    title?: string
     /** Disabled links will be displayed as plain text */
     disabled?: boolean
     ...
   }>(),
   { variant: 'link', size: 'medium' },
 )

Copy link
Author

Choose a reason for hiding this comment

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

Searching for the LinkBase in other components, I notice the same pattern in a few packages like Package/Header.vue, Package/Dependencies.vue and the page for [[org]]/[name].vue. If the risk is really Major, it would impact those areas as well and would indeed need to be strengthened.

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check the full LinkBase component
fd -e vue "Base.vue" --search-path app/components/Link/ | xargs cat

# Also check for inheritAttrs usage
rg -n "inheritAttrs" app/components/Link/

# Check title attribute usage in the mentioned files
rg -n ":title" app/components/Package/Header.vue app/components/Package/Dependencies.vue
rg -n ":title" app/pages/ --type vue

Length of output: 5751


@mihaizaurus, you're right to connect the dots — the concern is real and does affect all those locations.

Here's why: app/components/Link/Base.vue renders a v-if/v-else pair at the template root (a <span> when disabled, a <NuxtLink> otherwise). Vue 3 treats this as a fragment, and fragments do not support automatic fallthrough attribute inheritance — Vue will silently drop title (or emit a runtime warning in dev mode). Additionally, the <NuxtLink> uses v-bind="props", which only forwards explicitly declared props, and title is not one of them.

So every call site that passes :title to LinkBase — whether in ComparisonGrid.vue, Package/Header.vue, Package/Dependencies.vue, or [[org]]/[name].vue — currently has the attribute silently dropped.

The cleanest fix is to declare title as a prop and forward it explicitly on both branches:

// app/components/Link/Base.vue

 const props = withDefaults(
   defineProps<{
+    /** Native title attribute (tooltip) forwarded to the underlying element */
+    title?: string
     /** Disabled links will be displayed as plain text */
     disabled?: boolean
     ...
   }>(),
   { variant: 'link', size: 'medium' },
 )

And in the template, add :title="title" to both the <span> (disabled branch) and ensure it flows through v-bind="props" to <NuxtLink> (it will automatically once declared as a prop):

-  <span v-if="disabled" ...>
+  <span v-if="disabled" :title="title" ...>

The <NuxtLink> branch already uses v-bind="props", so declaring title as a prop is sufficient for it — no additional change needed there.

Would you like me to open a follow-up issue to track fixing LinkBase globally, or would you prefer to address it in this PR since it directly affects the contract this PR relies on?


✏️ Learnings added
Learnt from: mihaizaurus
Repo: npmx-dev/npmx.dev PR: 2072
File: app/components/Compare/ComparisonGrid.vue:43-47
Timestamp: 2026-03-14T09:05:41.326Z
Learning: In `app/components/Link/Base.vue` (npmx.dev project), the component renders a `v-if`/`v-else` fragment (disabled `<span>` vs `<NuxtLink>`), which means Vue 3 does NOT automatically apply fallthrough attributes. `v-bind="props"` on `<NuxtLink>` only forwards declared props. Therefore, any HTML attribute like `title` passed to `LinkBase` must be explicitly declared as a prop in `defineProps` and manually forwarded to both template branches (`:title="title"` on the `<span>`, and automatically via `v-bind="props"` on `<NuxtLink>` once declared). This affects all usages across the codebase including `Package/Header.vue`, `Package/Dependencies.vue`, and `Compare/ComparisonGrid.vue`.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: mihaizaurus
Repo: npmx-dev/npmx.dev PR: 2072
File: app/components/Compare/ComparisonGrid.vue:38-40
Timestamp: 2026-03-14T08:57:22.084Z
Learning: In Vue components, when using v-for, provide a stable unique key (e.g., :key="col.name"). This learning notes that in app/components/Compare/ComparisonGrid.vue, using :key="col.name" is safe because duplicates are prevented upstream: app/components/Compare/PackageSelector.vue enforces two guards (filteredResults excludes already-selected names; addPackage early-returns if the name already exists). Do not flag :key="col.name" as a collision risk in this context. If applying this guideline elsewhere, ensure a unique key is guaranteed by logic or consider fallbacks (e.g., index or composite keys) only if duplicates could be introduced.

Learnt from: skaldebane
Repo: npmx-dev/npmx.dev PR: 760
File: app/components/Compare/FacetCard.vue:134-136
Timestamp: 2026-02-06T07:14:04.242Z
Learning: In `app/components/Compare/FacetCard.vue`, the facet values use `dir="auto"` rather than `dir="ltr"` because the values are not always numbers/sizes and can contain mixed content types.

Learnt from: alexdln
Repo: npmx-dev/npmx.dev PR: 838
File: app/pages/package/[...package].vue:445-449
Timestamp: 2026-02-03T13:59:33.392Z
Learning: The copy button pattern in app/pages/package/[...package].vue may be made into a reusable component or pattern in the future, but currently it's acceptable to keep it inline with the CSS-only approach for smooth animations.

Learnt from: graphieros
Repo: npmx-dev/npmx.dev PR: 2032
File: app/components/Package/TrendsChart.vue:1356-1361
Timestamp: 2026-03-13T12:43:11.121Z
Learning: In `app/components/Package/TrendsChart.vue`, the asymmetry between `chartHeight` and the wrapper height class is intentional. `chartHeight` only reduces (from 600 to 494) when `showCorrectionControls.value && props.inModal` is true, but the wrapper height class (Lines ~1857-1863) compacts from `h-[567px]` to `h-[491px]` whenever `showCorrectionControls` is true regardless of `props.inModal`. Do not flag this as a bug or suggest aligning both conditions to `shouldCompactChart`.

Learnt from: jellydeck
Repo: npmx-dev/npmx.dev PR: 904
File: app/components/Package/AccessControls.vue:253-253
Timestamp: 2026-02-04T05:34:20.527Z
Learning: In the npmx.dev project, ensure that focus-visible styling for button and select elements is implemented globally in app/assets/main.css with the rule: button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }. Do not apply per-element inline utility classes like focus-visible:outline-accent/70 on these elements in Vue templates or components (e.g., AccessControls.vue). Rely on the global rule for consistency and maintainability; only use inline focus-visible utilities when styling non-button/select elements or in exceptional cases outside the global scope.

Learnt from: jellydeck
Repo: npmx-dev/npmx.dev PR: 904
File: app/components/Package/Versions.vue:332-332
Timestamp: 2026-02-04T05:34:54.335Z
Learning: In the npmx.dev project, focus-visible styling for buttons and selects is applied globally via main.css with: button:focus-visible, select:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }. Therefore, individual buttons or selects in Vue components should not rely on inline focus-visible utility classes like focus-visible:outline-accent/70. Ensure components do not add per-element focus-visible utilities; rely on the global rule. If a component seems to require a different focus state, adjust only through global CSS or maintain accessibility with native focus styles, rather than inserting inline utility classes.

Learnt from: userquin
Repo: npmx-dev/npmx.dev PR: 1170
File: app/components/Package/MetricsBadges.vue:66-66
Timestamp: 2026-02-08T15:02:02.232Z
Learning: In Vue components that use UnoCSS with the preset-icons collection, prefer colon-syntax for icons (e.g., i-carbon:checkmark) over the dash-separated form (i-carbon-checkmark). This aids UnoCSS in resolving the correct collection directly, which improves performance for long icon names. Apply this pattern to all Vue components (e.g., app/components/**/*.vue) where UnoCSS icons are used; ensure UnoCSS is configured with the preset-icons collection.

Learnt from: userquin
Repo: npmx-dev/npmx.dev PR: 1335
File: app/components/Compare/FacetSelector.vue:72-78
Timestamp: 2026-02-10T15:47:33.467Z
Learning: In the npmx.dev project, ButtonBase (used via app/components/ButtonBase.vue or similar) provides default classes: border border-border. When styling ButtonBase instances in Vue components (e.g., app/components/Compare/FacetSelector.vue and other files under app/components), avoid duplicating the border class to prevent the HTML validator error no-dup-class and CI failures. If styling overrides are needed, ensure only unique classes are applied (remove redundant border classes or adjust via props) so the default border remains intact without duplication.

Learnt from: abbeyperini
Repo: npmx-dev/npmx.dev PR: 1049
File: app/components/Settings/Toggle.client.vue:22-29
Timestamp: 2026-02-11T00:01:33.121Z
Learning: In Vue 3.4 and later, you can use same-name shorthand for attribute bindings: use :attributeName instead of :attributeName="attributeName" when binding to a variable with the same name in scope. For example, :id is equivalent to :id="id" when an id variable exists. Apply this shorthand in .vue components (notably in Settings/Toggle.client.vue) to simplify templates. Ensure the bound variable exists and that you are using a Vue version that supports this shorthand.

Learnt from: alexdln
Repo: npmx-dev/npmx.dev PR: 1845
File: app/components/InstantSearch.vue:6-11
Timestamp: 2026-03-03T09:42:52.533Z
Learning: Maintain the established prehydration pattern across the project: use JSON.parse(localStorage.getItem('npmx-settings') || '{}') without per-call try-catch blocks. Do not introduce try-catch error handling for this pattern unless a coordinated, project-wide refactor of all onPrehydrate readers is planned and executed.

Learnt from: graphieros
Repo: npmx-dev/npmx.dev PR: 1974
File: app/components/Compare/FacetBarChart.vue:174-178
Timestamp: 2026-03-07T12:00:18.120Z
Learning: When reviewing Compare/FacetBarChart.vue (or other Vue components in app/components), treat config.datapoint as a runtime object that can include extra properties from the dataset item (e.g., formattedValue, custom metadata). The TypeScript typings for config.datapoint may be incomplete, but that should not be flagged as a runtime error. Base reviews on actual runtime behavior and data presence, not on missing type checks; focus on correct usage and data-driven effects.

@graphieros graphieros requested review from alexdln and serhalp March 14, 2026 05:33
@graphieros
Copy link
Contributor

graphieros commented Mar 14, 2026

Thank you @mihaizaurus :)

I think this is a clear improvement vs the current layout when comparing 4 packages.

There have been talks about potentially increasing the max number of compared packages, which would probably require other adaptations for the table (sticky first col, etc), but this is probably for a later iteration.

You might want to check / address the coderabbit's remarks.

@mihaizaurus
Copy link
Author

I left comments on both, it also responded with the first minor issue not being required, the second one requiring a bigger discussion. I can tackle that change as well but it felt a bit beyond the scope of this specific issue as it would impact other components that use the Link

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.

Visual improvements for Compare page

2 participants