source: Klonkt/src/views/partials/view-switcher.ejs

main
Last change on this file 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: 6.5 KB
Line 
1<%# View switcher partial — Tijdlijn / Grid + grid-cols picker.
2 Mounted in shell.ejs once, visible on every non-admin page. Click
3 handlers are wired in shell.ejs via event-delegation on document so
4 HTMX-swapped content keeps working without rebinding. %>
5<%
6 var _bc = (typeof bodyClass === 'string') ? bodyClass : '';
7 var _onShows = _bc.indexOf('on-shows') >= 0;
8 // Pagina's zonder actieve feed-weergave → niets in de pill is 'actief' (Solo/Cirkel
9 // + Tijdlijn/Grid grijs). Alleen Agenda krijgt z'n eigen is-active op de agenda.
10 var _neutral = ['on-shows','on-downloads','on-post','on-epk','on-linkbio','on-newsletter','on-archive']
11 .some(function(c){ return _bc.indexOf(c) >= 0; });
12 // Lezen bestaat alleen waar een leessectie is, en dat is de eigen feed. De
13 // cirkel toont berichten van ANDEREN, opgehaald van hun servers -- daar is
14 // "het hele stuk" niet iets wat wij in handen hebben.
15 //
16 // De knop verdween daar eerst helemaal, met het argument dat een knop die
17 // niets doet erger is dan geen knop. In de praktijk pakte dat andersom uit:
18 // de pil werd smaller op de cirkel en breder op solo, en die sprong leest als
19 // "er is hier iets weg" -- terwijl juist NIET duidelijk werd dat Lezen
20 // bestaat en hier alleen niet kan. Hij staat er nu dood bij: zichtbaar, grijs
21 // en niet aan te klikken. Dan is er maar een mogelijkheid, Grid, en je ziet
22 // meteen dat dat geen toeval is maar de enige.
23 var _leesUit = _bc.indexOf('on-cirkel') >= 0;
24 var _gridStandaard = (typeof site !== 'undefined' && site && site.feed_view_default === 'grid');
25 // Welke tweede weergave biedt deze site aan? Bij 'auto' tekenen we ZE ALLEBEI
26 // en laat de CSS er per viewport een zien -- Lezen op mobiel, Tijdlijn op
27 // desktop. Dat scheelt een resize-afhandeling en een knop die van naam
28 // verspringt terwijl je kijkt.
29 var _alt = (typeof site !== 'undefined' && site && site.feed_alt_view) || 'reader';
30 if (['reader', 'timeline', 'auto'].indexOf(_alt) < 0) _alt = 'reader';
31 var _tweede = _alt === 'auto' ? ['reader', 'timeline'] : [_alt];
32 // De tijdlijn WERKT wel in de cirkel -- dat is er zelfs de natuurlijke vorm.
33 // Alleen Lezen kan daar niet, want dat zijn berichten van anderen.
34 var _dood = function (v) { return v === 'reader' && _leesUit; };
35 // Staat er GEEN levende tweede knop, dan is Grid het enige dat overblijft en
36 // hoort die aan te staan. Bij een tijdlijn-site is dat in de cirkel niet zo:
37 // daar werkt de tijdlijn gewoon.
38 var _alleenGrid = _tweede.every(_dood);
39 var _icoon = {
40 reader: '<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z"/><path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z"/>',
41 timeline: '<line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/>',
42 };
43%>
44<div class="view-switcher-wrap">
45 <div class="container">
46 <% if (typeof premiumUnlocked !== 'undefined' && premiumUnlocked && typeof agendaEnabled !== 'undefined' && agendaEnabled) {
47 var _sbA = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
48 %>
49 <div class="agenda-pill">
50 <a class="vs-agenda<%= _onShows ? ' is-active' : '' %>" href="<%= _sbA %>/shows" aria-label="Agenda — evenementen">
51 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="4" width="18" height="18" rx="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>
52 <span><%= t('switch.agenda') %></span>
53 </a>
54 </div>
55 <% } %>
56 <% if (typeof hasCirkel !== 'undefined' && hasCirkel) {
57 var _onCirkel = (typeof bodyClass === 'string' && bodyClass.indexOf('on-cirkel') >= 0);
58 var _sb = (typeof siteUrlBase !== 'undefined' && siteUrlBase) ? siteUrlBase : '';
59 %>
60 <div class="feed-scope" role="tablist" aria-label="Solo of cirkel">
61 <a class="feed-scope-btn<%= (!_neutral && !_onCirkel) ? ' is-active' : '' %>" href="<%= _sb %>/"
62 role="tab" aria-selected="<%= (!_neutral && !_onCirkel) ? 'true' : 'false' %>"><%= t('switch.solo') %></a>
63 <a class="feed-scope-btn<%= (!_neutral && _onCirkel) ? ' is-active' : '' %>" href="<%= _sb %>/cirkel"
64 role="tab" aria-selected="<%= (!_neutral && _onCirkel) ? 'true' : 'false' %>"><%= t('switch.circle') %></a>
65 </div>
66 <% } %>
67 <div class="view-switcher" role="tablist" aria-label="Weergave">
68
69 <%# De tweede weergave naast Grid. Welke dat is, staat in de instellingen
70 van de site (feed_alt_view). Bij 'auto' staan ze er allebei en kiest
71 een mediaquery; vandaar de klasse vs-alleen-*. %>
72 <% _tweede.forEach(function (v) { %>
73 <button type="button" class="view-switch-btn<%= _alt === 'auto' ? ' vs-alleen-' + (v === 'reader' ? 'mobiel' : 'desktop') : '' %>"
74 data-view="<%= v %>" role="tab"
75 <%- _dood(v) ? 'disabled aria-disabled="true" tabindex="-1" title="' + t('switch.reader_solo_only') + '"' : '' %>
76 aria-selected="<%= (!_dood(v) && !_neutral && !_gridStandaard) ? 'true' : 'false' %>">
77 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><%- _icoon[v] %></svg>
78 <span><%= t('switch.' + v) %></span>
79 </button>
80 <% }); %>
81 <button type="button" class="view-switch-btn" data-view="grid" role="tab"
82 aria-selected="<%= (!_neutral && (_alleenGrid || _gridStandaard)) ? 'true' : 'false' %>">
83 <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/></svg>
84 <span><%= t('switch.grid') %></span>
85 </button>
86 </div>
87 <% if (!_neutral) { %>
88 <div class="grid-cols-picker" aria-label="Aantal kolommen">
89 <button type="button" class="grid-cols-btn" data-cols="2" title="2 kolommen">2</button>
90 <button type="button" class="grid-cols-btn" data-cols="3" title="3 kolommen">3</button>
91 <button type="button" class="grid-cols-btn" data-cols="4" title="4 kolommen">4</button>
92 </div>
93 <% } %>
94 </div>
95</div>
Note: See TracBrowser for help on using the repository browser.