Skip to content

feat(site): render leveling section bullets as scannable cards - #6

Merged
stevemcqueenz merged 3 commits into
mainfrom
fm/tkb-site-items
Sep 14, 2026
Merged

stevemcqueenz merged 3 commits into
mainfrom
fm/tkb-site-items

Conversation

@stevemcqueenz

Copy link
Copy Markdown
Owner

Intent

The original ask was that the Turtle Knowledge Base site is still too hard to digest and not user-friendly enough for players who want to learn their class, rotation, leveling, and how to play it well. The captain approved the investigation's full recommendation set.

For this slice, implement recommendation R9: emit each leveling section's top-level bullets as structured items[] beside its Markdown so Playstyle, Stats, Mistakes, Hardcore, and equivalent sections can render as scannable cards or lists without changing the prose.

What Changed

  • build-data.py now emits a leveling section's unindented Markdown bullets as an items[] array beside the unchanged verbatim markdown, with check-data.mjs validating the field on generated data and types.ts declaring it optional.
  • LevelingPage renders sections that have items as a two-column card grid of the bullets while keeping the framing prose above, and falls back to a single prose card when a section has no bullets or omits items.
  • Regenerated classes.json/meta.json and updated PLAN.md and src/data/README.md to document the new items view.

Risk Assessment

✅ Low: The fix is small, data-verified, and resolves exactly the reported prose-loss cases without changing any markdown or the generated-data contract.

Testing

Stood the site up with the real generator plus a production vite preview (generated data) and a VITE_USE_FIXTURES=1 build, then drove the leveling pages in headless Chrome. The generator re-emitted the committed items arrays deterministically; the generated-data validator rejects a leveling section missing items while the fixture validator accepts omitted items; and the browser renders top-level bullets as card grids for Playstyle/Stats/Mistakes/Route/Hardcore while displaying the preserved non-bullet framing prose (verified on rogue Route, rogue Hardcore and warlock Professions) and falling back to full Markdown when items are absent. Screenshots capture the rendered surfaces.

  • Live validation: ✅ go - 6 of 6 scenarios driven live against the product
