Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can we have CSS bundle lazy-loading when page hydrates? #92

Open
Serpentarius13 opened this issue Oct 10, 2024 · 6 comments
Open

Can we have CSS bundle lazy-loading when page hydrates? #92

Serpentarius13 opened this issue Oct 10, 2024 · 6 comments

Comments

@Serpentarius13
Copy link

I have a search button with modal popup that renders search bar, so it is very unnecessary to include multple dozens of css into my initial chunk. I think it should be fairly easy to add ability to lazy load css import. I can submit a PR

@shishkin
Copy link
Owner

I think you can use Astro's client:visible to defer loading of the search component. It should not be responsibility of the search component to know that a website wants to lazy-load it or not. At most I would suggest making CSS import optional.

@Serpentarius13
Copy link
Author

Serpentarius13 commented Oct 10, 2024

I think you can use Astro's client:visible to defer loading of the search component. It should not be responsibility of the search component to know that a website wants to lazy-load it or not. At most I would suggest making CSS import optional.

Yeah, I think optional import would be cool. Then, when I need the styles I may load them myself easily with async import('@styles.css...')

@Serpentarius13
Copy link
Author

Serpentarius13 commented Oct 11, 2024

I'm unable to prevent the component from loading with anything.. I even tried resolving aliases in Vite differently and constructing the component myself.. Client directives do not work on astro component and passing it through children doesn't work because it still loads everything

Edit: okay, the dark side won

document
      .getElementById("search-dialog-trigger")
      ?.addEventListener("click", () => {
        Promise.all([
          //@ts-expect-error
          import("@pagefind/default-ui"),
          import("@pagefind/default-ui/css/ui.css?raw"),
        ]).then(([, { default: css }]) => {
          dialog.showModal();
          const style = document.createElement("style");
          style.textContent = css;
          document.body.appendChild(style);
        });
      });

Edit 2: Which also reduced initial bundle by 60kb in prod

Edit 3: Okay, it did nothing and added load time as the script loads lazily anyway.. But css is bundled up! which is about 2 or 3 kb...

@mseele
Copy link

mseele commented Oct 12, 2024

@Serpentarius13 exactly my use case, too. I do not use the build-in search component and do it lazy in my own component:

---
const bundlePath = `${import.meta.env.BASE_URL}pagefind/`
---

<dialog
  id="search_dialog"
  class="backdrop:overflow-hidden backdrop:backdrop-blur-sm backdrop:scrollbar-none"
>
  <div class="fixed inset-0 flex items-center justify-center">
    <div
      id="content"
      class="h-screen w-screen overflow-hidden bg-white p-8 opacity-95 dark:bg-gray-800 dark:opacity-90 md:h-5/6 md:w-[700px] md:rounded-3xl md:border md:border-gray-100 md:shadow-2xl md:shadow-gray-600/10 md:dark:border-gray-700 md:dark:shadow-none"
    >
      <div
        class:list={['pagefind-init']}
        data-pagefind-ui
        data-bundle-path={bundlePath}
      >
      </div>
    </div>
  </div>
</dialog>

<script>
  document.addEventListener('open-search', async (event) => {
    const dialog = document.querySelector<HTMLDialogElement>('#search_dialog')
    if (dialog) {
      await initPageFind()

      dialog.addEventListener('close', () => {
        document.body.classList.remove('overflow-hidden')
      })

      dialog.addEventListener('click', (event) => {
        const rect = dialog.querySelector('#content')?.getBoundingClientRect()
        if (rect) {
          const isClickOutside = !(
            event.clientX >= rect.left &&
            event.clientX <= rect.right &&
            event.clientY >= rect.top &&
            event.clientY <= rect.bottom
          )
          if (isClickOutside) {
            dialog.close()
          }
        }
      })

      dialog.showModal()
      document.body.classList.add('overflow-hidden')
    }
  })

  async function initPageFind() {
    const { PagefindUI } = await import('@pagefind/default-ui')
    // TODO: import custom css
    const allSelector = '[data-pagefind-ui]'
    for (const el of document.querySelectorAll(`${allSelector}.pagefind-init`)) {
      const elSelector = [
        ...(el.id ? [`#${el.id}`] : []),
        ...[...el.classList.values()].map((c) => `.${c}`),
        allSelector,
      ].join('')
      const bundlePath = el.getAttribute('data-bundle-path')
      new PagefindUI({
        bundlePath,
        element: elSelector,
        showImages: false,
      })
      el.classList.remove('pagefind-init')
    }
  }
</script>

@mseele
Copy link

mseele commented Oct 12, 2024

Just found out that loading a stylesheet dynamically is not possible with astro: withastro/astro#9495 (comment)

I'm now doing it with global styles.

@Serpentarius13
Copy link
Author

Serpentarius13 commented Oct 12, 2024

Just found out that loading a stylesheet dynamically is not possible with astro: withastro/astro#9495 (comment)

I'm now doing it with global styles.

Yeah, well, the thing is that the library incapsulates import itself and although it is possible to override it, it isn't worth it. Also, I have gotten this message now:
image
Although I don't have it on my screen yet, so that means autofocus is also a no-go.
I think its just better to use pagefind integration and build component ourselves in such a use-case :)

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

No branches or pull requests

3 participants