Both documents had drifted away from the repository they describe: they
pointed at fixtures and a helper script that are not present, documented a
local-server workflow that is no longer used, and filed one page type under
the wrong view. Following them meant setting up things that cannot work while
trusting claims that no longer held.
The README also explained every design decision before showing a single
result, so the question a reader asks first — what does this actually look
like — took the longest to answer. Screenshots and a short feature list now
carry that job. The reasoning stays where it is needed, next to the code.
Restricting the dev loader to the live site closes a side effect that only
surfaced locally: its patterns matched page names common enough that the
stylesheet would have leaked into unrelated local projects.
The version number existed in two places and had already drifted apart.
Reading it back from the metadata block leaves one place to change it.
The server renders search.php with no forum preselected and "Search
subforums" set to Yes, which searches the whole board by default. Most
searches from the mobile view are scoped to Main Forum only, so leaving
the server defaults in place meant re-selecting the same options on
every visit.
search.php had no mobile treatment: the query form rendered as an
unstyled desktop table, and a submitted search fell back to the regular
forum-listing view on the untested assumption that results looked like
topic rows.
The query form is now recognised by its forum-picker select, the one
element the small "search this forum/topic" widgets embedded on other
pages don't share — they reuse the same form action and keyword field
name and would otherwise be picked up by mistake. Results in the "Posts"
display mode the in-thread search bar produces turned out to carry post
excerpts, not topic rows, and needed their own card layout instead of
running through the list view, which would have misread the topic and
profile links inside each hit.
Submit/reset controls kept native iOS Safari chrome even under
-webkit-appearance:none once restyled, clipping their labels — only
visible on device, not in the emulator this project is developed
against. Replacing them with real <button> elements (same name/value,
so the submitted form data is unchanged) fixed it where the CSS-only
attempt didn't; touch targets were sized up to match.
The list view already handled every table row shape a forum page uses,
but the switch only ever routed viewforum.php and search.php into it;
index.php arrived untouched, so the very first page a visitor lands on
stayed in the desktop layout. It matches the same category/forum row
structure a viewforum.php category page already produces, so no new
building logic was needed, only the routing.
Submitting the login form reveals a second gap: it re-renders the same
URL with a generic confirmation page instead of the form, which the
existing form-lookup silently ignored, leaving that page unstyled too
right before phpBB's own redirect fires. It shares its shape with any
other message phpBB might render there (a wrong password, for example),
so the fix recognises it structurally rather than by its wording, and
carries the original message cell over unchanged the same way the login
form itself is reused rather than rebuilt.
The script previously had no way to log in or tell whether a session
was already active, since ucp.php fell outside the view switch and
rendered in the desktop layout. Session state is only visible in the
forum's own menu bar link (mode=login vs mode=logout), so every view
now surfaces it as a pill in the breadcrumb bar instead of leaving the
user to dig for it.
The login form itself is moved into the new layout rather than rebuilt
from scratch, keeping the hidden sid/redirect fields and the
autologin/viewonline checkboxes intact without the script having to
reimplement the session handshake. As the first short, unscrollable
view in the project, it also exposed a WebKit quirk that longer views
never triggered: without enough scroll headroom, Safari's own
partially-retracted toolbar has nothing to scroll away and permanently
covers the submit button. The generous bottom padding on the form
works around that, separately from the safe-area inset that only
accounts for the home indicator.
The list built its own page bar from every link on the page carrying a
start parameter. Topic rows bring their own "Go to page: 1 … 42, 43" along,
so on the main forum that bar came to 103 entries, most of them jumping into
the middle of some unrelated thread rather than to a page of the list. Even
the correct entries were only the handful phpBB renders, which for 610 pages
means there is no way to reach page 300.
The thread already solved this: read per_page out of the page's own jumpto
function, compute every target, and offer them through a fixed bar and a
sheet. Both views paginate over the same parameter, so the bar, the sheet
and the state behind them now sit in one place and serve both. The list
leaves the bar out where a single page holds everything, which a thread of
posts never does but a category page always does.
Bottom clearance moved onto the bar for that reason — tied to the view, it
would reserve an empty strip under lists that have no bar.
Every page of a forum repeats its global announcements, announcements and
stickies before the first real topic. On a phone that is the whole first
screen, scrolled past on every page turn, and the rebuild made it worse: the
board separates those blocks with header rows the card stream dropped, so
what remained was one undifferentiated run of cards.
Each block now carries a header with its name and card count, and tapping it
folds the block away. The announcement blocks start folded, which is the
point of the change; folding rather than filtering keeps them reachable
without editing the script. The state is kept per kind, so a decision taken
in one forum holds in all of them.
localStorage rather than GM_setValue: no @grant, and identical behaviour in
Tampermonkey and the iOS app, against the risk that Safari clears the value
after long inactivity and the sections return to their default. That is a
reset of one preference, not data loss.
Rows are classified by the icon in their first column instead of a title
prefix, because the board marks the type nowhere else. The header labels come
from the board's own separator rows, so sections nobody anticipated still
read correctly.
The menu bar links the forum rules and the FAQ as topics. That row has
neither a topic title nor numeric columns, but it passed the layout brake and
had been yielding a ghost card at the head of every list — visible now that
it would claim a section header of its own.
A forum page carries its subforums and its topics in tables of their own,
each with a header. Rebuilding the page into one stream of cards dissolves
that separation: both kinds render identically, so where the subforums end
and the topics begin was no longer visible, and a single card gave no hint
whether tapping it opens a forum or a thread.
A header naming the kind and its count is therefore inserted wherever the
kind changes, and forum cards are tinted and edged. Directly below a
category heading the header is suppressed — on the forum root that heading
already introduces the forums under it, and repeating the kind there adds
a strip per category for nothing.
Counting requires knowing how many cards a block actually yields, which is
not the number of rows: rows that turn out to be layout or fail conversion
drop out. The cards are therefore built completely before any of them is
emitted.
A viewforum page is not always a list of topics — a category page lists
forums, and a forum page can carry both. Every row was treated as a topic,
so on those pages the only link the row offered was the icon in its last-post
column: the forum name was dropped, its description ended up in the muted
footer line, and nothing was left to tap. Moving between forums on a phone
was impossible.
Rows are now classified by their first usable link, which relies on document
order: a forum row names the forum before it links the last post. Detection
prefers phpBB's own classes and falls back to the shape of the row, because
the board runs a custom theme whose markup could not be verified — the page
requires a login.
That fallback needs a brake. The page scaffolding is itself a table, so a
link outside any list row resolves to a row containing the whole document,
which passed for a forum row and turned the page header, a stylesheet and
inline scripts into a forum description. Rows carrying nested tables, forms
or scripts are therefore rejected, and descriptions are read only from the
title cell's own text.
Replacing the page hides the forum's own trail along with everything else,
so a thread became a dead end: the only ways back to the parent forum were
the browser's back button or editing the URL by hand. Deep in a long thread
neither is workable.
The bar has to be fixed rather than sticky. The view root carries
overflow:hidden to contain phantom horizontal overflow, which would make it
the scrollport a sticky bar sticks to — one that never scrolls. Its top
clearance hangs off a body class so pages without a trail keep their full
height instead of an empty strip.
Board index still leads to a page the script does not rebuild; that would
need a forum overview of its own.
Debug flag off for daily use.
The list script matched the whole forum, thread pages included, and injected its
stylesheet there before bailing out of its own build. Both stylesheets were
therefore live on every thread, and rules sharing a class name resolved by
userscript load order rather than by intent — a layout that could differ between
two machines with the same files installed. One of those clashes was already
costing the clearance behind the fixed bar.
A single script picks its view from the page type instead, which turns the
shared class namespace from a hazard into a plain fact of the file, resolved
with a modifier on the root element. Pages outside either view now stay
untouched, where before they still received a full stylesheet.
Two things the merge brought to light: the global image rule was overriding the
avatar dimensions, which only went unnoticed because square avatars happen to
scale to the same result, and a zero-width space had been sitting in the source
as a literal character rather than an escape.
Installations carrying the old thread script have to remove it; the remaining
one keeps its name and updates in place.
Claude-Session: https://claude.ai/code/session_0158owAAsXAfFiT3hGkQt1Xk
Threads on this forum reach four-digit page counts, and stepping one page at a
time was the only movement the navigation bar offered. The end of a thread, or
any specific page, was effectively unreachable on a phone.
Jump targets are derived arithmetically from the forum's own page size instead
of read from the rendered pagination, because phpBB never renders more than a
handful of page links regardless of thread length. Where that page size cannot
be determined, the bar falls back to the links the forum itself provides and
hides the jump.
Both userscripts inject their stylesheet on thread pages, so class names are a
shared namespace between them. The new ones avoid the two the list view already
uses, which would otherwise collide at equal specificity and resolve by load
order.
Claude-Session: https://claude.ai/code/session_0158owAAsXAfFiT3hGkQt1Xk
The forum nests each post body inside its own table, so ascending to the
nearest enclosing row stopped within that inner table rather than at the
actual post row. The author and profile columns were never in scope, and
every post silently degraded to the placeholder name and the generated
initial avatar instead of the real ones.
Author lookup additionally keyed on a profile link attribute this forum
does not emit at all, so even a correct row would have yielded nothing.
Both conditions failed quietly, which made the result look like a styling
problem rather than a broken DOM traversal.