Problem
Currently, puzzle thumbnail images use a square aspect ratio assumption (width=80 height=80) for the HTML width/height attributes. These attributes tell the browser how much space to reserve before the image loads, preventing Cumulative Layout Shift (CLS).
Since most puzzle images are not square (e.g. 135×200 portrait), the assumed dimensions don't match reality. This means either:
- The browser reserves wrong space and layout shifts when the image loads
- We rely on inline
max-height CSS to constrain overflow, which works visually but doesn't give the browser the correct aspect ratio hint
Solution
Store the image aspect ratio (width / height) as a single float column on the puzzle entity (e.g. imageRatio).
Why ratio instead of width+height
A single float is sufficient. Given the ratio and a target max display size, we can calculate exact width and height at render time:
if ratio > 1 (landscape): width = size, height = size / ratio
if ratio < 1 (portrait): height = maxHeight, width = maxHeight * ratio
if ratio = 1 (square): width = height = size
Example: Dino Frozen image (135×200, ratio=0.675) at size=80 → width="54" height="80"
Implementation steps
- Add
imageRatio (nullable float) column to the Puzzle entity
- On image upload, calculate and store
width / height from the uploaded image dimensions
- Write a migration to backfill existing puzzles (read dimensions from stored S3 images or thumbnails)
- Update
LazyImageTwigExtension::lazyPuzzleImage() to accept the ratio and calculate accurate width/height attributes
- Pass the ratio from templates/components that render puzzle images
Impact
- Eliminates CLS for puzzle image loading across all pages
- Removes the need for the inline
style="max-height:Xpx" workaround
- Better Lighthouse/PageSpeed scores
Problem
Currently, puzzle thumbnail images use a square aspect ratio assumption (
width=80 height=80) for the HTMLwidth/heightattributes. These attributes tell the browser how much space to reserve before the image loads, preventing Cumulative Layout Shift (CLS).Since most puzzle images are not square (e.g. 135×200 portrait), the assumed dimensions don't match reality. This means either:
max-heightCSS to constrain overflow, which works visually but doesn't give the browser the correct aspect ratio hintSolution
Store the image aspect ratio (width / height) as a single
floatcolumn on the puzzle entity (e.g.imageRatio).Why ratio instead of width+height
A single float is sufficient. Given the ratio and a target max display size, we can calculate exact
widthandheightat render time:Example: Dino Frozen image (135×200, ratio=0.675) at size=80 →
width="54" height="80"Implementation steps
imageRatio(nullable float) column to the Puzzle entitywidth / heightfrom the uploaded image dimensionsLazyImageTwigExtension::lazyPuzzleImage()to accept the ratio and calculate accuratewidth/heightattributesImpact
style="max-height:Xpx"workaround