Skip to content

perf(theme): add a core web vitals baseline - #195

Open
francoistibo wants to merge 4 commits into
mainfrom
feat/cwv-baseline
Open

francoistibo wants to merge 4 commits into
mainfrom
feat/cwv-baseline

Conversation

@francoistibo

@francoistibo francoistibo commented Oct 15, 2026 •

Copy link
Copy Markdown
Contributor

Dotez les projets d'images LCP « opt-in », d'images statiques redimensionnées, d'un logo à faible priorité, d'une initialisation JavaScript en mode veille et d'animations sans défilement, afin que les nouveaux thèmes démarrent avec des scores CWV plus proches de la moyenne.

Résumé

  • Ajoute un mode LCP opt-in sur get_the_image() (is_lcp) : fetchpriority="high", decoding="sync", pas de loading="lazy". fetchpriority="high" n'est jamais combiné avec loading="lazy".
  • Ajoute get_theme_static_image_markup() / the_theme_static_image() : width et height lus dans le fichier du thème (SVG ou raster) pour limiter le CLS. Un alt vide est rendu alt="" (image décorative) ; false ou null retire l'attribut.
  • Conserve alt="" quand l'appelant le demande sur une image de la médiathèque, pour que les technologies d'assistance ignorent l'image.
  • Passe le logo du header en fetchpriority="low", pour qu'il ne concurrence pas la vraie image LCP.
  • Précharge les fonts uniquement via le filtre bea_theme_framework_preload_fonts. La liste est vide par défaut : seuls les fichiers réellement affichés au premier rendu doivent y figurer.
  • Remplace la boucle de scroll de Animation par un IntersectionObserver partagé. Les options (start, end, playOnce, hideOnReachEnd) et les callbacks (onInit, onShow, onHide, onDestroy) restent les mêmes.
  • Ajoute deferInitialization() pour lancer le JavaScript non critique au repos du navigateur (INP).
  • Documente ce socle et ce qui reste à configurer par projet (WP Rocket, robots.txt, cache, fonts, embeds tiers) dans le README.
  • Sur BBT : deferInitialization est exposé sur window.beapi.utils pour les assets partiels (wp-block, wp-pattern, template).

Plan de test

  • the_image() avec 'is_lcp' => true produit fetchpriority="high", decoding="sync" et aucun loading="lazy".
  • the_image() avec 'alt' => '' produit alt="".
  • the_theme_static_image() sur un SVG qui a width et height reprend ces attributs et alt="".
  • Le logo du header a fetchpriority="low".
  • Sans le filtre bea_theme_framework_preload_fonts, aucun <link rel="preload" as="font"> n'est imprimé.
  • Un élément .js-animation-opacity reçoit is-visible en entrant dans le viewport, et reste masqué avant.
  • prefers-reduced-motion et l'impression désactivent toujours les animations.
  • window.beapi.utils.deferInitialization est disponible après le chargement du bundle common.

Note

Medium Risk
Animation visibility timing changes site-wide front-end behavior; image/LCP attribute changes affect rendering and accessibility if misused.

Overview
Adds a Core Web Vitals baseline for theme projects: README guidance plus PHP/JS/build hooks teams can opt into.

Images: get_the_image() gains is_lcp (drops lazy loading, sets fetchpriority="high") and honors explicit alt="" for decorative media. New the_theme_static_image / get_theme_static_image_markup helpers output dist/ assets with default decorative alt, optional dimensions from dist/image-dimensions.json (generated at build by a new webpack plugin), and the same fetchpriority/lazy rules.

Performance service: Opt-in font preloads via bea_theme_framework_preload_fonts; custom logo images get fetchpriority="low".

JS: Animation switches from scroll polling to shared IntersectionObserver (with a no-IO fallback); deferInitialization is exposed on window.beapi.utils for idle-time non-critical work.

SCSS: Minor Dart Sass updates (meta.type-of, math.is-unitless) and removal of debug output in em().

Reviewed by Cursor Bugbot for commit 7d6c339. Bugbot is set up for automated code reviews on this repo. Configure here.

@cursor cursor Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Stale Bugbot comment from a previous run.

Comment thread src/js/common/classes/Animation.js
francoistibo and others added 2 commits October 19, 2026 17:36
Give projects opt-in LCP images, sized static images, a low-priority logo, idle JS init, and scroll-free animations so new themes start closer to good CWV scores.

Co-authored-by: Cursor <cursoragent@cursor.com>
Write image width/height into dist/image-dimensions.json at build time and
resolve them from the memoized manifest at runtime to avoid N+1 filesystem
I/O in loops. Keep a per-request filesystem fallback for assets outside the
build. Drop default decoding=sync on is_lcp and document manifest-based CLS
helpers.

Co-authored-by: Cursor <cursoragent@cursor.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit e245d85. Configure here.


if (!sharp) {
return null
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Raster dimensions never hit the manifest

Low Severity

The dimensions plugin reads raster files only through sharp, which is not a package dependency (it is an optional peer of image-minimizer-webpack-plugin and is not installed). require('sharp') always fails, so PNG/JPEG/WebP/AVIF never land in dist/image-dimensions.json. Runtime fallback still reads the files, so CLS is preserved, but the build-time cache this PR adds does not apply to rasters such as default.webp.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit e245d85. Configure here.

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.

1 participant