feat: add evidence-led project media layouts

This commit is contained in:
2026-09-04 21:18:50 +03:00
parent 242d79599a
commit 9cec17e139
9 changed files with 352 additions and 15 deletions

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { PublicProject } from '@/content/types';
import ProjectMedia from './ProjectMedia.vue';
import ProjectMediaGallery from './ProjectMediaGallery.vue';
withDefaults(defineProps<{ project: Readonly<PublicProject>; allowAutomaticVideo?: boolean }>(), {
allowAutomaticVideo: false,
@@ -8,13 +8,17 @@ withDefaults(defineProps<{ project: Readonly<PublicProject>; allowAutomaticVideo
</script>
<template>
<article class="project-card" :data-project-id="project.id" :data-maturity="project.maturity">
<article
class="project-card"
:data-project-id="project.id"
:data-maturity="project.maturity"
:data-media-layout="project.mediaLayout ?? 'none'"
>
<h3>{{ project.title }}</h3>
<p>{{ project.summary }}</p>
<ProjectMedia
v-for="media in project.media ?? []"
:key="`${media.kind}-${media.assetId}`"
:presentation="media"
<ProjectMediaGallery
:media="project.media ?? []"
:layout="project.mediaLayout ?? 'single'"
:allow-automatic-video="allowAutomaticVideo"
/>
<p>{{ project.contribution }}</p>

View File

@@ -1,6 +1,6 @@
<script setup lang="ts">
import type { PublicProject } from '@/content/types';
import ProjectMedia from './ProjectMedia.vue';
import ProjectMediaGallery from './ProjectMediaGallery.vue';
withDefaults(defineProps<{
project: Readonly<PublicProject>;
@@ -13,15 +13,19 @@ withDefaults(defineProps<{
</script>
<template>
<article class="project-case-study" :data-project-id="project.id" :data-project-priority="priority">
<article
class="project-case-study"
:data-project-id="project.id"
:data-project-priority="priority"
:data-media-layout="project.mediaLayout ?? 'none'"
>
<header class="project-case-study__header">
<h3>{{ project.title }}</h3>
<p>{{ project.summary }}</p>
</header>
<ProjectMedia
v-for="media in project.media ?? []"
:key="`${media.kind}-${media.assetId}`"
:presentation="media"
<ProjectMediaGallery
:media="project.media ?? []"
:layout="project.mediaLayout ?? 'single'"
:allow-automatic-video="allowAutomaticVideo"
/>
<dl class="project-evidence">

View File

@@ -0,0 +1,21 @@
<script setup lang="ts">
import ProjectMedia from './ProjectMedia.vue';
import type { MediaPresentation, ProjectMediaLayout } from '@/content/types';
withDefaults(defineProps<{
media: readonly MediaPresentation[];
layout?: ProjectMediaLayout;
allowAutomaticVideo?: boolean;
}>(), { layout: 'single', allowAutomaticVideo: false });
</script>
<template>
<div v-if="media.length > 0" class="project-media-gallery" :data-media-layout="layout">
<ProjectMedia
v-for="presentation in media"
:key="`${presentation.kind}-${presentation.assetId}`"
:presentation="presentation"
:allow-automatic-video="allowAutomaticVideo"
/>
</div>
</template>

View File

@@ -485,6 +485,17 @@
background: var(--surface-soft);
}
@media (min-width: 100rem) {
.project-case-study[data-project-id="private-medication-tracker"][data-media-layout]:not([data-media-layout="none"]) {
inline-size: min(72rem, 88%);
}
.project-case-study[data-project-id="footy-tracker"][data-media-layout]:not([data-media-layout="none"]) {
inline-size: min(68rem, 82%);
margin-inline-start: auto;
}
}
.project-case-study__header {
display: grid;
gap: var(--space-1);
@@ -661,6 +672,56 @@
overflow: hidden;
}
.project-media-gallery {
display: grid;
grid-template-columns: minmax(0, 1fr);
gap: var(--space-3);
min-inline-size: 0;
margin-block: var(--space-3);
isolation: isolate;
overflow: visible;
}
.project-media-gallery .project-media {
position: relative;
min-inline-size: 0;
overflow: visible;
}
.project-media-gallery .project-media:focus-within {
z-index: 1;
}
.project-media-gallery[data-media-layout="single"] {
grid-template-columns: minmax(0, 1fr);
}
@media (min-width: 48rem) {
.project-media-gallery[data-media-layout="portrait-strip"] {
grid-template-columns: repeat(3, minmax(0, 1fr));
gap: 0;
align-items: start;
padding-inline: 0.5rem;
}
.project-media-gallery[data-media-layout="portrait-strip"] > .project-media + .project-media {
margin-inline-start: -0.75rem;
}
.project-media-gallery[data-media-layout="portrait-strip"] > .project-media:nth-child(even) {
margin-block-start: 1rem;
}
.project-media-gallery[data-media-layout="portrait-strip"] > .project-media:nth-child(3n) {
margin-block-start: 0.5rem;
}
.project-media-gallery[data-media-layout="chart-pair"] {
grid-template-columns: repeat(2, minmax(0, 1fr));
align-items: start;
}
}
.project-media img,
.project-media video {
inline-size: 100%;
@@ -822,9 +883,23 @@
}
.project-card[data-maturity] {
inline-size: 100%;
margin-inline: 0;
}
.project-media-gallery,
.project-media-gallery[data-media-layout="portrait-strip"],
.project-media-gallery[data-media-layout="chart-pair"] {
grid-template-columns: minmax(0, 1fr);
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 {
margin-block-start: 0;
margin-inline-start: 0;
}
.contact-destination__copy {
font-size: var(--step-1);
}

View File

@@ -136,6 +136,7 @@
.portfolio-section[data-layout="project-stage"] {
inline-size: 92%;
min-inline-size: 0;
margin-inline: 4%;
padding: 0;
border: 0;
@@ -143,6 +144,10 @@
box-shadow: none;
}
.portfolio-section[data-layout="project-stage"] .project-stage__projects {
min-inline-size: 0;
}
.portfolio-section[data-layout="project-stage"] > h2,
.portfolio-section[data-layout="project-stage"] > .section-eyebrow {
inline-size: fit-content;

View File

@@ -101,6 +101,25 @@
box-shadow: none;
}
.project-media-gallery,
.project-media-gallery[data-media-layout] {
display: block;
overflow: visible;
padding: 0;
}
.project-media-gallery .project-media {
break-inside: avoid;
margin: 0 0 0.9rem;
overflow: visible;
}
.project-media-gallery img,
.project-media-gallery video {
max-inline-size: 100%;
block-size: auto;
}
a {
color: inherit;
text-decoration: underline;