Skip to content

fix: core web vitals on mobile IN-1001#1854

Open
gaspergrom wants to merge 1 commit intomainfrom
feat/IN-1001-fix-core-web-vitals-seo
Open

fix: core web vitals on mobile IN-1001#1854
gaspergrom wants to merge 1 commit intomainfrom
feat/IN-1001-fix-core-web-vitals-seo

Conversation

@gaspergrom
Copy link
Copy Markdown
Collaborator

Summary

  • Fix collection detail page CLS on mobile: the header was rendering empty on SSR because currentCollection captured the useQuery ref at setup time (still undefined) with a non-immediate watcher. Use a computed over collection.value and update the query cache from the edit flow so the header renders with real content on SSR (measured drop from CLS 0.28 → ~0.001).
  • Reserve layout during loading on the collection header: hero <img> now has width/height + fetchpriority="high", skeletons mirror the final layout (logo, title, description, meta row) so height is stable during client-side navigation.
  • Fix project header CLS: remove the pageWidth-gated logo size swap (SSR / client mismatch caused header reflow post-hydration) and wrap <lfx-login> in a fixed-width (min-w-24) container so the search bar width doesn't change when the client-only login hydrates.
  • Improve INP: scroll listener is now passive and updates are batched through requestAnimationFrame, so the many components reacting to scrollTop don't do reactive work multiple times per frame.
  • Defer Intercom anonymous boot until after window.load + idle so its widget iframe entrance animation doesn't land inside the CLS measurement window.
  • Add preconnects for avatars.githubusercontent.com / raw.githubusercontent.com where project and collection hero logos are served from.

Changes

  • frontend/app/components/modules/collection/views/collection-details.vue — computed currentCollection, edit flow uses queryClient.setQueryData.
  • frontend/app/components/modules/collection/components/details/header.vue — skeleton matches final layout, hero <img> gets dimensions + priority hint.
  • frontend/app/components/modules/project/components/shared/header.vue — logo size no longer depends on pageWidth.
  • frontend/app/components/shared/layout/menu.vue — fixed-width wrapper around <client-only> login.
  • frontend/app/components/shared/utils/scroll.ts — passive listener + rAF-batched updates.
  • frontend/app/plugins/intercom.ts — defer anonymous boot via requestIdleCallback.
  • frontend/setup/head.ts — preconnect to GitHub avatar origins.

Signed-off-by: Gašper Grom <gasper.grom@gmail.com>
@gaspergrom gaspergrom self-assigned this Apr 22, 2026
Copilot AI review requested due to automatic review settings April 22, 2026 17:06
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR targets Core Web Vitals regressions (CLS/INP) on mobile by stabilizing SSR/hydration output, reserving layout during loading, batching scroll-driven reactive updates, and deferring third-party UI effects.

Changes:

  • Fix SSR header rendering on collection details by making currentCollection reactive via computed state and updating the TanStack query cache after edits.
  • Reduce layout shifts in collection/project headers and the global menu by stabilizing logo/login sizing and improving skeleton/layout reservation.
  • Improve INP by making scroll listeners passive and batching scrollTop updates with requestAnimationFrame; defer Intercom anonymous boot to post-load idle; add GitHub origin preconnects.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
frontend/app/components/modules/collection/views/collection-details.vue Uses computed collection state and updates TanStack cache on edit to avoid SSR empty header.
frontend/app/components/modules/collection/components/details/header.vue Improves layout reservation with skeletons and image dimension hints to reduce CLS.
frontend/app/components/modules/project/components/shared/header.vue Removes page-width-dependent logo sizing to prevent SSR/client mismatch reflow.
frontend/app/components/shared/layout/menu.vue Wraps client-only login in fixed-width container to stabilize header layout pre-hydration.
frontend/app/components/shared/utils/scroll.ts Adds passive scroll listener and rAF batching to reduce per-frame reactive work.
frontend/app/plugins/intercom.ts Defers anonymous Intercom boot until after load + idle to avoid CLS window impact.
frontend/setup/head.ts Adds preconnects for GitHub origins used by hero/logo assets.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 126 to 130
<div
v-if="props.collection?.logoUrl"
class="shrink-0"
v-if="loading || props.collection?.logoUrl"
class="shrink-0 flex items-center justify-start"
:class="scrollTop > 50 ? 'h-8 md:h-10' : 'h-12 md:h-30'"
>
Copy link

Copilot AI Apr 22, 2026

Choose a reason for hiding this comment

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

logoUrl is optional/nullable (see Collection type), but the logo container now renders during loading and disappears when loading completes for collections without a logo. That will shift the title left/right at the end of loading and can introduce CLS for logo-less collections. Consider keeping a stable-width placeholder even when logoUrl is null (e.g., render a fallback icon/placeholder box, or keep the container with visibility: hidden), so the layout doesn’t change between loading and loaded states.

Copilot uses AI. Check for mistakes.
@gaspergrom gaspergrom requested a review from joanagmaia April 22, 2026 17:15
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