source: Klonkt/src/views/partials/shared-styles.ejs@ 2d2fd8c

main
Last change on this file since 2d2fd8c was 2d2fd8c, checked in by Bart <bart@…>, 3 weeks ago

Tijdlijn | Grid | Lezen: de leesweergave krijgt een ingang

De leesweergave bestond wel maar was alleen te bereiken door /read in te tikken.
Nu staat ze als derde optie in dezelfde pil.

Een <a>, geen <button>, en met opzet NIET de klasse view-switch-btn: de
gedelegeerde klik in shell.ejs pakt alles met die klasse op, zet
body[data-feed-view] erop en springt op niet-feedpagina's terug naar home. Die
had deze link opgegeten.

Door er een link naartoe te leggen kwamen twee fouten boven die er al zaten:

  1. De route gaf ELKE htmx-aanvraag één kaal artikel terug, want de leesmodule had ?partial=1 geleend -- en dat betekent op deze site al iets anders: "de pagina zonder de schil". Een gebooste link naar /read schoof dus één artikel in #pcms-main: geen stroom, geen script. De module vraagt nu ?fragment=1, en al het andere gaat naar pages/read waar renderPage zelf over de schil beslist.
  1. PAGE_CLASSES in shell.ejs is een witte lijst, en 'on-read' stond er niet in. Bij een htmx-navigatie werd de class dus weggefilterd en sloeg ELKE regel van de leesweergave (.on-read ...) niet aan: geen schermhoge berichten, geen snappen, geen wegschuivende balken. Bij intikken ging het goed, want dan zet de shell de class zelf -- vandaar dat het onzichtbaar bleef.

Beide nagelopen in de browser: de pil navigeert, on-read staat er, min-height is
de schermhoogte, snappen staat aan, en de opgeslagen feed-view blijft ongemoeid.

MOD_V naar 3 (read.js) en style.css naar v68 (nieuwe selector).

Co-Authored-By: Claude Opus 5 <claude@…>

  • Property mode set to 100644
