Skip to content

Fix/showcase filter blink#424

Open
GabrielaReyna wants to merge 13 commits into
mainfrom
fix/showcase-filter-blink
Open

Fix/showcase filter blink#424
GabrielaReyna wants to merge 13 commits into
mainfrom
fix/showcase-filter-blink

Conversation

@GabrielaReyna

Copy link
Copy Markdown
Contributor

Here's a summary of what was done:

Root cause: The page uses client-side filtering (client:load), so Svelte only applies the URL filter params after the JS bundle hydrates — by which point the browser has already painted the full unfiltered page.

Fix in three layers:

  • Guard script in (packages/astro-theme/layouts/Shell.astro) — An inline script runs synchronously before any body content is rendered. If the URL has ?product= or ?categories= params, it adds a filtering-pending class to . A 3-second failsafe removes it in case hydration fails.
  • CSS hides the section — While filtering-pending is active, both [data-filter-section] (the entire filter bar + grid wrapper) and [data-filter-grid] are set to opacity: 0; pointer-events: none. A transition: opacity 0.35s ease is applied so the reveal is a smooth fade rather than a pop.
  • Reveal after hydration (sites/labs/src/components/ProductFilter.svelte) — Inside onMount, after applyTagFilter() has already hidden the non-matching cards, classList.remove("filtering-pending") is called. The section fades in already in its filtered state.
    The result: when opening a filtered URL, the user sees nothing until the correct filtered view is ready, then it fades in smoothly.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jun 15, 2026

Copy link
Copy Markdown

Deploying labs-browserpod-previews with  Cloudflare Pages  Cloudflare Pages

Latest commit: e4718db
Status: ✅  Deploy successful!
Preview URL: https://eb21a2e9.labs-browserpod-previews.pages.dev
Branch Preview URL: https://fix-showcase-filter-blink.labs-browserpod-previews.pages.dev

View logs

@@ -0,0 +1,4 @@
---
// No-op: filter paint guard is handled in Shell.astro <head>.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This seems completely spurious, probably the results of multiple attempts by the machine. It's introduced in this same PR.

}
</style>
<script is:inline>
// Hide filter grids before first paint when the URL carries a filter param,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I believe this solution will be a source of confusion, things are normally filtered in ProductFilter.svelte, and we apply this clutch here which is completely unrelated.

I can suggest 2 solutions

  1. [Easier and less elegant]: In ProductFilter initialize all the content as "not visible" and during the onMount handler setup visibility correctly.
  2. [Harder but nicer]: I think the reason why onMount is needed here is to have all the DOM element ready since the code fiddles with classes. Another option is to create the elements with the right classes already and use svelte reactivity to update their state. For example, all visible content could be listed in a array and the visibility could be controlled via svelte reactivity depending on the id of the content being present or not in the list.

if (filterPage) {
try {
document.documentElement.classList.add("filtering-pending");
setTimeout(

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I do understand the pattern here, but doing anything on timeouts is a bad practice.

We should reset this styling as the end of the code that actually setup the right final state.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I can actually see below that the style is correctly removed, which makes this operation redundant.

I can guess the machine put it here as a fallback if this snippet apply to some other page, but that does not make sense, we are explicitly filtering on pages that need it.

// has rendered its first filtered state. $effect fires after DOM updates and
// does not run during SSR, so this only executes in the browser.
$effect(() => {
document.documentElement.classList.remove("filtering-pending");

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This should be enough to reset the state. setTimeout is redundant

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.

2 participants