Files
portfolio-next/docs/plans/009-work-page-cv-first.md
T
Lucas WintherandClaude Opus 5.5 974b3eb0df feat(work): CV-first work page, six proud projects, record and courses (spec 009)
- CV header with highlights and keeping-up summary replaces the intro and Now strip
- "Projects I'm proudest of": up to six featured panels; screenshots muted until hover
- "Also made" list; searching it also lists matching courses
- Record section: recognition & volunteering, courses by topic
- /work/learning/: searchable course list (plain script), in the sitemap
- Top bar: Projects, Record, Writing, CV; no Bluesky link in the work footer
- Highlights: only the placement takes the accent (owner, after the preview)

Owner looked at the rendered preview on 2026-11-12 before this commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 10:44:46 +01:00

6.8 KiB
Raw Blame History

status
status
approved

Plan 009 — /work/: CV first, six proud projects, record of competitions and courses

Spec: docs/specs/009-work-page-cv-first.md. Mockup: mockups/work-k2.html, mockups/shots/work-k2-1440.png (built from mockups/tech-r3-c.html, whose featured panels are already the site's Featured.astro). Branch: feat/work-page-cv-first. Commands: bun run typecheck, bun test, bunx playwright test (--workers=2 if pages crash).

Commit rule. Tasks 1–2 (data and pure logic) are committed as they finish. Tasks 3–9 change what visitors see: build and test them, but leave them uncommitted until the owner has looked at the rendered preview (his standing rule: visual changes are shown before they are committed). Task 10 is the preview.

TDD for every task: write the failing test, run it and see it fail, write the code, see it pass. Test imports use explicit .ts extensions. src/core stays pure (no imports from other src/ folders, no I/O).

1. Content: recognition.yml and courses.yml (commit)

  • test/library/load.test.ts: both files load (order kept for recognition); missing files → empty lists; one error per problem with file and field: missing required field, wrong kind, bad completed date (incl. 2025-02-30), non-positive or non-integer minutes, non-boolean certificate/highlight, bad url, status on a competition or not ongoing|retired, keywords not a list of text, unknown key, file not a list, entry not a map.
  • src/library/types.ts: Recognition (title, kind: 'competition'|'volunteering', result, highlight, detail?, year?, url?, status?) and Course (title, provider, kind: 'course'|'lab', topic, completed, minutes, certificate, url?, keywords: string[]); Content.recognition, Content.courses.
  • src/library/load.ts: read both optional files (a missing file is not an error); validate as above.
  • fixtures/content/recognition.yml, fixtures/content/courses.yml: synthetic, enough to exercise every branch (≥ 4 highlighted entries so the "+N more" shows; both kinds; one with url, one retired; ≥ 3 topics, labs and courses, some without certificate). fixtures/sparse/ gets neither file.
  • docs/CONTENT-CONTRACT.md: both files, as in the spec; intro no longer shown on /work/.
  • Commit feat(library): recognition.yml and courses.yml.

2. Pure course logic in src/core/courses.ts (commit)

  • test/core/courses.test.ts: courseSummary(courses) → { count, certificates, minutes, since (YYYY-MM), topics: [{ topic, courses, labs, certificates, minutes }] } with topics ordered by minutes desc then name; courseMatches(course, query) (case-insensitive over title, provider, topic, keywords; empty query → false); formatHours(minutes) ("9.4h"; "~27 h" form for the totals is the caller's); monthYear('2025-02') → "Feb 2025".
  • src/core/courses.ts (takes a plain course shape, not the library type). Commit feat(core): course summary and search.
  • Test for the pure part in sections.ts: featuredOf returns up to 6; featLayout(n) is ['wide','','','wide rev','',''] sliced to n, with a trailing lone half made wide: n=1 → wide; n=2 → wide, wide; n=3 → wide, half, half; n=4 → … wide rev; n=5 → … wide rev, wide; n=6 → as listed.
  • Featured.astro uses it; heading "Projects I'm proudest of", hint, AI legend line at the right (moved from intro).
  • CSS: .feat image stages and .row thumbnails muted (saturate(.5) brightness(.8)), full colour on :hover/:focus-within; transition 300 ms, none under prefers-reduced-motion. Drawn showcases untouched.
  • Fixtures: six featured fixture projects (add featured to existing synthetic ones / add synthetic ones).

4. CV header (do not commit)

  • src/site/components/work/WorkHeader.astro replaces the intro block and NowStrip.astro (delete it if unused). Content and fallbacks exactly as spec §1. Uses courseSummary.
  • test/site/build-pages.test.ts: header rows, highlights (max 3, "+N more"), keeping-up count/line/chips, blocks left out on the sparse fixture, site.now list.

5. Also made + certificate search (do not commit)

  • ProjectList.tsx: heading "Also made"; new prop courses (minimal fields); when the query is non-empty show the matching-courses strip (max 5, "+ N more on the learning page →" to /work/learning/?q=), and the count text "

    project(s) · course(s) match" (spec: courses, labs included; a seal marks the ones with a certificate).

  • Tests: unit test for the strip's data (pure helper) + Playwright check in e2e/work.spec.ts.

6. Record section (do not commit)

  • src/site/components/work/Record.astro (#record) as spec §4; workSections gains record.
  • Build test: both cards, groups, accent on highlights, status words, years only when present, topic table.

7. /work/learning/ (do not commit)

  • src/site/pages/work/learning.astro (only when courses exist) + src/site/scripts/learning-filter.ts (plain script: search, topic buttons, ?q=, "No course matches").
  • Add to the sitemap page list (src/site/feeds.ts), not to feeds.
  • Tests: build test (rows newest first, no page without courses), sitemap test, Playwright (filter, ?q=, no-JS shows all rows in e2e/nojs.spec.ts).
  • TopBar.astro: Projects (#featured), Record (#record), Writing, CV — only existing sections; on /work/learning/ the links point to /work/#….
  • Work.astro: no Bluesky link. Personal footer and /simple unchanged. Tests for both footers.

9. Checks and docs (do not commit)

  • bun run typecheck, bun test, bunx playwright test all pass; update existing tests that asserted the old intro, "All projects"/"Everything else" headings, the Now strip or a four-panel cap.
  • Axe checks in e2e/a11y.spec.ts cover /work/ and /work/learning/. (No axe dependency in the repo; /work/learning/ added to the existing alt-text and motion checks instead; see docs/TODO.md.)
  • docs/ARCHITECTURE.md (new files, src/core/courses.ts, learning page), docs/DECISIONS.md (the [call]s), DESIGN.md work section, docs/TODO.md (anything noticed and left), spec status → implemented after review.

10. Preview for the owner (main session)

  • Build a temporary copy of the real content with the proposed recognition.yml, courses.yml, featured ranks and showcases (scratchpad content-proposal/); serve on port 4392; screenshots at 1440 and 390 in ~/.
  • Wait for his look; then commit tasks 3–9 in small Conventional Commits; remove this plan before merging.