Skip to content

Redesign: home page - #40

Merged
meaningfool merged 5 commits into
redesignfrom
redesign-31-home
Nov 3, 2026
Merged

meaningfool merged 5 commits into
redesignfrom
redesign-31-home

Conversation

@meaningfool

@meaningfool meaningfool commented Nov 3, 2026 •

Copy link
Copy Markdown
Owner

Implements #31 (part of #35). It builds on #36 (foundations), #37 (share images), #39 (article page) and #38 (background and topic map).

What changes

  • The home (src/pages/index.astro) follows the prototype's home view (docs/redesign/prototype/template.html). Its <main> drops the legacy class.
    • Intro: Redesign: page background and topic map, drawn from prepared images #38's Intro, with the label "Josselin Perrus · Product manager · Paris", the title "I build products / with AI agents." with the full stop in the accent colour, and HOME_INTRO from src/lib/share.ts as the text. The button "About me →" goes to /about/, followed by LinkedIn, GitHub and X. There's no RSS link. The map is beside the text, and goes under it below 1120px.
    • Projects: a tile for each project in src/data/projects.ts, newest first. Each has its screenshot on the grey panel, "App · May 2026", the name with ↗, and its one line. The whole tile links to the project. The heading has the prototype's "Things I built to learn" on its right.
    • Writing (id="writing", where "← All writing" goes): the articles only, newest first. Each row shows the date, title, project and reading time; on phones, the title and reading time. The reading time is readingMinutes(), the same as on the article page and its share image. The first row's top line lies on the heading's line. On the right of the heading it says "18 articles", or "Product · 10 of 18 · Show all" when a topic is picked.
    • Activity logs leave the home. Their pages, URLs, sitemap entries and RSS items don't change.
    • Page: the title is "meaningfool | Josselin Perrus, product manager building with AI agents". The meta description, also in the WebSite JSON-LD, is "Josselin Perrus's site: a product manager in Paris building AI products with AI agents. His projects, and writing on customer success, product and AI agents."
  • The topic filter. Clicking a dot on the map shows only that topic's articles, and marks the dot with data-picked. Clicking it again, or "Show all", shows all of them.
    • The script is written for Astro's ClientRouter (Redesign: About page #33 may add it):
      • its listeners are on the document and are added once;
      • every arrival on the home, by a page load or astro:page-load, resets the list and clears data-picked;
      • leaving the home (astro:before-swap) clears the pick, so a map kept with transition:persist arrives without it;
      • it acts only when the home's Writing list is on the page, and on the home it cancels topicmap:pick, so a dot that is a link never navigates there.
    • The count is a polite live region, rewritten only when it changes. After "Show all", focus goes to the Writing heading.
  • Topics (src/data/topics.ts, src/content.config.ts):
    • An article belongs to a topic when that topic makes up 30% or more of it.
    • The content schema gains an optional topics field, an array of customer-success, product and ai-agents. Any other value fails the build and names the article.
    • The frontmatter is read first. Until it's there, src/data/topics.ts gives the 18 articles the topics from the prototype's weights in maps.js: 2 in Customer success, 10 in Product, 12 in AI agents, the prototype's counts.
    • meaningfool-writing: Give each article its topics in the frontmatter meaningfool-writing#6 (draft, not merged) adds the same topics to the 18 articles' frontmatter and documents it in its AGENTS.md. The site builds that repository's main, so the data file keeps the home working until it's merged.
    • The build warns about an article with no topics, and about an id in topics.ts that matches no article.
    • The ids are checked against src/data/topic-map.json, and the names come from that file.
  • Removed: the temporary /map-preview/ page and its sitemap filter (TEMPORARY in Redesign: page background and topic map, prepared at build time #30), and ArticlesList.astro, which only the old home used.
  • Smaller:
    • src/data/profiles.ts holds LinkedIn, GitHub and X for the intro and the Footer; the footer's HTML is unchanged.
    • plural() is exported from src/lib/share.ts.
    • AGENTS.md describes the home and its topics.
  • Unchanged: Layout, Intro, TopicMap, Background, Frame and legacy.css. legacy.css goes once Redesign: About page #33 is merged too.

Choices to check

  • Not in the prototype:
    • The button, the profile links and the project titles change on hover: the button gets the tint, a profile's underline turns to the accent, and a project's name is underlined.
    • A row shows its hover look when it has keyboard focus.
  • The 24px phone gutter (Redesign foundations: fonts, colours, header, footer and 404 #36): at 375px the label fits on one line and the map is 327px wide. The prototype, with 32px, has two lines and a 311px map. At 1440px the only difference is the prototype's 8px body margin.
  • Activity logs stay in the sitemap and RSS, as before. The issue only takes them off the home.
  • After "Back": a page that the browser restores from its back-forward cache keeps the topic that was picked, in step with the list.
  • Old anchors: the old home's #about and #articles are gone. Nothing on the site links to them.

For #33

  • About's map has links. If it's kept on the home with transition:persist, its dots still filter Writing there, but TopicMap sets aria-pressed only on button dots.
  • legacy.css's header comment still lists the home. I left the file alone, to avoid a conflict, since it is deleted after both are merged.

Validation

Head commit cd5b9e80d2eeb09bbccf024a16d3cc206ee6c676, on base 78b78fdcbeda08341f25c4c84560d04d8aa5d36f (origin/redesign). Node 24.22.1, Bun 1.4.4. Content submodule at bf783ba, meaningfool-writing's main (git submodule update --init --remote; the pointer isn't changed).

  • bun install, then bun run build from an empty dist/: passes, 62 pages. The base has 63, including /map-preview/. No warnings.
  • bun run test:documents: 10 tests, 10 passed.
  • astro check (@astrojs/check 0.9.4 and TypeScript 5.9.3, in a scratch copy): 0 errors, 0 warnings, 0 hints. tsc --noEmit: no errors.
  • Frontmatter path: I built with meaningfool-writing#6 checked out as the content.
    • The whole dist/ is byte-identical to the build that uses the data file.
    • Changing one article's topics changed that article's row on the home.
    • topics: ["marketing"] failed the build: topics.0: Invalid option: expected one of "customer-success"|"product"|"ai-agents".
  • Comparison with the prototype. I took Playwright screenshots in Chromium and measured element boxes. The prototype's fonts fall back to DejaVu Sans Mono there.
    • At 1440px: the label, title, text, button, links, map (636 × 454.3) and its dots have the prototype's boxes, fonts and spacing. So do the section heads, tiles (381.3 × 355.4, screenshot 361.3 × 225.8), the count and the rows (50px, columns 96/844/120/56). Each is 8px higher, from the prototype's body margin. The Writing section is pixel-identical once shifted by 8px.
    • At 1000px and 800px: the map is under the text at 560 × 400, as in the prototype, with the 24px gutter.
    • At 375px: the same layout, with the 24px gutter. The rows show only the title and the reading time.
    • Picked state (Product, at 1440px): the count "Product · 10 of 18 · Show all" has the same box as the prototype's, 219.6px wide, and so does its button. The other dots are at 40%, and the map screenshots match.
  • Filter: for each of the three topics, the site shows the same articles as the prototype, in the same order, with the same count text: 2, 10 and 12 of 18.
    • The first visible row's top line lies on the heading's line in each case.
    • Picking the same dot again clears the pick. Switching topics and "Show all" work, and so do Enter on a dot and on "Show all".
    • The live region isn't rewritten on load or by a repeated reset.
  • ClientRouter: I built once with <ClientRouter /> added to Layout, then removed it; it isn't committed. One full page load covered this whole run:
    • Home → article → "← All writing" → Home → About → Home, then history back and forward;
    • every arrival showed 18 articles and no pick;
    • after each swap, one click still toggled once;
    • the map left behind had its pick cleared.
  • Widths: from 320px to 2560px (14 widths), the home doesn't scroll sideways. It logs no errors and requests nothing from another host.
  • Pages outside the issue: I diffed the base build against the head build.
    • Only index.html and _redirects change; _redirects loses /map-preview.
    • map-preview/index.html and its script are removed.
    • Added: the home's CSS and script, and three 362px screenshot files. The 720px ones already existed.
    • Every other page, rss.xml, the sitemap, _headers, robots.txt, the share images and the favicons are byte-identical. llms*.txt differ only in their build timestamp.

Review gate

There were 5 passes over the whole change: four independent code reviews, then a final read-through with every check above run again on the head commit. These were fixed:

  • clearing the pick when the ClientRouter leaves the home;
  • the "Show all" focus in Safari and for screen readers;
  • the live region being reread;
  • — being read out;
  • the "Show all" spacing, which now matches the prototype;
  • the hover underline running under the arrow;
  • the duplicate count text;
  • a click target that isn't an element;
  • warnings for an empty or missing topics list and for stale ids;
  • checking the topic ids against topic-map.json.

The final read-through found nothing more at P1 or P2.

🤖 Generated with Claude Code

https://claude.ai/code/session_013MdjUerTCBDeF5DG9puDd2

Implements #31, part of #35.

- The intro: the shared Intro block with the home's text (HOME_INTRO), the
  "About me" button and the profiles, beside the topic map.
- Projects: a tile per project from src/data/projects.ts, newest first.
- Writing: the articles only, newest first, with their date, project and
  reading time; the section is #writing, where "All writing" goes. The
  activity logs keep their pages but leave the home.
- Picking a dot on the map shows only the articles about its topic. An
  article's topics come from a `topics` frontmatter field, or else from
  src/data/topics.ts, which holds the prototype's reading of the 18
  articles. The script also works when a ClientRouter swaps pages.
- The page has its own title and meta description.
- The temporary /map-preview/ page (#30) is gone, with its sitemap filter.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MdjUerTCBDeF5DG9puDd2
- Clear the map's pick when the ClientRouter leaves the home, so a map kept
  for the next page arrives without it.
- Guard the Show all listener against clicks on non-elements, and pass the
  Writing list to show() instead of looking it up twice.
- Take the unfiltered count text from the page, built with share.ts's
  plural(), rather than repeating it in the script.
- Warn only about articles that have no topics at all, not about an
  explicit `topics: []`, and fail the build if topic-map.json's topics stop
  matching TOPICS.
- Use readingMinutes() and the projects' own order directly.
- Describe the home and its topics in AGENTS.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MdjUerTCBDeF5DG9puDd2
- Rewrite the Writing count only when it changes: it is a live region, so
  resetting it on arrival made screen readers read it again.
- After "Show all", move the keyboard's place to the Writing heading rather
  than to the live region, which it would read twice.
- Mark the pick on the map that was clicked.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MdjUerTCBDeF5DG9puDd2
- After "Show all", always move to the Writing heading: Safari doesn't focus
  a clicked button, and the screen reader's place went with the button.
- Hide the "—" of rows without a project from screen readers.
- Underline only a project's name on hover, not the space and arrow.
- Warn when src/data/topics.ts names an article that doesn't exist, as
  projects.ts does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MdjUerTCBDeF5DG9puDd2
At 30% or more, every article has at least one topic, so an empty list is a
mistake like a missing one. Say in the script what a page restored from the
back-forward cache does.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013MdjUerTCBDeF5DG9puDd2
@github-actions

github-actions Bot commented Nov 3, 2026

Copy link
Copy Markdown

Preview: https://pr-40-meaningfool.josselin-perrus.workers.dev

Built from cd5b9e8 with the latest content. The link stays the same and shows the latest push. Search engines are asked not to index it.

Copy link
Copy Markdown
Owner Author

Review checkpoint: accepted at cd5b9e80d2eeb09bbccf024a16d3cc206ee6c676, on base 78b78fd (origin/redesign). I found nothing at P1 or P2 and pushed no fixes.

Setup: Node 24.22.1, Bun 1.4.4, content at meaningfool-writing main (bf783ba), bun install, bun run build.

Build and tests

  • bun run build passes: 62 pages, no warnings. The Deploy build check passed on this commit.
  • bun run test:documents: 10 of 10 pass.
  • astro check (in a scratch copy): 0 errors, 0 warnings, 0 hints.

Against #31 and the prototype (Playwright, Chromium, the prototype with its shaders from jsDelivr)

  • 1440px: all 23 measured boxes match the prototype's: label, title, text, button, links, map (636 × 454.3), section heads, tiles, screenshots, count and rows (columns 96/844/120/56). Each sits 8px higher, because of the prototype's body margin. The Writing list is identical side by side.
  • 1120px, 1119px, 1000px and 375px: the same layout. The map is beside the text at 1120px and under it, 560 × 400, at 1119px. The only offsets come from the 24px gutter Redesign foundations: fonts, colours, header, footer and 404 #36 left to the owner. On phones, rows show the title and reading time only.
  • Filter, for each topic at 1440px and 375px: the same rows as the prototype (date, title, project and minutes), and the same count text and box ("Product · 10 of 18 · Show all", 219.6px). The other dots are at 40%. The first row's top line lies on the heading line in every state.
    • Clicking a dot again, and "Show all", both clear the pick. Enter works on a dot and on "Show all".
  • Copy and page: the copy matches the issue. There's no RSS link and no activity log. id="writing" is on the section. The <title> and meta description name the site.
  • Topics: src/data/topics.ts matches the prototype's 30% reading: 2, 10 and 12 articles.

Scope (base build against head build)

  • Only index.html and _redirects changed (_redirects loses /map-preview), apart from the llms*.txt timestamps.
  • /map-preview/ is gone. Every other page, rss.xml, the sitemap and _headers are byte-identical.
  • No TEMPORARY markers are left. There are no secrets or personal data, no new outside resources (the CSP is unchanged), every file is under 25 MiB, and internal links end with a slash.

ClientRouter

  • With a temporary <ClientRouter />, the map wrapper persisted, and a test page with link dots: all of it ran in one page load.
    • Every arrival on the home showed 18 articles and no pick. A map leaving the home had its pick cleared.
    • One click changed data-picked exactly once after every swap, with Back and Forward too.
    • A persisted link dot on the home filtered and didn't navigate.
  • In a throwaway local merge with origin/redesign-33-about (Redesign: About page #41, 1f36b5e; not pushed), at 1440px and 375px:
    • Home → About → Home, with Back and Forward, never reloaded the page, and the same map canvas came along.
    • About's dots are links, with no pick. They go to their parts and filter nothing.
    • On every arrival the home's filter is reset, and it works once per click.
    • Home → article is a full load. "← All writing" lands on /#writing with Writing at the top, and Back keeps that place.
    • Loading About first, then going to Home, works too.

Left to the owner, as the PR lists: the hover styles that aren't in the prototype, the 24px gutter, and activity logs staying in the sitemap and RSS.


Generated by Claude Code

@meaningfool
meaningfool marked this pull request as ready for review November 3, 2026 20:51
@meaningfool
meaningfool merged commit 319deac into redesign Nov 3, 2026
1 check passed
@meaningfool
meaningfool deleted the redesign-31-home branch November 5, 2026 10:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants