Skip to content

feat: Video covers support + fetchpriority tuning for LCP covers#1787

Open
Void4m0n wants to merge 10 commits intojerryc127:devfrom
Void4m0n:feat/allow-videos-for-all-covers
Open

feat: Video covers support + fetchpriority tuning for LCP covers#1787
Void4m0n wants to merge 10 commits intojerryc127:devfrom
Void4m0n:feat/allow-videos-for-all-covers

Conversation

@Void4m0n
Copy link
Contributor

PR Context

Hi! I know this PR doesn’t align with the simplicity-first direction Jerry wants to follow as stated on #1774 (comment). I implemented it for my own blog and I’m sharing it in case it is useful to others. Jerry, feel free to close the PR.

This PR is already implemented on my personal blog https://void4m0n.com/

Motivation

I wanted to add more dynamism to my blog. Since Butterfly didn’t support video covers, I initially used GIFs, but they are not optimal for performance or user experience. Using video instead of GIF is generally recommended, so I implemented video cover support across the theme’s cover slots.

Separately, I noticed that with Lazy mode enabled, some above-the-fold covers (part of the LCP) were still treated as lazy-loaded, negatively impacting Web Core Vitals. This PR also adds a small configuration option to prioritize the first N covers.

Video cover support and fallbacks

What changed

This PR extends the existing cover system to support video in addition to the current behavior (img / CSS background string), across:

  • cover (main page)
  • pagination_cover (post page)
  • related_post (post page)
  • recent_post (main page)
  • article_sort (articles page)

Supported video formats (auto-detected)

Video support is auto-detected from the cover value (no manual type selection):

  • .mp4 and .webm

When detected as video, the theme renders <video><source></video> and sets cover_mime automatically:

  • .webm -> video/webm
  • otherwise -> video/mp4

Configurable video parameters (YAML examples)

Video rendering always includes:

  • muted, playsinline

autoplay, loop & poster are User-configurable parameters provided via *_video_parameters objects. Example (main cover):

cover: intro.mp4
cover_video_parameters:
  autoplay: true
  loop: true
  poster: intro-poster.web

Section-specific examples:

pagination_cover: pagination.mp4
pagination_video_parameters:
  autoplay: true
  loop: false
  poster: pagination-poster.webp

recent_post_cover: recent.mp4
recent_post_video_parameters:
  autoplay: false
  loop: true
  poster: recent-poster.webp

article_sort_cover: sort.mp4
article_sort_video_parameters:
  autoplay: true
  loop: true
  poster: sort-poster.webp

related_post_cover: related.mp4
related_post_video_parameters:
  autoplay: false
  loop: true
  poster: related-poster.webp

Parameter workflow (inheritance / overrides)

Per-section overrides are supported via *_video_parameters, even if a section-specific cover is not defined.

  • If a parameter is defined in *_video_parameters, it is used
  • Otherwise, it inherits from cover_video_parameters (per-parameter inheritance)

No-cover behavior (false)

  • Can be disabled with false: cover, recent_post, article_sort
  • Cannot be disabled with false: pagination, related_post

Section cover fallback (when a section cover is missing)

If the section cover is not provided, the renderer falls back to the post’s main cover value:

  • If pagination_cover is undefined, it falls back to cover
  • If related_post_cover is undefined, it falls back to cover
  • The same fallback pattern applies to other section covers (recent_post_cover, article_sort_cover) when they are not explicitly defined

This preserves the existing theme behavior: section-specific covers are optional overrides, and when they are not set the main cover is used as before.

LCP_Optimization

With Lazy mode enabled, covers that end up being part of the LCP could still be treated as lazy-loaded, negatively impacting Web Core Vitals. This PR adds an option under LCP_Optimization to prioritize the first N covers (above-the-fold) so they are not affected by lazy-loading behavior.

How it works

  • A configurable number of covers on the index page & Article page are treated as “eager”.
LCP_Optimization:
  enable: true
  main_page: 3 # default 3
  articles_sort_page: 8 # default 8
  • For those covers:
    • Images are rendered with loading="eager" and fetchpriority="high" (and decoding="async" is avoided for eager items).
    • Video covers preload the poster using:
      • <link rel="preload" as="image" href="...poster..." fetchpriority="high">

Note about preload placement

Ideally, the preload <link> should live in the document <head> so the browser can discover it as early as possible. In this implementation it is kept close to the cover markup to preserve simplicity and avoid introducing global head-injection logic, while still providing the intended effect: Web Core Vitals/Lighthouse no longer report the missing-preload warning for the LCP resource, which may translate into improved scoring while keeping the implementation straightforward.

Additional fix (unrelated to video covers)

  • Added an onerror fallback to the related posts <img> cover:
    • onerror="this.onerror=null;this.src='...'"

TODO

If this PR is of interest, it may be worth adding a fallback for cases where a section-specific cover is configured but the referenced file does not exist. It is not fully clear to me whether the best target should be the 404.jpg image, or falling back to the base cover.

My preference would be to fall back to the 404.jpg, since the user explicitly configured a section-specific cover and the requested asset is missing. That said, falling back to the base cover would also be a reasonable choice, depending on the desired UX.

Extra Comments

Sorry for the many commits. I wanted to keep this PR as close as possible to the style used in the theme, and since I’ve been working on it in short sessions, I occasionally lost track of what I was changing and ended up committing more often than intended. Thanks, Jerry, for developing and maintaining this beautiful theme.

@dosubot dosubot bot added size:L This PR changes 100-499 lines, ignoring generated files. enhancement New feature or request labels Jan 30, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

Comments