Skip to content

Comments

perf(frontend): improve LCP with critical CSS inlining and async loading#255

Merged
test3207 merged 2 commits intomainfrom
perf/improve-lcp
Dec 21, 2025
Merged

perf(frontend): improve LCP with critical CSS inlining and async loading#255
test3207 merged 2 commits intomainfrom
perf/improve-lcp

Conversation

@test3207
Copy link
Owner

Summary

Improves Largest Contentful Paint (LCP) by implementing critical CSS inlining and async stylesheet loading.

Closes #227

Changes

1. Critical CSS Inlining (index.html)

  • Added inline <style> block with minimal critical CSS:
    • Basic reset (box-sizing, margin)
    • System font stack for instant text rendering
    • Loading spinner styles matching PageLoader component
    • Dark mode support via prefers-color-scheme media query
  • Added pre-rendered loading spinner in HTML (visible before JS loads)

2. Async CSS Loading (vite.config.ts)

  • Created asyncCssPlugin Vite plugin
  • Converts <link rel="stylesheet"> to non-blocking async loading
  • Uses the media="print" onload="this.media='all'" technique
  • CSS loads in background while critical inline styles enable immediate paint

3. Resource Prioritization

  • Moved viewport meta before link elements
  • Favicon loads after critical meta tags

LCP Improvement Strategy

Before After
CSS blocks render CSS loads async
White screen until CSS loads Spinner visible immediately
JS must load before any content Content shows with inline CSS

Technical Details

The async CSS technique:

<link rel="stylesheet" href="..." media="print" onload="this.media='all'">
  • media="print" prevents render-blocking (browser doesn't wait for print stylesheets)
  • onload switches to media="all" when loaded, applying styles
  • Well-supported across all modern browsers
  • No JavaScript dependency for initial render

Testing

  • npm run build - Success
  • npm run lint - Pass
  • Built HTML contains inline critical CSS
  • Built HTML has async stylesheet loading
  • Loading spinner visible in initial HTML

Combined with PR #254

This PR works together with PR #254 (bundle optimization) which:

  • Reduced main bundle from 629KB → 383KB (-39%)
  • Added lazy loading for overlays (FullPlayer, PlayQueueDrawer, etc.)
  • Implemented dynamic import for offline proxy module

Together, these changes should significantly improve LCP from the baseline 9.4s.

- Add inline critical CSS for immediate visual feedback (loading spinner)
- Add asyncCssPlugin to convert stylesheet loading to non-blocking
- Add dark mode support in inline critical CSS
- Move favicon link after viewport meta for better resource prioritization

Addresses: #227
Copilot AI review requested due to automatic review settings December 21, 2025 12:12
Copy link

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 implements performance optimizations to improve Largest Contentful Paint (LCP) by introducing critical CSS inlining and async stylesheet loading. The changes enable instant visual feedback through a pre-rendered loading spinner while the full application loads.

Key Changes:

  • Added inline critical CSS in index.html with loading spinner styles and dark mode support
  • Created asyncCssPlugin in vite.config.ts to convert blocking stylesheets to async loading via the media="print" technique
  • Added pre-rendered loading spinner HTML to provide immediate visual feedback before React hydration

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 5 comments.

File Description
frontend/vite.config.ts Adds custom Vite plugin to transform stylesheet links for async loading using the print media trick
frontend/index.html Adds inline critical CSS with loading spinner styles, reorders meta tags, and includes pre-rendered spinner HTML in root element

- Add validation for href value before transformation
- Skip links with existing media attribute to avoid duplicates
- Use CSS variables aligned with Tailwind theme (gray-900, gray-50)
- Fix misleading comment about JavaScript requirement
@github-actions
Copy link
Contributor

Coverage after merging perf/improve-lcp into main will be

78.72%

Coverage Report
FileStmtsBranchesFuncsLinesUncovered Lines
100%100%100%100%
src/lib
   events.ts100%100%100%100%
   logger-client.ts82.61%60%100%100%15, 21, 27, 8
src/lib/audio
   media-session.ts74.84%90.91%71.43%65.12%105–106, 124, 135–137, 144–146, 148, 155–157, 159, 167–169, 171, 178–180, 182, 198, 212, 226, 237, 245–246, 259, 44–45, 76, 86–87, 93
   queue.ts86.15%82.54%92%86.92%210–211, 223, 225, 225, 225–226, 228, 249–252, 303, 313, 313, 313–314, 316, 37–38, 54–55, 72, 76–77
src/lib/offline-proxy/routes
   libraries.ts42.57%24.32%66.67%51.72%122–123, 208, 248, 261–263, 266, 268, 268, 268–269, 278, 278, 278, 278, 278–279, 288–289, 291, 291, 291–292, 296, 299–300, 303, 305, 305, 305, 307, 311, 311–312, 312, 316, 320, 323, 326, 326, 326–327, 338, 341, 341, 341, 341, 341–342, 344, 349, 353, 356, 358, 361, 361–362, 362–363, 363–364, 364–365, 365–366, 366, 366, 366–367, 367–368, 368, 370, 370, 370, 370, 373, 373, 385–386, 389, 394, 394, 394–396, 396, 402, 412–413, 419–422, 422, 422, 424, 426, 426, 426, 426, 426–427, 437, 440, 442, 447, 61, 90, 90–91
   player.ts47.26%35%57.14%55.70%101, 137, 149–150, 153, 158, 160, 164, 166, 166, 166–168, 170, 170, 170–171, 179, 179–180, 180, 194, 199–200, 206–207, 207, 207–208, 216, 216–217, 217, 230, 235, 258, 279–282, 293, 305–308, 310, 312, 312, 312–313, 313, 313–314, 314, 314–315, 315, 315, 319, 321, 326, 56–58, 58, 58, 58, 58–59, 63–64, 71, 73, 93
   playlists.ts86.27%92.31%91.67%84.35%100, 119, 136, 153, 173, 187, 193–194, 213, 226, 232, 57, 63–68, 70, 87
   songs.ts89.66%75%100%94.87%102, 104, 111, 113, 139–140
src/lib/offline-proxy/utils
   auth.ts93.33%100%100%89.47%70–71
   index.ts100%100%100%100%
   sorting.ts100%100%100%100%
src/lib/storage
   storage-constants.ts100%100%100%100%
src/lib/utils
   audio-filter.ts100%100%100%100%
   defaults.ts100%100%100%100%
   format-duration.ts100%100%100%100%
   format.ts100%100%100%100%
   hash.ts88.24%100%100%85.19%64–65, 87–88
   uuid.ts90%87.50%83.33%93.75%31–32
src/locales
   i18n.ts95.45%87.50%100%97.37%23, 40–41

Copy link

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

Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.

@test3207 test3207 merged commit 9b711a0 into main Dec 21, 2025
10 checks passed
@test3207 test3207 deleted the perf/improve-lcp branch December 21, 2025 12:52
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.

Improve LCP from 9.4s to < 2.5s

1 participant