diff --git a/themes/intotheeast/blueprints/trip.yaml b/themes/intotheeast/blueprints/trip.yaml index 3e697ef..6e22b60 100644 --- a/themes/intotheeast/blueprints/trip.yaml +++ b/themes/intotheeast/blueprints/trip.yaml @@ -38,10 +38,9 @@ form: help: 'Leave blank if trip is ongoing' header.cover_image: - type: text - label: 'Cover Image Filename' - placeholder: 'cover.jpg' - help: 'Used in the trips listing page' + type: pagemediaselect + label: 'Cover Image' + help: 'Pick from images uploaded to this trip page. Shown on the trips listing and the trip-page banner. Falls back to the first journal entry photo if left unset.' header.album_url: type: text diff --git a/themes/intotheeast/css/style.css b/themes/intotheeast/css/style.css index 71cbf7a..ac6ab6c 100644 --- a/themes/intotheeast/css/style.css +++ b/themes/intotheeast/css/style.css @@ -950,6 +950,66 @@ body::after { color: var(--color-ink-muted); } +/* ── Trip page header extras: one-liner, description, banner ──────────────────── */ + +.home-trip-tagline { + font-size: var(--text-md); + line-height: var(--leading-snug); + color: var(--color-ink-2); + margin: 0 0 var(--space-2); +} + +.trip-header-desc { + margin: var(--space-3) 0 var(--space-4); +} + +.trip-header-desc-body { + font-size: var(--text-sm); + line-height: var(--leading-normal); + color: var(--color-ink-2); +} + +.trip-header-desc-body > :first-child { margin-top: 0; } +.trip-header-desc-body > :last-child { margin-bottom: 0; } +.trip-header-desc-body p { margin: 0 0 var(--space-2); } + +/* Collapsed preview ≈3 lines. Uses max-height (not -webkit-line-clamp) so it + holds across the multiple
that markdown content renders. */
+.trip-header-desc[data-collapsed="true"] .trip-header-desc-body {
+ max-height: 4.8em;
+ overflow: hidden;
+}
+
+.trip-header-desc-toggle {
+ display: inline-block;
+ margin-top: var(--space-1);
+ padding: 0;
+ background: none;
+ border: none;
+ font: inherit;
+ font-size: var(--text-sm);
+ color: var(--color-accent);
+ cursor: pointer;
+}
+
+.trip-header-desc-toggle:hover { color: var(--color-accent-hover); }
+
+.trip-header-banner {
+ width: 100%;
+ height: 200px;
+ margin: var(--space-4) 0 0;
+ border-radius: var(--radius-md);
+ overflow: hidden;
+ background: var(--color-border);
+}
+
+.trip-header-banner img {
+ width: 100%;
+ height: 100%;
+ object-fit: cover;
+ display: block;
+}
+
/* ── Trip page filter bar ────────────────────────────────────────────────────── */
.feed-sort-bar {
@@ -1045,6 +1105,7 @@ body::after {
.home-map-col { position: static; height: 40vh; align-self: stretch; }
.home-map { height: 40vh; }
.home-feed-col { padding: var(--space-6) var(--space-5); }
+ .trip-header-banner { height: 130px; }
}
/* ── Past trips archive ──────────────────────────────────────────────────────── */
@@ -1114,6 +1175,13 @@ body::after {
margin-bottom: var(--space-2);
}
+.trip-card-tagline {
+ font-size: var(--text-sm);
+ line-height: var(--leading-snug);
+ color: var(--color-ink-muted);
+ margin: 0 0 var(--space-3);
+}
+
.trip-card-meta {
display: flex;
gap: var(--space-4);
diff --git a/themes/intotheeast/templates/macros/cover.html.twig b/themes/intotheeast/templates/macros/cover.html.twig
new file mode 100644
index 0000000..bd0ed64
--- /dev/null
+++ b/themes/intotheeast/templates/macros/cover.html.twig
@@ -0,0 +1,45 @@
+{#
+ Shared trip cover: one source of truth for cover resolution + retina
+ rendering, used by both the trip-list card (trips.html.twig) and the
+ trip-page banner strip (trip-feed-col.html.twig) so the two surfaces
+ cannot drift (KTD2).
+
+ Resolution (R7 / R11):
+ 1. author-selected header.cover_image, when it resolves to page media;
+ 2. else the first published journal entry's first image;
+ 3. else nothing.
+ A set-but-missing cover_image (deleted/moved file) is `is not defined` in
+ page media, so it falls through to the auto-pick rather than rendering a
+ broken image (R11).
+
+ Twig macros can only emit strings — they cannot return a Medium object —
+ so resolution and rendering live together in one macro: it emits the
+ wrapper + retina when a cover exists, and nothing at all when none
+ does (which gives R9/AE4 a clean text-only header for free).
+
+ Retina (R6 / R14): two explicit cropResize derivatives (1x at w×h, 2x at
+ 2w×2h) as an explicit `srcset` with w-descriptors; `alt` is the trip title.
+#}
+{% macro render(trip_page, alt, w, h, wrapper_class, sizes) %}
+{%- set cover = null -%}
+{%- if trip_page.header.cover_image and trip_page.media[trip_page.header.cover_image] is defined -%}
+ {%- set cover = trip_page.media[trip_page.header.cover_image] -%}
+{%- else -%}
+ {%- set dailies_page = grav.pages.find(trip_page.route ~ '/dailies') -%}
+ {%- if dailies_page -%}
+ {%- set first_entry = dailies_page.children.published()|first -%}
+ {%- if first_entry and first_entry.media.images|length > 0 -%}
+ {%- set cover = first_entry.media.images|first -%}
+ {%- endif -%}
+ {%- endif -%}
+{%- endif -%}
+{%- if cover -%}
+
{{ trip_page.header.tagline }}
+ {% endif %} {% if trip_page.header.date_start %}{{ trip_page.header.date_start|date('d M Y') }} @@ -13,6 +21,15 @@ {{ journal_count }} journal {{ journal_count == 1 ? 'entry' : 'entries' }} {% if story_count > 0 %} · {{ story_count }} {{ story_count == 1 ? 'story' : 'stories' }}{% endif %} + {% if trip_header_extras and trip_page.content|striptags|trim %} +