source: Klonkt/src/views/pages/home.ejs@ e46ebaa

main
Last change on this file since e46ebaa was e46ebaa, checked in by Robin <roboburr@…>, 3 weeks ago

Lezen: snappen dat je laat lezen, en geen lege schermen meer

Barts meldingen van 20 augustus, in een stuk of zes rondes bijgesteld. De
uitkomst is korter dan de weg ernaartoe, en die weg is het waard om vast te
leggen omdat ik er twee keer overheen ben geschoten.

SNAPPEN. De regel is nu in drie zinnen te zeggen:

omhoog scrollen nooit snappen -- je zoekt iets terug en bepaalt zelf

waar je stopt

omlaag, ver weg vrij scrollen
omlaag, laatste
150px van een
bericht vangt, en je landt op de bovenkant van het volgende

Er is geen top-zone: de bovenkant van het bericht waar je IN zit is geen
doelwit meer. Dat was de oorspronkelijke klacht -- je las een paar regels
verder en proximity trok je terug naar de bovenrand, waardoor het leek of
het scrollen vastliep. De grens die wel vangt is de onderkant van je huidige
bericht, en dat is hetzelfde punt als de bovenkant van het volgende.

Onderweg twee doodlopende wegen, allebei door Robin gecorrigeerd:

  • Eerst zette ik de snap UIT voor berichten langer dan het scherm. Op een telefoon is bijna elk bericht net iets langer dan de viewport, dus daar verdween het snappen overal: op 812px hoog kregen berichten van 1009 en zelfs 839 al geen snap meer.
  • Toen probeerde ik de vangzone aan de VOET van het bericht te hangen ("ben je de reacties voorbij"). Bij een kort bericht staat die voet middenin de lege ruimte die min-height maakte, dus de zone begon op een willekeurige plek. Gelukkig ving Robin dat voordat het uitgerold stond.

Het moest dus een getal in pixels zijn, en 150 is dat getal.

GEEN LEGE SCHERMEN MEER. min-height:100svh rekte elk bericht op tot een vol
scherm. Bij een kort bericht gaf dat een halve pagina leegte onder "Replies
and reactions" -- je keek naar niets en moest doorscrollen om te ontdekken
dat er nog iets kwam. Weg dus: berichten hebben hun eigen hoogte (gemeten:
360/1009/839/250 waar het eerst 888/888/888/888 was) en de stroom is gewoon
bericht na bericht. Het snappen hangt aan scroll-snap-align en niet aan die
hoogte, dus dat werkt onveranderd.

VLOEIEND: scroll-behavior:smooth, want zonder dat springt een snap er in een
frame naartoe en voelt het als een hik. Uit bij prefers-reduced-motion.

TERUG NAAR BOVEN, rechtsonder, in twee stappen: eerst naar het begin van DIT
bericht, daarna pas naar de kop van de pagina. Die knop is de enige die naar
een bovenkant mag springen, en hij heeft het snappen daar niet voor nodig --
scrollIntoView stuurt zelf. Nagemeten, juist omdat omhoog-snappen uit staat:
1706 -> 1206, exact de bovenkant.

COVERS nemen de volle kolombreedte met de verhouding intact. De ronde ervoor
haalde het opblazen eruit maar liet kleine afbeeldingen op ware grootte
staan, en dat was als postzegel in een brede kolom te klein.

DE CIRKEL. Lezen blijft daar dood (berichten van anderen), maar een tijdlijn
werkt er juist bij uitstek. Die dwong ik eerst af zodra de site geen
Lezen-site was, en dan kon je in de cirkel geen Grid meer kiezen. Nu is Grid
de landing en staat Tijdlijn ernaast: bij feed_alt_view=timeline altijd, bij
auto op desktop. De cirkel krijgt zijn eigen geheugen terug, met een eigen
sleutel zodat een keuze daar je thuisweergave niet aanraakt.

style.css naar v77 en MOD_V naar 13.

Suite 1165/1165.

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

  • Property mode set to 100644
