source: Klonkt/src/views/partials/post-card.ejs@ 3e86f1c

main
Last change on this file since 3e86f1c was 7963a6e, checked in by roboburr <roboburr@…>, 3 months ago

fix: feed post links use siteUrlBase (clicking did not work in hub)

post-card + post-tile linked to '/<slug>' (absolute). In hub that falls back
to the main site -> 404 -> click did nothing. Now siteUrlBase + '/<slug>'
(and likewise for tag chips), so /user/<slug>/<post> in hub and '/<post>'
in solo.

Co-Authored-By: Claude <noreply@…>

  • Property mode set to 100644
File size: 6.9 KB
Line 
1<%
2// Post list item — MOBILE-FIRST.
3//
4// <768px (default):
5// Vertical stack — full-bleed 16:9 cover on top, content below.
6// Whole card is tappable (one big <a> wrapping everything).
7// Active state: scale(0.985) + opacity dim.
8//
9// ≥768px (desktop):
10// v9 pattern — 120px square cover left, content right.
11// Hover state on cover. Tap-target stays large.
12
13const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
14const _href = _base + '/' + post.slug;
15const _hasCover = !!post.cover_image_url;
16const _typeLabel = (post.type && post.type !== 'overig' && post.type !== 'post') ? post.type : '';
17const _isPinned = !!post.pinned;
18const _isDraft = post.status && post.status !== 'published';
19
20function _ddmmyyyy(iso) {
21 if (!iso) return '';
22 const d = new Date(iso);
23 if (isNaN(d.getTime())) return '';
24 const pad = n => String(n).padStart(2, '0');
25 return pad(d.getDate()) + '-' + pad(d.getMonth() + 1) + '-' + d.getFullYear();
26}
27
28let _tags = [];
29if (post.tags) {
30 if (Array.isArray(post.tags)) _tags = post.tags;
31 else if (typeof post.tags === 'string') {
32 try {
33 const parsed = JSON.parse(post.tags);
34 _tags = Array.isArray(parsed) ? parsed : post.tags.split(',').map(t => t.trim()).filter(Boolean);
35 } catch (e) {
36 _tags = post.tags.split(',').map(t => t.trim()).filter(Boolean);
37 }
38 }
39}
40%>
41<li class="post-list-item<%= _hasCover ? ' has-cover' : '' %><%= _isPinned ? ' is-pinned' : '' %>">
42
43 <% if (_hasCover) { %>
44 <a class="post-list-cover" href="<%= _href %>"
45 hx-get="<%= _href %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
46 hx-push-url="<%= _href %>" hx-indicator="#pcms-loading"
47 aria-label="<%= post.title || '(zonder titel)' %>" tabindex="-1">
48 <img src="<%= post.cover_image_url %>" alt="" loading="lazy" decoding="async">
49 </a>
50 <% } %>
51
52 <div class="post-list-body">
53 <div class="post-list-meta mono">
54 <% if (_isPinned) { %><span class="post-list-pin" aria-label="Vastgepind">★</span><% } %>
55 <time datetime="<%= post.published_at || post.created_at %>"><%= _ddmmyyyy(post.published_at || post.created_at) %></time>
56 <% if (_typeLabel) { %>
57 <span class="post-list-type">· <%= _typeLabel.charAt(0).toUpperCase() + _typeLabel.slice(1) %></span>
58 <% } %>
59 <% if (_isDraft) { %>
60 <span class="post-list-type post-list-draft">· concept</span>
61 <% } %>
62 </div>
63
64 <h3 class="post-list-title">
65 <a href="<%= _href %>"
66 hx-get="<%= _href %>?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
67 hx-push-url="<%= _href %>" hx-indicator="#pcms-loading">
68 <%= post.title || '(zonder titel)' %>
69 </a>
70 </h3>
71
72 <% if (post.excerpt) { %>
73 <p class="post-list-excerpt"><%= post.excerpt %></p>
74 <% } %>
75
76 <% if (_tags.length) { %>
77 <div class="post-list-tags">
78 <% _tags.forEach(function(t) { %>
79 <a href="<%= _base %>/tag/<%= encodeURIComponent(t) %>" class="tag-chip"
80 hx-get="<%= _base %>/tag/<%= encodeURIComponent(t) %>?partial=1" hx-target="#pcms-main"
81 hx-swap="innerHTML" hx-push-url="<%= _base %>/tag/<%= encodeURIComponent(t) %>"
82 hx-indicator="#pcms-loading">#<%= t %></a>
83 <% }); %>
84 </div>
85 <% } %>
86 </div>
87</li>
88
89<style>
90/* ============================================================
91 POST-LIST-ITEM — MOBILE-FIRST OVERRIDE
92 The base v9 styles in /assets/css/style.css use a desktop
93 120×120 grid by default. We flip that: stacked on mobile,
94 side-by-side on ≥768px.
95 ============================================================ */
96
97/* Reset v9's grid on mobile and stack vertically */
98@media (max-width: 767px) {
99 .post-list {
100 gap: 2rem;
101 }
102 .post-list-item {
103 display: flex !important;
104 flex-direction: column;
105 grid-template-columns: none !important;
106 gap: .85rem !important;
107 padding-bottom: 2rem;
108 /* Active feedback for the whole card row */
109 transition: transform 120ms ease, opacity 120ms ease;
110 }
111 .post-list-item:active {
112 transform: scale(0.985);
113 opacity: 0.85;
114 }
115 .post-list-item:last-child {
116 border-bottom: none;
117 padding-bottom: 0;
118 }
119
120 /* Cover: full-bleed of the container, 16:9 ratio, no border-radius
121 on small phones (edge-to-edge feels app-native). 8px radius from 480px+. */
122 .post-list-cover {
123 aspect-ratio: 16 / 9 !important;
124 width: 100% !important;
125 border-radius: 0 !important;
126 /* Negative margin to break out of .container's padding */
127 margin-left: calc(-1 * clamp(1.25rem, 4vw, 2rem));
128 margin-right: calc(-1 * clamp(1.25rem, 4vw, 2rem));
129 width: calc(100% + 2 * clamp(1.25rem, 4vw, 2rem)) !important;
130 background: var(--paper-2);
131 }
132 .post-list-cover img {
133 width: 100%; height: 100%;
134 object-fit: cover;
135 display: block;
136 }
137
138 /* Re-apply rounded corners on slightly larger phones */
139 @media (min-width: 480px) {
140 .post-list-cover {
141 border-radius: var(--radius) !important;
142 margin-left: 0 !important;
143 margin-right: 0 !important;
144 width: 100% !important;
145 }
146 }
147
148 /* Larger title for mobile readability */
149 .post-list-title {
150 font-size: 1.45rem !important;
151 line-height: 1.2 !important;
152 }
153 .post-list-excerpt {
154 font-size: 1rem !important;
155 color: var(--ink-soft) !important;
156 line-height: 1.55 !important;
157 }
158 .post-list-meta {
159 font-size: .8rem !important;
160 gap: .5rem !important;
161 }
162
163 /* Pinned indicator */
164 .post-list-item.is-pinned .post-list-title a {
165 /* No visual change on title — the ★ in meta is enough */
166 }
167
168 /* Tap targets for tags */
169 .post-list-tags { gap: .4rem; }
170 .tag-chip {
171 min-height: 32px;
172 display: inline-flex;
173 align-items: center;
174 padding: .2rem .65rem;
175 }
176}
177
178/* ============================================================
179 DESKTOP — keep v9's 120px-square pattern (style.css does this)
180 We just enforce the layout here for clarity + override safety.
181 ============================================================ */
182@media (min-width: 768px) {
183 .post-list-item.has-cover {
184 display: grid !important;
185 grid-template-columns: 120px 1fr !important;
186 gap: 1.25rem !important;
187 }
188 .post-list-item:not(.has-cover) {
189 grid-template-columns: 1fr !important;
190 }
191 .post-list-cover {
192 aspect-ratio: 1 !important;
193 border-radius: var(--radius) !important;
194 }
195 /* Hover lift on desktop (touch devices use :active above) */
196 @media (hover: hover) {
197 .post-list-cover {
198 transition: transform 200ms ease;
199 }
200 .post-list-item:hover .post-list-cover {
201 transform: scale(1.02);
202 }
203 }
204}
205
206/* ============================================================
207 PIN + DRAFT INDICATORS (shared)
208 ============================================================ */
209.post-list-pin {
210 color: var(--accent);
211 margin-right: .35rem;
212 font-size: .9rem;
213}
214.post-list-draft {
215 color: var(--accent);
216 font-style: italic;
217}
218</style>
Note: See TracBrowser for help on using the repository browser.