UI redesign: implementation checklist

A backlog of cycles for building the redesign and its theme from the mockups. It is a planning input, not a Railspike artifact. Each cycle is marked with the process it uses (see below); Railspike cycles still go through Railspike's steps with an explicit approval at each gate, and may be reshaped during their own Step 1 or Step 2. Context and decisions are in next_steps.md.

Process per cycle

Decided by the user on 2026-10-26. The mockups and this checklist already cover much of Railspike's problem framing and requirements, so Railspike is kept where there are real unknowns or high stakes. Marking a cycle Direct is the user's explicit Railspike opt-out for that cycle under AGENTS.md.

ProcessCyclesReason
Railspike (start with Step 1, solution assessment)1, 5, T1Open design choices: a new navigation surface across every theme (1), key generation and signing (5), registering a day/night theme (T1).
Railspike, no Step 1 (start with Step 2)3, 4, 8, T2, D1, D2, D3Clear direction but risky behavior: the docked reply box on phones, backups, encrypted message failures, hooks into decryption and verification, read-model schema changes, security UX.
Direct (no Railspike)2, 6, 7, 9, T4Rearranging markup and wording on screens whose behavior already exists, or a small settings change (T4).
Decide when scheduledT3, D4Optional or deferred.

For a Direct cycle:

  • Work on its own branch (no direct merges to main).

  • Use this checklist's items for the cycle as the spec; record any change of scope here rather than silently expanding it.

  • Apply every check in Checks that apply to every cycle, and record UI verification in docs/plans/ui_copy_audit_checklist.md or a short note in this folder.

  • Commit when the cycle's "Done when" is met. Direct cycles are not part of the railspike-effort study, which only collects Railspike cycles.

Sizing follows Railspike's rules for every cycle, Direct ones included: each is a releasable vertical slice (normal entry, end-to-end outcome, required recovery) that fits within a day or eight Step 3 stages. The item lists below are suggestions for Step 3 (or the spec, for Direct cycles), not approved plans.

Mockup references are relative to mockups/. Template paths are from plan_prompt.md.

Before the first cycle

Checks that apply to every cycle

Copy these into each Railspike cycle's Step 3 verification and Step 4 summary; Direct cycles apply them before committing.

Phase 1: Base structure

Wording, arrangement and flow from round 1, theme-independent. Each cycle also adds its screen's theme hooks.

Cycle 1: ui_shell_navigation

Process: Railspike. Step 1 compares options: the phone tab bar is a new navigation surface, and moving rare destinations into the account menu has alternatives.

App shell and navigation. Mockup: shell.html. Templates: templates/layout.php, templates/partials/nav.php, templates/partials/theme_menu.php.

  • Done when: every page uses the new shell in every theme, with no lost destination. Done 2026-10-27, merged to main (5ac1432de).

  • Follow-up (2026-10-28, ui_tab_bar_scroll_away): on phones the tab bar slides away while scrolling down and returns on scroll up, at either page end, on focus and while the account menu is open, after the user reported that floating bars cover content and cannot be dismissed. Real-iPhone feel pending.

Cycle 2: ui_board_list

Process: Direct.

Mockup: board.html. Templates: pages/board.php, partials/thread_card.php. Depends on cycle 1.

  • Done when: browsing, filtering, sorting and paging work in all themes.

Built 2026-10-27 on feat/ui-board-list as a boardCard presentation choice list selected only by ZenMemes; Chouse and MIT Rap Club keep the card board, QDB its quote board. Decisions and limits:

  • Paging uses the shared native-link pagination (docs/reference/pagination.md): 50 latest threads per page, pinned threads on the first page only, "Show older threads" / "Newer threads" links, and First page after an expired or foreign position. It does not load more into the same list.

  • Sort is a native <details> menu ("Newest ▾"); filter links and sort items keep .nav-link[data-nav-key], so the icon themes' existing icons apply with no new mapping.

  • The board's own "New Post" link is gone (the shell has New post), matching ae0ab8859 on ui/remove-board-new-post.

  • Rows have no avatars. Excerpts are plain text and skip image references; the quick-compose row is unchanged (composers are cycle 6).

  • The flag-gated thread density toggle is not shown on the list board, which is already the dense view; it stays on tag pages and the card boards.

  • Unchanged: the offline reader's own board rendering, and pending-post insertion after the composer.

  • Verification: BoardListTest (paging both ways, pinned placement, recovery, empty Liked, sort menu, Pending chip, image excerpts, other sites unchanged); one full suite run (1594 run, failures limited to five that also fail on main plus three board tests since updated and passing); guest JavaScript on/off matrix at 1280px and 390px; all 13 themes plus Auto with the sort menu open on desktop and phone; board_compose_focus, listing_query_dialog, nav_active_theme_styles, feature_flags_toolbar, standard_activity_pagination pass. signed_reactions failed once and passed on rerun; compose_layout_stability also fails on main. Signed-in viewers and a real Pending author were checked through server-rendered tests, not in a browser.

