Skip to content

fix(client): give sizes="auto" card images their intrinsic aspect ratio - #7973

Merged
matthewevans merged 2 commits into
phase-rs:mainfrom
cuinhellcat:fix/card-image-intrinsic-aspect
Aug 27, 2026
Merged

fix(client): give sizes="auto" card images their intrinsic aspect ratio#7973
matthewevans merged 2 commits into
phase-rs:mainfrom
cuinhellcat:fix/card-image-intrinsic-aspect

Conversation

@cuinhellcat

@cuinhellcat cuinhellcat commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Fixes #7971.

Complements #7974, which fixed the desktop hover preview at its own call site (aspect-[488/680] on the frame, h-full w-full on the image). The four sites below are untouched by that commit and still letterbox, because they are the ones that let the image supply its own height.

What

getCardImageSrcSetProps hands every card image sizes="auto" plus loading="lazy", but no width/height. Without that pair the <img> has no intrinsic aspect ratio of its own: the spec's default object size (300x150) stands in, so an element that derives its height from the image lays out a 2:1 box, and object-cover / object-contain crops or letterboxes the card.

Measurements

Chromium 151, the shipped stylesheet, the site's own markup, only the attribute set differing:

markup shape with the attribute set without
width-only (w-[clamp(…)], object-cover) 472 x 150 472 x 658
max-only (max-h-*/max-w-*, object-contain) 302 x 152 428 x 596
w-full inside an aspect-ratio parent, translateY slice 298 x 150 298 x 415

Sites still affected on main (fba2513)

site markup shape
CardPreview mobile compact peek (both branches) max-only
CardPreview mobile modal, upright branch max-only
CardTextboxPreview w-full slice — the translateY offset is a percentage of that height, so the visible band is wrong, not just the size
CardCoverageDashboard width-only

The other fifteen call sites of the helper constrain both axes in CSS and were never affected.

Why the helper rather than four more call sites

The helper is what introduces sizes="auto", so it owes every consumer the intrinsic size that keyword resolves against; fixing call sites leaves the next one to rediscover this. It composes with #7974 rather than competing with it: CSS wins wherever a call site sizes the element, so the frame's aspect-[488/680] there stays authoritative and the pair only supplies the ratio nothing else provides.

Both rungs are the same card scan at two widths, so pinning the normal rung's 488x680 — the ratio the client's own aspect-[488/680] placeholder tiles already encode — fixes the ratio for the whole ladder.

Verification

cardImageSrcSet.test.ts: the two exact-shape assertions pin width/height, plus a contract test that no result may offer sizes="auto" without them, for both the derived-URL and the installed-rung branch. jsdom has no layout, so this guards the contract, not the geometry — the geometry evidence is the table above.

vitest run (full suite) 3648 passed, tsc -b --noEmit and eslint clean.

`getCardImageSrcSetProps` hands every card image `sizes="auto"` plus
`loading="lazy"`, but no `width`/`height`. Without that pair an `<img>`
has no intrinsic aspect ratio of its own, so the spec's 300x150 default
object size stands in and any element that lets the image supply its own
height lays out a 2:1 box. `object-cover` then crops the card to a
middle slice.

Measured in Chromium 151 with the shipped stylesheet, the desktop hover
preview's markup lays out 472x150 with the attribute set and 472x658
without it.

Five image sites derive their height from the image and were letterboxed:
the desktop hover preview, both mobile preview overlays (`CardPreview`),
the textbox slice (`CardTextboxPreview`, whose `translateY` offset is a
percentage of that height), and the coverage dashboard. The other
fifteen call sites constrain both axes in CSS and were unaffected — CSS
still wins there, so the pair only ever supplies the ratio nothing else
provides.

Fixed in the helper rather than at the five call sites: the helper is
what introduces `sizes="auto"`, so it owes every consumer the intrinsic
size that keyword resolves against. Both rungs are the same card scan at
two widths, so pinning the `normal` rung's 488x680 — the ratio the
client's `aspect-[488/680]` placeholder tiles already encode — fixes the
ratio for the whole ladder.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 27, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 39a467dd-6ec3-41da-babc-0ca07dded6dd

📥 Commits

Reviewing files that changed from the base of the PR and between 49dbc23 and d4e252d.

📒 Files selected for processing (2)
  • client/src/components/card/__tests__/cardImageSrcSet.test.ts
  • client/src/components/card/cardImageSrcSet.ts

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

Changes

The card image source-set contract now includes intrinsic width and height values. Both generated and explicit image variants return 488×680 dimensions. Tests verify these values with sizes="auto".

Card image intrinsic dimensions

Layer / File(s) Summary
Intrinsic dimension contract
client/src/components/card/cardImageSrcSet.ts
CardImageSrcSetProps now requires numeric width and height fields. RUNG_INTRINSIC_SIZE defines the 488×680 dimensions.
Return and validation paths
client/src/components/card/cardImageSrcSet.ts, client/src/components/card/__tests__/cardImageSrcSet.test.ts
Both return paths include the intrinsic dimensions. Tests cover generated and explicit image variants.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to d4e25

This change gives auto-sized card images their intended intrinsic aspect ratio, restoring correct card rendering without changing data flow, permissions, dependencies, or deployment behavior. No actionable merge-blocking risk remains beyond normal checks and review.

Suggested reviewers: matthewevans

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 2 files.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding an intrinsic aspect ratio to card images that use sizes="auto".
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@matthewevans

Copy link
Copy Markdown
Member

Deferred by maintainer intake policy — not ignored.

This current head (d4e252d6f2ff22c81f008253115910b0ac677d08) was triaged as a frontend-only change (client/src/components/card/__tests__/cardImageSrcSet.test.ts, client/src/components/card/cardImageSrcSet.ts) by cuinhellcat. The local frontend-review allowlist does not include this author, so this route does not perform an implementation-diff review or approve the PR.

A maintainer must explicitly take this PR or add a local frontend-review exception before it can receive substantive review. The defer label is a routing marker only, not a verdict on the change.

@matthewevans matthewevans added the defer-fe Frontend/client/UI PR deferred to Matt's direct review label Aug 27, 2026
@matthewevans

Copy link
Copy Markdown
Member

Maintainer note: approved for review

@matthewevans matthewevans removed the defer-fe Frontend/client/UI PR deferred to Matt's direct review label Aug 27, 2026
@matthewevans matthewevans self-assigned this Aug 27, 2026

@matthewevans matthewevans left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Reviewed current head b8a46f6: the helper is the correct contract seam and its regression tests discriminate both source paths.

@matthewevans matthewevans added the bug Bug fix label Aug 27, 2026
@matthewevans
matthewevans enabled auto-merge August 27, 2026 22:36
@matthewevans matthewevans removed their assignment Aug 27, 2026
@matthewevans
matthewevans added this pull request to the merge queue Aug 27, 2026
Merged via the queue into phase-rs:main with commit 8142aed Aug 27, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Bug fix

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Card images lose their aspect ratio: previews render a 150px letterbox slice of the card

2 participants