Scenario Result Live Evidence
Regenerating site data emits every leveling section's top-level bullets as items[] while leaving the section Markdown unchanged ✅ pass live python3 site/scripts/build-data.py regenerated classes.json identical to the committed file (only meta.json's timestamp changed) and an independent extraction confirmed all 99 leveling sections' ite…
Adversarial: the generated-data contract rejects a leveling section that omits items ✅ pass live Removing items from warrior leveling section 0 made node scripts/check-data.mjs exit 1 with 'classes[0] (warrior).leveling.sections[0]: leveling sections must include an items array'; the original…
The documented development-fixture contract still validates when fixtures omit items ✅ pass live node scripts/check-data.mjs --fixtures exits 0 with 'contract OK', and node scripts/smoke.mjs --fixtures renders 11 routes with 0 failures.
Browsing a class leveling page renders each bulleted section as a scannable card grid instead of a wall of prose ✅ pass live Against the running vite preview build, section#lv-stats contained one ul with 5 li.card, section#lv-hardcore one ul with 6 cards, and warlock section#lv-route one ul with 4 profession…
Browsing a leveling section that has both framing prose and bullets shows the preserved non-bullet lead-in above the cards ✅ pass live Live DOM text and screenshots: rogue Route shows 'The corpus gives only fragments, not a zone-by-zone route:', rogue Hardcore shows 'Rogue is discussed as one of the easier Hardcore classes because of…
Browsing a section with no top-level bullets falls back to rendering its complete Markdown as one card (generated and fixture data) ✅ pass live Generated rogue Playstyle (section#lv-press, items absent) rendered its full Markdown including ### subheadings with no ul; the fixture build of the warrior leveling page (section#lv-press) li…

Rogue leveling page, full page: bullets rendered as scannable card grids (Stats 5 cards, Route 3 cards, Hardcore 6 cards)
Rogue Route section: preserved framing prose ('The corpus gives only fragments, not a zone-by-zone route:') above its bullet cards
Rogue Hardcore section: preserved framing prose above 6 Hardcore bullet cards
Warlock leveling page, full page: Professions section framing prose plus cards; item-less Route section falls back to a full Markdown card
Warlock Professions section: preserved framing prose ('No profession advice exists in the Warlock forum itself...') above 4 bullet cards, with the item-less Route fallback visible below

Pipeline

Updates from git push no-mistakes

✅ **intent** - passed

✅ No issues found.

✅ **Rebase** - passed

✅ No issues found.

🔧 **Review** - 2 issues found → auto-fixed ✅
  • ⚠️ site/src/pages/LevelingPage.tsx:27 - When a leveling section has any top-level bullet, Prose renders only s.items and never renders s.markdown, so the section's non-bullet prose silently disappears from the page (there is no ProseDetails fallback for press/stats/dont/route/hardcore sections). Concrete occurrences in the committed data: rogue 'Route' loses 'The corpus gives only fragments, not a zone-by-zone route:'; rogue 'Hardcore-mode differences' loses 'Rogue is discussed as one of the easier Hardcore classes because of its escape kit, in contrast to Warrior:'; warlock 'Professions' loses 'No profession advice exists in the Warlock forum itself; the only sourced guidance is from the Hardcore Lounge'. The intent says these sections should render as scannable cards 'without changing the prose'; the Markdown is preserved in data but the displayed prose is truncated, so the user cannot read those lead-in sentences at all. This is a product decision (render the non-bullet prose alongside the cards vs. intentionally drop framing sentences), so it needs the author's call rather than a mechanical fix.
  • ⚠️ site/scripts/check-data.mjs:86 - checkLevelingSection now hard-requires an items key on every leveling section, but the committed development fixtures (site/src/data/fixtures/classes.json) intentionally omit items — the updated PLAN.md and site/src/data/README.md both state 'development fixtures omit items'. Running the validator against the documented fixture fallback (node scripts/check-data.mjs --fixtures) therefore fails with 'leveling sections must include an items array', a regression from the previous checkSection which accepted them. Make the absent-items case valid when validating fixtures (or add items to the fixtures) so the documented fixture contract still validates.

🔧 Fix applied.
✅ Re-checked - no issues remain.

✅ **Test** - passed

✅ No issues found.

  • Live validation: ✅ go - 6 of 6 scenarios driven live against the product
Scenario Result Live Evidence
Regenerating site data emits every leveling section's top-level bullets as items[] while leaving the section Markdown unchanged ✅ pass live python3 site/scripts/build-data.py regenerated classes.json identical to the committed file (only meta.json's timestamp changed) and an independent extraction confirmed all 99 leveling sections' ite…
Adversarial: the generated-data contract rejects a leveling section that omits items ✅ pass live Removing items from warrior leveling section 0 made node scripts/check-data.mjs exit 1 with 'classes[0] (warrior).leveling.sections[0]: leveling sections must include an items array'; the original…
The documented development-fixture contract still validates when fixtures omit items ✅ pass live node scripts/check-data.mjs --fixtures exits 0 with 'contract OK', and node scripts/smoke.mjs --fixtures renders 11 routes with 0 failures.
Browsing a class leveling page renders each bulleted section as a scannable card grid instead of a wall of prose ✅ pass live Against the running vite preview build, section#lv-stats contained one ul with 5 li.card, section#lv-hardcore one ul with 6 cards, and warlock section#lv-route one ul with 4 profession…
Browsing a leveling section that has both framing prose and bullets shows the preserved non-bullet lead-in above the cards ✅ pass live Live DOM text and screenshots: rogue Route shows 'The corpus gives only fragments, not a zone-by-zone route:', rogue Hardcore shows 'Rogue is discussed as one of the easier Hardcore classes because of…
Browsing a section with no top-level bullets falls back to rendering its complete Markdown as one card (generated and fixture data) ✅ pass live Generated rogue Playstyle (section#lv-press, items absent) rendered its full Markdown including ### subheadings with no ul; the fixture build of the warrior leveling page (section#lv-press) li…
  • python3 scrape/validate.py && python3 site/scripts/test_data.py && cd site && npm ci && npm run check && npm run smoke
  • python3 site/scripts/build-data.py (regenerated classes.json byte-identical to committed data; items deterministic)
  • node scripts/check-data.mjs (generated contract OK, 9 classes/9 leveling guides)
  • node scripts/check-data.mjs --fixtures (fixture contract OK with items omitted)
  • Adversarial: deleted items from warrior leveling section 0 and reran node scripts/check-data.mjs -> exit 1 with 'leveling sections must include an items array'; restored file
  • Semantic check over committed classes.json: all 99 leveling sections' items equal the ^[-*+]\s+(\S.*)$ extraction of the section Markdown (254 items, 0 mismatches)
  • npm run smoke (78 routes rendered, 0 failures) and node scripts/smoke.mjs --fixtures (11 routes, 0 failures)
  • Live browser via chrome-devtools-axi/CDP against vite preview of the generated build (http://localhost:4188) and the fixture build (http://localhost:4189): DOM counted ul/li.card per section[id^=lv-], asserted framing text presence, and full-page screenshots
✅ **Document** - passed

✅ No issues found.

✅ **Lint** - passed

✅ No issues found.

✅ **Push** - passed

✅ No issues found.

@stevemcqueenz
stevemcqueenz merged commit 5c40b0e into main Sep 14, 2026
1 check passed
@stevemcqueenz
stevemcqueenz deleted the fm/tkb-site-items branch September 16, 2026 22:28
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.

1 participant