Cycle 3: ui_thread_reply_dock

Process: Railspike, no Step 1.

Mockup: thread.html. Templates: pages/thread.php, partials/thread_root_card.php, partials/post_card.php, partials/reply_form.php. Depends on cycle 1.

  • Done when: reading, liking, replying and the agent states work on phone without the keyboard hiding the reply box.

Cycle 4: ui_account_states

Process: Railspike, no Step 1.

Mockup: account.html. Template: pages/account_key.php.

Built 2026-10-28 on feat/ui-account-states for all sites; see the Step 4 summary.

  • Done when: a member can see their state, back up, restore and reach advanced controls in all themes.

Cycle 5: ui_first_post_identity

Process: Railspike. Step 1 compares options for when and where keys are generated.

Mockups: compose-thread.html, account.html. Templates: pages/compose_thread.php, partials/thread_compose_form.php, pages/account_key.php, plus the browser signing scripts. Depends on cycle 4.

  • Done when: a new visitor can write and publish a first post with no browser dialogs and no long wait after pressing Post.

Built 2026-10-28 on feat/ui-first-post-identity for all sites (not merged); see the Step 4 summary. Readers no longer register keys. The real-iPhone check on ZenMemes is pending with the user.

Cycle 6: ui_composers

Process: Direct.

Mockups: compose-thread.html, compose-reply.html. Templates: pages/compose_thread.php, pages/compose_reply.php. Depends on cycle 5.

  • Done when: new threads and replies post from both pages, with and without JavaScript.