File size: 4.4 KB
Line 
1<%
2// Homepage — feed of posts.
3//
4// Renders BOTH a timeline view (.post-list, default) AND a grid view
5// (.feed-grid, square Instagram-style tiles). CSS toggles between the
6// two based on body[data-feed-view="timeline"|"grid"]. This mirrors the
7// v9 pattern in lib/template-content.php so the same view-switcher works.
8//
9// Pinned posts sort first inside the timeline list; they are not given a
10// separate "Pinned" header (v9 just lets order speak for itself + a ★ chip).
11
12const allPosts = [...pinnedPosts, ...posts];
13const _base = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
14%>
15
16<%# View switcher used to be rendered here; moved to shell.ejs (above
17 #pcms-main) so it survives HTMX swaps and is visible on every
18 non-admin page. %>
19
20<%# FEP-7628 slice 3: this account moved. The signpost comes FIRST, before
21 any post: a visitor should not scroll a dead feed to find out we left.
22 typeof-guard because an old route may render this template for one
23 request right after a deploy (view cache fills on first hit). %>
24<% if (typeof movedTo !== 'undefined' && movedTo) { %>
25 <div class="container">
26 <aside class="moved-banner">
27 <p class="moved-banner-lead"><%= t('phome.moved_lead') %></p>
28 <a class="moved-banner-link" href="<%= movedTo %>" rel="me noopener"><%= movedToLabel || movedTo %></a>
29 <p class="moved-banner-hint"><%= t('phome.moved_hint') %></p>
30 </aside>
31 </div>
32<% } %>
33
34<!-- ========== TIMELINE ========== -->
35<div class="container feed-timeline">
36
37 <% if (allPosts.length === 0) { %>
38 <div class="empty">
39 <h1><%= t('phome.empty_title') %></h1>
40 <p><%= t('phome.empty_sub') %></p>
41 <% if (user && canMutate && permissions.canCreatePost(user, site)) { %>
42 <p style="margin-top:2rem">
43 <a class="btn" href="<%= _base %>/posts/new"
44 hx-get="<%= _base %>/posts/new?partial=1" hx-target="#pcms-main" hx-swap="innerHTML"
45 hx-push-url="<%= _base %>/posts/new" hx-indicator="#pcms-loading">
46 <%= t('phome.write_first') %> →
47 </a>
48 </p>
49 <% } %>
50 </div>
51 <% } else { %>
52 <ul class="post-list feed-timeline-list" id="post-list">
53 <% allPosts.forEach(function(post) { %>
54 <%- include('../partials/post-card', { post: post }) %>
55 <% }); %>
56 </ul>
57 <% } %>
58
59</div>
60
61<!-- ========== LEZEN (alleen zichtbaar bij body[data-feed-view="reader"]) ==========
62 Hetzelfde stel berichten, maar heel: één per scherm, met de grenzen door
63 CSS-snapping. Geen eigen route en geen eigen shell -- de balken blijven
64 staan, want je bent nog gewoon op de feed en moet terug kunnen schakelen.
65 Leeg? Dan valt de lege staat van de tijdlijn hierboven in beeld; deze
66 sectie rendert dan niet. -->
67<% if (typeof readerItems !== 'undefined' && readerItems && readerItems.length) { %>
68<section class="feed-reader" aria-label="<%= t('switch.reader') %>">
69 <%# Terug naar boven. Staat IN de HTML en niet in JavaScript, zodat hij er ook
70 is als de module niet laadt -- dan doet hij niets, maar hij springt ook niet
71 opeens in beeld. Verschijnt pas als je ver genoeg bent (mod/read.js). %>
72 <button type="button" id="read-top" class="read-top" aria-label="<%= t('read.to_top') %>" title="<%= t('read.to_top') %>">
73 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><line x1="12" y1="19" x2="12" y2="5"/><polyline points="5 12 12 5 19 12"/></svg>
74 </button>
75 <div class="read-stream" id="read-stream" data-base="<%= _base %>">
76 <% readerItems.forEach(function(it) { %>
77 <%- include('../partials/read-article', { post: it.post, entry: it.entry }) %>
78 <% }); %>
79 </div>
80</section>
81<% } %>
82
83<!-- ========== GRID (only visible when body[data-feed-view="grid"]) ========== -->
84<% if (allPosts.length > 0) { %>
85<section class="feed-grid container" aria-label="<%= t('phome.grid_view') %>">
86 <div class="grid-tiles" id="grid-tiles">
87 <% allPosts.forEach(function(post) { %>
88 <%- include('../partials/post-tile', { post: post }) %>
89 <% }); %>
90 </div>
91</section>
92<% } %>
93<% if (typeof hasMore !== 'undefined' && allPosts.length > 0) { %>
94<div class="container feed-more-wrap">
95 <%- include('../partials/load-more', { hasMore: hasMore, nextOffset: nextOffset, moreBase: moreBase, moreTarget: '#post-list', morePath: '/' }) %>
96</div>
97<% } %>
Note: See TracBrowser for help on using the repository browser.