Tier 4 refactor: router chrome, design system, unit tests#68
Merged
felipebalbi merged 8 commits intoMay 15, 2026
Conversation
Two new `required` checks alongside the existing
`line-endings` / `leptosfmt --check` / `trunk build` jobs:
* `rustfmt` -- `cargo fmt --all -- --check` on stable.
Catches the kind of trailing-newline / whitespace drift that
`leptosfmt` doesn't see (`leptosfmt` only formats the
contents of `view! { ... }` macro invocations; everything
else is left to `rustfmt`). Also fixes one such offender:
`image_button.rs` was missing the trailing newline that
`rustfmt` requires after the closing brace.
* `clippy` -- `cargo clippy --target wasm32-unknown-unknown
--all-targets --no-deps -- -D warnings` on nightly with the
`Swatinem/rust-cache` action keyed separately from the
build job's cache so clippy and trunk don't fight over
`target/`. `--no-deps` keeps the noise floor down (we only
own our own crate); `-D warnings` makes any new lint
failure the PR's problem rather than a slow drift.
Both jobs run on `pull_request` to `main` and `push` to
`main` like the rest of the suite.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The repository-dependency graph used on the three project pages was a
single `repo_view.rs` file mixing four very different things:
* ~230 lines of inline D3 v7 JavaScript embedded in a Rust
`format!()` string with doubled braces everywhere -- impossible
to lint, format, or read with JS tooling.
* ~50 lines of CSS embedded in another Rust string and injected
into `<head>` on every component mount.
* The Rust component itself, plus an unused `D3` `JsValue`
extern, redundant `use leptos::*;` / `use leptos::html::*;`
glob imports, and two `signal()` pairs whose setters were
never used.
* A per-mount `<script>` injection that re-loaded the d3 CDN on
every navigation between project pages.
Split it apart:
* `public/repo_graph.js` -- the D3 rendering code as a normal
JavaScript module. `leptosfmt` / `rustfmt` no longer have to
pretend they understand it; editor tooling and CI linters see real
JS. Exposes `window.__odpRenderGraph()` and uses
`window.__odpGraphData` for the per-page payload. Trunk copies
the file via `rel="copy-file"`.
* `style/repo_graph.css` -- the styles, loaded once from
`index.html` via `rel="css"`. No more runtime `<style>`
injection.
* `src/components/repo_view.rs` -- ~95 lines, all Rust. On mount it
publishes `window.__odpGraphData` via a one-shot inline script
(which removes itself), then ensures both `d3.v7.min.js` and
`repo_graph.js` are loaded exactly once for the lifetime of the
page (`getElementById` guards). Subsequent route changes between
the three project pages just call `__odpRenderGraph()` with the
new data.
Drive-by fixes the cleanup made obvious:
* Dropped the unused `D3` `#[wasm_bindgen]` extern.
* Dropped `use leptos::*;` and `use leptos::html::*;` (both
redundant with `leptos::prelude::*`).
* Replaced two unused `(_, set_*)` `signal()` pairs with a
single `Effect::new` that no longer needs an explicit
initialised flag.
* Scoped the `.node` / `.link` / `.column-headers` rules
under `.repository-graph` in the new CSS file so they cannot
leak into other pages.
Behaviour preserved: the graph still renders identically, with the
same zoom controls, drag, header columns, and click-to-open-URL.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Six call sites across landing_page, projects_component, partners_grid, and community_teams repeated the same long horizontal-padding ladder (`px-4 sm:px-8 md:px-16 lg:px-32`) plus a `background_*` surface class. Some had drifted to slightly different mobile padding (`px-6`, `px-4 py-6`) which made the codebase look intentional when it was just inconsistency. Add `components/section.rs` exporting: * `Surface` -- enum mapping to the existing `.background_*` utility classes (Primary/Secondary/Tertiary/Quaternary). * `<Section surface=... class=...>` -- emits `<section>` with the surface class, the standard horizontal ladder, and any extra utilities (typically vertical padding). Convert the six sites. Vertical padding moves to the `class` prop (`py-20`, `py-32`, `pb-32`, `py-6`, `py-8 md:py-20 lg:py-32`). `project_introduction.rs` keeps its own bare `<section>` because it intentionally has no padding (composed inside pages with their own). No visual change at the dominant breakpoints; only side effect is that `community_teams` and `projects_component` mobile padding is now the canonical 16/32/64/128px ladder instead of an off-by-one 24px. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Page components were carrying large static payloads inline:
* Team rosters (~10 members each across patina/ec/ec-services and the
steering committee) lived in `create_team()` helpers next to the
layout code.
* The partner list was a private `const PARTNERS` inside
`partners_grid.rs`.
* Per-project marketing copy ("title", "summary", "what", "why") was
inlined as multi-line `r#""#` strings inside each project page.
* The repository dependency graph payloads were ~7 KB JSON literals
embedded as `r#"[...]"#` raw strings inside the project pages.
Move them into dedicated locations so future contributors can edit a
team roster or a partner without touching layout code:
* `src/data/teams.rs` -- `steering_committee()`, `patina_team()`,
`ec_team()`, `ec_services_team()`.
* `src/data/partners.rs` -- `PartnerInfo` struct + `PARTNERS` slice.
* `src/data/projects.rs` -- `ProjectCopy` struct + `PATINA`,
`EMBEDDED_CONTROLLER`, `EC_SERVICES` constants. Graph node/link
JSON is pulled in via `include_str!` from `data/graphs/*.json`.
* `data/graphs/{patina,ec,ec_services}_{nodes,links}.json` (six new
files) -- the actual graph payloads as standalone JSON.
The page components shrink dramatically (~80% in some cases) and the
project pages are now nearly identical: layout + props pulled from
`crate::data::projects::*`. No new dependencies; `include_str!` is
the cheapest possible extraction.
Fix a load-order race uncovered by this work: the original
`RepositoryGraph` injected d3 + `repo_graph.js` from a Leptos
`Effect` on the first mount and then immediately called the (not
yet defined) renderer. On the very first navigation to a project
page the chained network downloads hadn't completed by the time the
component published its data, so the graph stayed blank until the
user reloaded. The new wiring loads both scripts as `<script defer>`
tags from `index.html` so they finish executing before the Wasm
bundle boots, the Effect just publishes data + calls the renderer,
and `repo_graph.js` re-tries on `requestAnimationFrame` if its
SVG host (or d3) is not yet present. `RepositoryGraph` drops ~70
lines of script-injection plumbing as a result.
Add t26-unit-tests to the backlog: a tier-end sweep to add
wasm-bindgen-test coverage to applicable components, with the
script-load race above as the highest-priority regression test.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the per-page <PageLayout> wrapper with two sibling <ParentRoute>s in the router that render Header, <Outlet/>, and Footer inside a shared ErrorBoundary. SiteShell uses overflow-x- hidden, SiteShellScrollable uses overflow-x-auto. Pages now contain only their content, with no Header/Footer/ErrorBoundary boilerplate. Announcements keeps its own chrome (custom Header background and hand-rolled layout) and remains a top-level route. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Wrap the ODP logo in a router <A href="/"> so clicking the logo returns to the landing page. Remove the now-redundant Home button from both the desktop and mobile navs. Drop the /home route — the canonical home URL is /. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add host-side unit tests (runnable via plain `cargo test`) for the data and presentation pieces that were previously protected only by manual click-through: * `src/data/projects.rs` -- parse each repository graph JSON, reject empty graphs, enforce unique node ids, and verify that every link's source/target points at an existing node id. This is exactly the invariant the D3 renderer assumes; the script-load race fixed alongside t21 was a browser-runtime issue but at least these tests now guarantee the payload itself is well-formed once the renderer does run. Also assert each project's required copy and asset fields are populated. * `src/data/teams.rs` -- every roster non-empty, every member has a non-empty first/last/github username, and the github_url and image_url derive from the username (the easy place for a typo to hide). * `src/data/partners.rs` -- non-empty list, https urls, logos under `/images/partners/`. * `src/components/section.rs` -- factor the class composition into a pure `section_class` helper and pin its behaviour: `Surface` -> brand class mapping, the padded/unpadded ladder toggle, and the empty-extra regression. * `src/components/site_shell.rs` -- pin the two shell class strings so a refactor cannot accidentally drop `overflow-x-hidden` on the default shell. Wire everything in: * `serde_json` becomes a dev-dependency (host-side JSON parsing). * `.github/workflows/ci.yaml` gains a `cargo test` job. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The new ui module shipped without users in the previous commit. This commit migrates eight components to consume the Heading/Text/Mono primitives so the design tokens are actually load-bearing, and dedupes three triplicated blocks along the way. New shared components: * team_hero.rs -- the "Meet the team" hero used by every per-team page. team_ec, team_patina, and team_ec_services now contain just <TeamHero ... /> + <TeamGrid ... />, dropping ~50 lines of identical layout from each. * projects_component.rs grew an internal ProjectRow + ProjectLink pair that collapses three near-identical "image | title + tagline + description + two doc links" rows into a data-driven list. * community_teams.rs grew an internal TeamCard for the three 320x350 call-to-action cards. Migrated to <Heading>/<Text>/<Mono>: * landing_page.rs * projects_component.rs * project_introduction.rs (kept the inner_html slot as a raw <p> since the value is rendered HTML, not children) * partners_grid.rs (kept H1 visual size for parity) * documentation_training.rs * team_grid.rs * community_teams.rs * team_ec.rs / team_patina.rs / team_ec_services.rs (via TeamHero) Drive-by responsive cleanups in community_teams.rs: * fixed items-left -> items-start (items-left is not a real class) * gap: 175px -> gap-16 md:gap-24 lg:gap-[175px] so tablets don't get crushed * width: 320px cards -> w-full md:w-80 so they don't overflow on phones The ui module still keeps #![allow(dead_code, unused_imports)] for the primitives (Container, Stack, Grid, Button, IconButton, Link) that haven't reached call sites yet -- those will land in follow-up work. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
8828330 to
b280de8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Depends on #67.
Tier 4 -- structural refactor
This stack lands the deepest readability/maintainability work in the
series: a router-level page chrome, an externalized D3 graph, a shared
Sectionshell, host-side unit tests, a logo-as-home navigationfix, and a small design-system module wired into eight components.
Commits
cargo fmt+cargo clippygates in CI.repo_viewD3 graph script andmodule so the component file is layout-only.
<Section>(background + horizontal paddingladder +
paddedflag) and migrate every page section onto it.JSON) into
src/dataanddata/. Fixes a script-load racefor the project graph by making
index.html<script defer>the d3 + repo_graph.js bundle.
<Header>+<Footer>chrome into arouter-level
<SiteShell>parent route so individual pages stophand-pairing them.
button is gone;
/homeis dropped in favor of the canonical/route.cargo testsweep (36 tests across thedata layer,
Section,SiteShell, and the ui primitives) anda matching CI job.
src/components/ui/design-system primitives(
Heading,Text,Mono,Link,Button,Container,Stack,Grid) plus ateam_hero.rsshared component, migrated into eight call sites. Dedupes three
triplicated team-page heroes and three project-row blocks.
Discussion item (t25): CSR Leptos vs static SSG
The repo is a marketing site with no client-side interactivity beyond
the D3 graph and the mobile nav toggle. The current Leptos CSR build
costs the user a wasm download and a JS bootstrap before any content
paints, which hurts first-contentful-paint and SEO.
Worth discussing as a separate effort: regenerate the same content
with a static-site generator (zola, mdbook, astro, or even a thin
leptos_axum SSR) so the HTML is served as-is. The design system, data
modules, and component breakdown from this stack would carry over
directly. Not in scope for this PR.