File size: 13.3 KB
Line 
1<style>
2/* ─────────────────────────────────────────────────
3 View switcher (timeline ↔ grid + grid-cols picker)
4 ───────────────────────────────────────────────── */
5.view-switcher-wrap {
6 border-bottom: 1px solid var(--rule);
7 background: var(--paper);
8 /* No extra margin-top: this centres the pill between the profile-header
9 rule (above) and the own border (below). Equal space above/below the
10 pill comes from the symmetric container padding below. The margin-bottom
11 (1.5rem in style.css) keeps breathing room toward the feed. */
12 margin-top: 0;
13}
14.view-switcher-wrap .container {
15 display: flex; align-items: center; justify-content: center; gap: 0.75rem;
16 /* Symmetric padding → equal space above and below the pill (≈8px),
17 centred between the two dividing lines. */
18 padding: 0.5rem 1rem;
19 max-width: 1200px; margin: 0 auto;
20 flex-wrap: wrap;
21}
22.view-switcher {
23 display: inline-flex;
24 background: var(--paper-2);
25 border: 1px solid var(--rule);
26 border-radius: 999px;
27 padding: 0.15rem;
28}
29.view-switch-btn,
30.view-switch-link {
31 display: inline-flex; align-items: center; gap: 0.35rem;
32 padding: 0.35rem 0.85rem;
33 border: 0; background: transparent; color: var(--ink-muted, var(--ink-soft));
34 cursor: pointer; border-radius: 999px;
35 font-size: 0.85rem; font-family: inherit;
36 transition: background 120ms, color 120ms;
37}
38.view-switch-btn:hover,
39.view-switch-link:hover { color: var(--ink); }
40.view-switch-link { text-decoration: none; }
41/* Active = white (light background, dark text). Via aria-selected (element
42 attribute, set by syncAria + on htmx:afterSettle) → htmx-robust. On non-
43 feed pages syncAria sets aria-selected=false → both grey. */
44.view-switch-btn[aria-selected="true"],
45.view-switch-link[aria-selected="true"] { background: var(--ink); color: var(--paper); border-color: var(--ink); }
46
47/* Agenda = its OWN pill (separate from Timeline/Grid, so the feed-toggle is never
48 disturbed). Same look as the view-switcher pill. Visible in all modes. */
49.agenda-pill { display: inline-flex; align-items: center; } /* no box — standalone icon+text link */
50.vs-agenda {
51 display: inline-flex; align-items: center; gap: 0.4rem;
52 padding: 0.35rem 0.5rem;
53 color: var(--ink-muted, var(--ink-soft));
54 text-decoration: none;
55 font-size: 0.9rem; font-family: inherit;
56 transition: color 120ms;
57}
58.vs-agenda:hover { color: var(--ink); }
59/* Active (on the agenda page) = bright/white. Element-class (in the OOB-rendered
60 chrome) rather than body-class CSS → stays reliable after htmx navigation. */
61.vs-agenda.is-active { color: var(--ink); font-weight: 600; }
62
63/* Mobile: Agenda on its own row at the top, Solo/Circle + Timeline/Grid together
64 on the row below (together ~330px → fits within a phone, instead of the old
65 unbalanced split Agenda+Solo / Timeline). */
66@media (max-width: 520px) {
67 .view-switcher-wrap .agenda-pill { flex: 0 0 100%; justify-content: center; }
68}
69
70.grid-cols-picker {
71 display: none; /* desktop-only, see media query */
72 gap: 0.2rem;
73}
74.grid-cols-btn {
75 width: 28px; height: 28px;
76 border: 1px solid var(--rule);
77 background: var(--paper-2);
78 color: var(--ink-muted, var(--ink-soft));
79 border-radius: 4px;
80 font-size: 0.8rem; font-weight: 600;
81 cursor: pointer;
82 font-variant-numeric: tabular-nums;
83}
84.grid-cols-btn:hover { color: var(--ink); border-color: var(--accent); }
85.grid-cols-btn.is-active { background: var(--accent); color: white; border-color: var(--accent); }
86
87/* Show grid-cols picker only on grid view + desktop */
88@media (min-width: 720px) {
89 body[data-feed-view="grid"] .grid-cols-picker { display: inline-flex; }
90}
91
92/* Switcher is hidden only on admin pages — admin has a dedicated layout
93 and its own back-button flow where Tijdlijn/Grid is irrelevant. On
94 every other page (home, post, tag, archive, search, account) it stays
95 visible so the user keeps a consistent return-to-feed control. */
96.on-admin .view-switcher-wrap { display: none; }
97
98/* On a standalone post there is no feed to switch, so no button should look "active"
99 (coloured). The active state lives in style.css on body[data-feed-view]
100 / body[data-grid-cols] (background:var(--ink)); we override that MORE SPECIFICALLY
101 via body.on-post so the buttons become neutral/grey — but remain clickable
102 (the click-handler then navigates to the feed in that view). The
103 body-class switches client-side on HTMX-nav, so this works without a reload too. */
104body.on-post[data-feed-view="timeline"] .view-switch-btn[data-view="timeline"],
105body.on-post[data-feed-view="grid"] .view-switch-btn[data-view="grid"],
106body.on-post[data-grid-cols="2"] .grid-cols-btn[data-cols="2"],
107body.on-post[data-grid-cols="3"] .grid-cols-btn[data-cols="3"],
108body.on-post[data-grid-cols="4"] .grid-cols-btn[data-cols="4"] {
109 background: transparent;
110 color: var(--ink-muted, var(--ink-soft));
111 border-color: var(--rule);
112}
113
114
115/* Spacing between switcher-bar and the page content below.
116 .feed-timeline (used on home + archive + post-list pages) gets a top
117 margin so the first post doesn't collide with the switcher's border.
118 .feed-grid already has its own top spacing from grid-tile padding. */
119.view-switcher-wrap + #pcms-main .feed-timeline,
120.view-switcher-wrap + #pcms-main .container:first-child {
121 margin-top: 1.5rem;
122}
123
124/* Feed layout switching is in the v9 style.css (style.css):
125 .feed-timeline always rendered, .feed-grid hidden unless on-home + grid-mode. */
126
127/* ─────────────────────────────────────────────────
128 PWA install button (lives in topnav, hidden by default)
129 ───────────────────────────────────────────────── */
130#pwa-install-btn { color: var(--accent); }
131#pwa-install-btn:hover { background: var(--paper-2); }
132
133/* Shared button + form styles (used across pages) */
134.btn {
135 display: inline-flex;
136 align-items: center;
137 gap: 0.4rem;
138 padding: 0.55rem 1rem;
139 border: 1px solid var(--rule);
140 background: var(--paper-2);
141 color: var(--ink);
142 font-family: var(--font-ui, system-ui), sans-serif;
143 font-size: 0.9rem;
144 font-weight: 500;
145 text-decoration: none;
146 border-radius: 5px;
147 cursor: pointer;
148 transition: background 150ms, border-color 150ms;
149}
150.btn:hover { border-color: var(--accent); }
151.btn-primary { background: var(--accent); color: white; border-color: var(--accent); }
152.btn-primary:hover { opacity: 0.92; border-color: var(--accent); }
153.btn-success { background: #16a34a; color: white; border-color: #16a34a; }
154.btn-danger { background: #dc2626; color: white; border-color: #dc2626; }
155
156.alert { padding: 0.75rem 1rem; border-radius: 5px; margin-bottom: 1rem; }
157.alert-error { background: #fee2e2; color: #991b1b; border: 1px solid #fecaca; }
158.alert-success { background: #d1fae5; color: #065f46; border: 1px solid #a7f3d0; }
159
160.auth-page { max-width: 420px; margin: 3rem auto; padding: 0 1rem; }
161.auth-page h1 { font-family: var(--font-display, serif); text-align: center; margin: 0 0 1.5rem; }
162.auth-form { display: flex; flex-direction: column; gap: 1rem; }
163.auth-form label { display: flex; flex-direction: column; gap: 0.3rem; }
164.auth-form label > span { font-size: 0.85rem; font-weight: 600; color: var(--ink-soft); }
165.auth-form input { padding: 0.6rem 0.75rem; border: 1px solid var(--rule); border-radius: 4px; background: var(--paper); color: var(--ink); font-size: 1rem; }
166.auth-form .btn { justify-content: center; padding: 0.7rem; font-size: 1rem; margin-top: 0.5rem; }
167.auth-link { text-align: center; color: var(--ink-muted); font-size: 0.9rem; margin: 1rem 0 0; }
168.auth-link a { color: var(--accent); }
169.auth-intro { text-align: center; color: var(--ink-muted); font-size: 0.95rem; margin: 0 0 1rem; }
170.auth-divider { display: flex; align-items: center; gap: 0.75rem; margin: 1.25rem 0; color: var(--ink-muted); font-size: 0.85rem; }
171.auth-divider::before, .auth-divider::after { content: ''; flex: 1; height: 1px; background: var(--rule); }
172
173/* HTMX loading indicator */
174.pcms-loading {
175 display: none;
176 position: fixed;
177 top: 60px;
178 right: 1rem;
179 width: 24px;
180 height: 24px;
181 border: 3px solid var(--paper-2);
182 border-top-color: var(--accent);
183 border-radius: 50%;
184 animation: spin 0.8s linear infinite;
185 z-index: 1000;
186}
187.htmx-request .pcms-loading { display: block; }
188@keyframes spin { to { transform: rotate(360deg); } }
189
190/* Load more (Solo, Cirkel, News, Messages) — page size 72, htmx append. */
191.load-more-wrap { display: flex; justify-content: center; margin: 1.5rem 0 .5rem; }
192.load-more-wrap:empty { margin: 0; }
193.load-more-btn { padding: .6rem 1.4rem; border-radius: 999px; cursor: pointer;
194 font: inherit; font-weight: 600; color: var(--ink, inherit);
195 background: color-mix(in srgb, var(--ink, #000) 5%, transparent);
196 border: 1px solid color-mix(in srgb, var(--ink, #000) 14%, transparent); }
197.load-more-btn:hover { background: color-mix(in srgb, var(--ink, #000) 9%, transparent); }
198.load-more-btn.htmx-request { opacity: .6; pointer-events: none; }
199
200/* FEP-9098 custom emojis: small inline images (Shaer parity), rendered in note
201 content and display names across the timeline, messages and threads. Never
202 styled as media (no rounding/background), and never blown up to media width. */
203img.emoji { height: 1.3em; width: auto; margin: 0 .05em; vertical-align: -0.2em;
204 display: inline-block; border-radius: 0; background: none; box-shadow: none; }
205.tl-content img.emoji, .tl-quote-body img.emoji, .msg-content img.emoji, .comment-content img.emoji,
206.tl-author img.emoji, .tl-boost-by img.emoji, .tl-quote-name img.emoji, .msg-who img.emoji, .comment-author img.emoji {
207 max-width: none; height: 1.3em; border-radius: 0; background: none; box-shadow: none; vertical-align: -0.2em; }
208
209/* ── The body of a post ────────────────────────────────────────────────────
210 Shared with partials/note-body.ejs, so a post looks the same in de Krant, in
211 Berichten and in the Guardian PWA. These used to live in the Krant's own
212 <style> block, which is why a post outside de Krant came out unstyled.
213 Krant-only behaviour (the clamp + "read more", the poll, the action bar)
214 stays in pages/news.ejs. */
215.tl-content { line-height: 1.55; overflow-wrap: anywhere; }
216.tl-content p { margin: .4rem 0; } .tl-content p:first-child { margin-top: 0; } .tl-content p:last-child { margin-bottom: 0; }
217.tl-content a { color: var(--accent, #06c); }
218.tl-content img { max-width: 100%; height: auto; border-radius: 10px; background: #fff; }
219
220/* FEP-044f embedded quote card (mirror of the Shaer QuoteCard). Also carries
221 an external link preview: same card, different origin. */
222.tl-quote { margin: .75rem 0 0; padding: .6rem .7rem; border-radius: 12px;
223 border: 1px solid color-mix(in srgb, var(--ink, #000) 12%, transparent);
224 background: color-mix(in srgb, var(--ink, #000) 3.5%, transparent); }
225.tl-quote-head { display: flex; align-items: center; gap: .4rem; margin: 0 0 .35rem; min-width: 0; }
226.tl-quote-avatar { flex: 0 0 auto; width: 22px; height: 22px; border-radius: 50%; object-fit: cover; background: #fff; }
227.tl-quote-name { font-weight: 700; font-size: .85rem; color: var(--ink, inherit); white-space: nowrap; }
228.tl-quote-handle { color: var(--ink-soft, #888); font-size: .78rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; }
229.tl-quote-body { line-height: 1.5; font-size: .92rem; color: var(--ink-soft, #555);
230 overflow-wrap: anywhere; max-height: 16em; overflow: hidden; }
231.tl-quote-body p { margin: .3rem 0; } .tl-quote-body p:first-child { margin-top: 0; } .tl-quote-body p:last-child { margin-bottom: 0; }
232.tl-quote-body a { color: var(--accent, #06c); }
233.tl-quote-media { display: block; margin: .45rem 0 0; }
234.tl-quote-media img { max-width: 100%; height: auto; border-radius: 8px; }
235
236/* Always show the FULL image at its natural ratio — never cropped, no letterbox. */
237.tl-media { display: flex; flex-direction: column; gap: .4rem; margin: .75rem 0 0; }
238.tl-media-img { display: block; border-radius: 12px; overflow: hidden;
239 background-image: linear-gradient(135deg,
240 color-mix(in srgb, var(--accent, #888) 22%, var(--paper-2, var(--paper))) 0%,
241 color-mix(in srgb, var(--accent, #888) 6%, var(--paper-2, var(--paper))) 100%); }
242.tl-media-img img { width: 100%; height: auto; display: block; background: #fff; transition: opacity .4s ease; }
243.tl-media-img img.is-loading { opacity: 0; }
244/* Reserve placeholder space while the (natural-aspect) feed image loads. */
245.tl-media-img:has(img.is-loading) { min-height: 220px; }
246.tl-media-video, .tl-media-audio { width: 100%; margin: .75rem 0 0; border-radius: 12px; display: block; }
247.tl-media-video { max-height: 480px; background: #000; }
248
249/* In Berichten a post sits inside a notification row, so it starts tighter and
250 its media stays modest: the row is a pointer to the post, not the post page. */
251.msg-note .tl-content { line-height: 1.5; }
252.msg-note .tl-quote, .msg-note .tl-media, .msg-note .tl-media-video, .msg-note .tl-media-audio { margin-top: .5rem; }
253.msg-note .tl-media-img:has(img.is-loading) { min-height: 120px; }
254.msg-note .tl-media-video { max-height: 320px; }
255</style>
Note: See TracBrowser for help on using the repository browser.