feat(v2): use natural cover aspect ratio across remaining surfaces

Extend natural-aspect cover rendering beyond the gallery to the rest of
the app (continue-playing stays a fixed 16:9 hero, by request).

- GameCard size tiers now render fixed-height / natural-width like the
  default card (size just picks the height); covers EditRomDialog (lg)
  and ManageCollectionsDialog (xs) for free. Adds a `fixed` opt-out for
  dense aligned tables.
- GameListRow (compact list view) opts into `fixed` so its cover column
  stays uniform for row alignment.
- Compact fixed-column thumbnails (ScanPlatformRow, DeleteRomDialog,
  RefreshMetadataDialog, ActivityCard) stop cropping (object-fit
  cover → contain): the whole cover shows at its true aspect while the
  slot stays uniform.

Deliberately unchanged: collection mosaics (composite collage art),
save/state screenshots (16:9, not covers), and the provider cover-picker
comparison grids (uniform tiles aid side-by-side selection).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Georges-Antoine Assi
2026-06-21 19:46:01 -04:00
parent 74ef594609
commit c8362ed22e
7 changed files with 24 additions and 10 deletions

View File

@@ -32,7 +32,7 @@ defineProps<Props>();
:alt="romName"
width="100%"
aspect-ratio="2/3"
cover
contain
/>
<RChip
color="success"

View File

@@ -334,7 +334,9 @@ function closeDialog() {
.r-v2-del-rom__cover img {
width: 100%;
height: 100%;
object-fit: cover;
/* Show the whole cover at its natural aspect (no crop); the slot stays a
uniform width so the delete list's rows keep their alignment. */
object-fit: contain;
display: block;
}
.r-v2-del-rom__cover-placeholder {

View File

@@ -681,7 +681,8 @@ function closeDialog() {
.r-v2-refresh__cover img {
width: 100%;
height: 100%;
object-fit: cover;
/* Whole cover at its natural aspect (no crop); the slot stays uniform. */
object-fit: contain;
display: block;
}
.r-v2-refresh__cover-placeholder {

View File

@@ -317,6 +317,7 @@ onBeforeUnmount(() => {
<GameCard
:rom="rom"
size="xs"
fixed
:webp="webp"
decorative
:show-title="false"

View File

@@ -74,6 +74,11 @@ interface Props {
* not a separate scale), so `hero` + any `size` paints the 16:9
* shape at that tier's footprint. */
size?: "xs" | "sm" | "md" | "lg" | "xl";
/** Keep the tier's fixed width×height footprint (cover cropped via
* object-fit) instead of the default fixed-height/natural-width. For
* dense aligned layouts — list-row tables — where a uniform cover
* column matters more than showing the cover's true shape. */
fixed?: boolean;
focused?: boolean;
webp?: boolean;
showPlatformIcon?: boolean;
@@ -369,6 +374,7 @@ function onStaticKeydown(e: KeyboardEvent) {
size !== 'md' && `r-gc--size-${size}`,
{
'r-gc--hero': hero,
'r-gc--fixed': fixed,
'r-gc--focused': focused,
'r-gc--static': static || decorative,
'r-gc--no-hover': noHover || decorative,
@@ -529,21 +535,25 @@ function onStaticKeydown(e: KeyboardEvent) {
color: var(--r-color-fg);
}
/* Default (gallery) card: fixed art height (a 2/3 cover's height at
`--r-card-art-w`), natural width from the cover's own ratio. Same height,
varying widths. Size tiers + hero keep their fixed footprints. */
/* Non-hero, non-fixed cards render at a FIXED HEIGHT with NATURAL WIDTH —
same height, varying widths, never cropped. `size` just picks the height
(the default derives it from the reference width; tiers set it directly).
`hero` (16:9) and the `fixed` opt-out (dense aligned lists / tables that
need a uniform cover column) keep their fixed w×h footprint. */
.r-gc:not([class*="r-gc--size-"]):not(.r-gc--hero) {
--r-card-art-h: calc(var(--r-card-art-w) / 0.6667);
}
.r-gc:not(.r-gc--hero):not(.r-gc--fixed) {
width: auto;
display: flex;
flex-direction: column;
}
/* Width follows the cover (overrides GameCover's base `width: 100%`). */
.r-gc:not([class*="r-gc--size-"]):not(.r-gc--hero) .r-gc__art {
.r-gc:not(.r-gc--hero):not(.r-gc--fixed) .r-gc__art {
width: auto;
}
/* Label fills the cover's width and ellipsises, without widening the card. */
.r-gc:not([class*="r-gc--size-"]):not(.r-gc--hero) .r-gc__label {
.r-gc:not(.r-gc--hero):not(.r-gc--fixed) .r-gc__label {
width: 0;
min-width: 100%;
max-width: 100%;

View File

@@ -60,7 +60,7 @@ const visible = () => props.sections.filter((s) => s.items.length > 0);
surfaces feel like siblings. */
.r-v2-det-infogrid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
grid-template-columns: repeat(auto-fill, minmax(240px, auto));
gap: 18px 24px;
width: 100%;
}

View File

@@ -85,7 +85,7 @@ function coverFor(rom: SimpleRom): string {
:alt="displayName"
:width="36"
:height="48"
cover
contain
class="r-v2-scan-platform__cover"
:class="{ 'r-v2-scan-platform__cover--reveal': coverPop }"
@load="onCoverLoad"