Built 2026-10-28 on feat/ui-composers:

  • partials/composer.php is the one composer. thread_compose_form.php and reply_form.php set its options and include it, so every composer on every site renders there: New post, reply page, board quick compose, thread inline reply, QDB add, and Forte's new post dialog and reply panel.

  • Order: Title ("Title — or we’ll use your first line", full New post only), body, a tools row (# tags and Event disclosures, which work without JavaScript), the "Post as" row, then Post and the ··· menu.

  • Wording from the mockups: New post / Post / Post reply; ··· menu: Post anonymously or Reply anonymously, Save to Outbox, Remove timestamps (QDB), Discard draft.

  • Reply page: "Reply to {name}" with the parent as a quote, "author · date · in {thread title}". Long parents are clamped to four lines, with Show more linking to the post. A missing parent says "That post was removed or isn’t in this thread. Reply to the thread instead."

  • Scope notes, deviations from the mockups, deferred:

    • Attach image stays in the ··· menu, not the tools row. It appears only once the browser knows the viewer may attach images, so in a toolbar it would shift the page after first paint; compose_layout_stability caught this.

    • The ··· menu stays beside Post, not in the tools row, so the phone keyboard panel keeps its layout.

    • Deferred: the phone header bar with Cancel/Post, the event card and tag chips (the existing fields are kept inside the tool disclosures), the offline "Save" mode, and "Keep draft / Discard" on Cancel.

Cycle 7: ui_profile_users

Process: Direct.

Mockups: profile.html, users.html. Templates: pages/profile.php, pages/username.php, pages/users.php.

  • Done when: finding a member, opening their profile and messaging or approving them works in all themes.

Built 2026-10-28 on feat/ui-profile-users:

  • Users: "Members | Waiting" tabs. Members is a compact list with one row per person ("N threads · M posts", an Agent chip), a search field and an Activity/Name sort; both work on the list already on the page and are hidden without JavaScript.

  • Waiting (/users/pending): it used to redirect approved members to User Access. It now shows the waiting list itself, with the same Approve buttons, beside Members, and the count updates in place after an approval. It stays approved-members-only (403 otherwise), so the Waiting tab, with its count, is rendered only for approved members; visitors without cookies get the static copy, which has no tabs, and other viewers see just "Users" (user request, 2026-10-28: don't show a tab people can't open). The empty state reads "No one is waiting."

  • Person page (/user/{name}): the heading shows the name, "N threads · M posts" and an Approved (or Automated reply agent) chip. Message opens /messages/conversation/{name}, which replaces the embedded send-only form; your own page shows "Edit in Account" instead. Threads | Replies is a tab pair, and each key is listed under Identities ("Approved by …"), linking to its key page. "Manage access" shows for operators only; it replaces the "User Access" link and the "Approved by: …" line.

  • Built from existing components (fixed after review; see the debug log): the tabs are the board's All | Liked filter (.board-filter); member rows and Identities are Account's grouped rows (.account-group / .account-row); the person heading is Account's card (.account-card, .account-id); threads use partials/thread_row.php, and replies use the same row markup. These pages load account.css and thread-list.css; the only CSS of their own is page layout in site.css.

  • Kept as is: the key page (/profiles/{slug}) keeps the presentation accepted on 2026-10-27 in profile_public_details: no name link, and Approval details beside Approved/Not approved. That decision is newer than the mockup.

  • Deferred: approving with a 5-second Undo before publishing (a proposal in the mockup that changes approval behavior), showing each waiting person's first post in their row, avatars, and the agent page's empty-state wording.

Cycle 8: ui_messages

Process: Railspike, no Step 1.

Mockups: messages.html, conversation.html. Templates: pages/private_messages.php, partials/private_message_list.php, pages/private_message_conversation.php, partials/private_message_composer.php.

  • Done when: reading, sending and recovering from each failure work on phone and desktop.

Cycle 9: ui_tools_launcher

Process: Direct.

Mockup: tools.html. Templates: pages/tools.php, partials/tools_nav.php.

  • Done when: every tool is reachable once, in all themes.

Phase 2: Theme hooks

There is no separate cycle. Hooks ship with the base cycle for their screen (listed above). Before Phase 3, confirm the full set exists:

Phase 3: The theme

Ma was chosen (2026-10-26) and ships opt-in. Living Sky (T3) is optional and unscheduled.

Cycle T1: ma_theme_day

Process: Railspike.

Ma in light mode. Step 1 decides how a theme with a day and a night look is registered (one auto theme that follows the system, or separate "Ma" and "Ma Night" entries), given ThemeRegistry's one-mode-per-theme entries.

  • Done when: Ma can be chosen from the theme menu and every redesigned screen renders correctly in it on phone and desktop.

Cycle T2: ma_theme_night

Process: Railspike, no Step 1.

Ma's cyberpunk dark mode. Depends on T1.

  • Done when: night mode renders every redesigned screen with the night contrast values met (cyan 12.6:1, neon 5.4:1, muted text 5.7:1 or better).

Cycle T3 (optional, unscheduled): living_sky_theme

Process: decide when scheduled.

  • Done when: Living Sky is selectable and scrolls smoothly on the test phone.

Cycle T4: ma_theme_default

Process: Direct.

Only when we decide Ma is ready to become the default (decision 2: opt-in until then).

  • Done when: new visitors get the new theme and existing choices are preserved.

Phase 4: Features that need data

Each needs its own product decision before its cycle starts.

Cycle D1: community_pulse

Process: Railspike, no Step 1.

Cycle D2: profile_constellation

Process: Railspike, no Step 1.

Cycle D3: post_seals

Process: Railspike, no Step 1. Step 2 must state the security rule: a seal appears only after verification succeeds.

Cycle D4 (deferred): fingerprint_colors

Process: decide when scheduled.

Not yet planned

Decided against or deferred on 2026-10-26: Unlike (not implemented), Undo after Approve (skipped), offline message queue (deferred), hiding Tools groups the viewer cannot use (deferred), fingerprint colors (cycle D4, deferred).

From the graded idea list in visual_direction_notes.md, still unbuilt even as mockups: time-of-day sky (A4), planet flight between screens (A5), kintsugi for edits and resends (B3), the closing ensō for reply progress (B1), riso meme thumbnails (B6, risky), and bolder titles for busy threads (B7, needs font approval). Add a cycle here if any is chosen.