diff --git a/src/styles/components.css b/src/styles/components.css
index 91148f3..3a4b93f 100644
--- a/src/styles/components.css
+++ b/src/styles/components.css
@@ -682,13 +682,14 @@
overflow: visible;
}
+.project-media-gallery__item,
.project-media-gallery .project-media {
position: relative;
min-inline-size: 0;
overflow: visible;
}
-.project-media-gallery .project-media:focus-within {
+.project-media-gallery__item:focus-within {
z-index: 1;
}
@@ -704,15 +705,15 @@
padding-inline: 0.5rem;
}
- .project-media-gallery[data-media-layout="portrait-strip"] > .project-media + .project-media {
+ .project-media-gallery[data-media-layout="portrait-strip"] > .project-media-gallery__item + .project-media-gallery__item {
margin-inline-start: -0.75rem;
}
- .project-media-gallery[data-media-layout="portrait-strip"] > .project-media:nth-child(even) {
+ .project-media-gallery[data-media-layout="portrait-strip"] > .project-media-gallery__item:nth-child(even) {
margin-block-start: 1rem;
}
- .project-media-gallery[data-media-layout="portrait-strip"] > .project-media:nth-child(3n) {
+ .project-media-gallery[data-media-layout="portrait-strip"] > .project-media-gallery__item:nth-child(3n) {
margin-block-start: 0.5rem;
}
@@ -720,6 +721,32 @@
grid-template-columns: repeat(2, minmax(0, 1fr));
align-items: start;
}
+
+ .project-media-gallery[data-media-layout="chart-pair"] > .project-media-gallery__item > .project-media {
+ display: grid;
+ grid-template-rows: clamp(12rem, 24vw, 22rem) auto;
+ position: relative;
+ align-content: start;
+ }
+
+ .project-media-gallery[data-media-layout="chart-pair"] > .project-media-gallery__item > .project-media > :is(img, video) {
+ position: absolute;
+ inset: 0 0 auto;
+ inline-size: 100% !important;
+ max-inline-size: 100% !important;
+ block-size: clamp(12rem, 24vw, 22rem) !important;
+ max-block-size: clamp(12rem, 24vw, 22rem) !important;
+ object-fit: contain;
+ object-position: center;
+ }
+
+ .project-media-gallery[data-media-layout="chart-pair"] > .project-media-gallery__item > .project-media > figcaption {
+ grid-row: 2;
+ }
+
+ .project-media-gallery[data-media-layout="chart-pair"] > .project-media-gallery__item > .project-media > :not(:is(img, video, figcaption)) {
+ grid-row: 3;
+ }
}
.project-media img,
@@ -894,8 +921,8 @@
padding-inline: 0;
}
- .project-media-gallery[data-media-layout="portrait-strip"] > .project-media,
- .project-media-gallery[data-media-layout="portrait-strip"] > .project-media + .project-media {
+ .project-media-gallery[data-media-layout="portrait-strip"] > .project-media-gallery__item,
+ .project-media-gallery[data-media-layout="portrait-strip"] > .project-media-gallery__item + .project-media-gallery__item {
margin-block-start: 0;
margin-inline-start: 0;
}
diff --git a/src/styles/print.css b/src/styles/print.css
index 2fe7849..63cdda6 100644
--- a/src/styles/print.css
+++ b/src/styles/print.css
@@ -117,7 +117,11 @@
.project-media-gallery img,
.project-media-gallery video {
max-inline-size: 100%;
+ max-block-size: min(9in, calc(100vh - 6rem));
+ inline-size: auto;
block-size: auto;
+ margin-inline: auto;
+ object-fit: contain;
}
a {
diff --git a/tests/browser/responsive-layout.spec.ts b/tests/browser/responsive-layout.spec.ts
index ba1b5bd..29eae1f 100644
--- a/tests/browser/responsive-layout.spec.ts
+++ b/tests/browser/responsive-layout.spec.ts
@@ -1,6 +1,6 @@
import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';
-import { expect, test, type Page, type TestInfo } from '@playwright/test';
+import { expect, test, type Page } from '@playwright/test';
import { createServer, type ViteDevServer } from 'vite';
const stylesheetFiles = [
@@ -33,6 +33,11 @@ const syntheticProjectMedia = [
{ kind: 'image', assetId: 'synthetic-portrait-three', alt: 'Synthetic schedule evidence.', caption: 'Fictional demo schedule.' },
] as const;
+const syntheticChartPair = [
+ { kind: 'image', assetId: 'synthetic-chart-wide', alt: 'Synthetic wide chart evidence.', caption: 'Fictional wide chart.' },
+ { kind: 'image', assetId: 'synthetic-chart-tall', alt: 'Synthetic tall chart evidence.', caption: 'Fictional tall chart.' },
+] as const;
+
function withSyntheticProjectMedia(contentInput: unknown, manifestInput: unknown): { content: unknown; manifest: unknown } {
const content = structuredClone(contentInput) as {
projects: { selected: Array<{ assetIds: string[]; media?: unknown; mediaLayout?: unknown }> };
@@ -43,6 +48,10 @@ function withSyntheticProjectMedia(contentInput: unknown, manifestInput: unknown
project.assetIds = syntheticProjectMedia.map((media) => media.assetId);
project.media = syntheticProjectMedia;
project.mediaLayout = 'portrait-strip';
+ const chartPairProject = content.projects.selected[1]!;
+ chartPairProject.assetIds = syntheticChartPair.map((media) => media.assetId);
+ chartPairProject.media = syntheticChartPair;
+ chartPairProject.mediaLayout = 'chart-pair';
manifest.assets.push(...syntheticProjectMedia.map((media, index) => ({
id: media.assetId,
fileName: `${media.assetId}.webp`,
@@ -55,6 +64,32 @@ function withSyntheticProjectMedia(contentInput: unknown, manifestInput: unknown
license: 'Test-only synthetic asset',
attribution: 'Synthetic test fixture',
})));
+ manifest.assets.push(
+ {
+ id: syntheticChartPair[0].assetId,
+ fileName: `${syntheticChartPair[0].assetId}.webp`,
+ sha256: '4'.repeat(64),
+ mediaType: 'image/webp',
+ width: 1200,
+ height: 600,
+ durationMs: null,
+ rightsHolder: 'Synthetic test fixture',
+ license: 'Test-only synthetic asset',
+ attribution: 'Synthetic test fixture',
+ },
+ {
+ id: syntheticChartPair[1].assetId,
+ fileName: `${syntheticChartPair[1].assetId}.webp`,
+ sha256: '5'.repeat(64),
+ mediaType: 'image/webp',
+ width: 600,
+ height: 1200,
+ durationMs: null,
+ rightsHolder: 'Synthetic test fixture',
+ license: 'Test-only synthetic asset',
+ attribution: 'Synthetic test fixture',
+ },
+ );
return { content, manifest };
}
@@ -98,10 +133,6 @@ async function loadInformationLayer(page: Page): Promise