Skip to content

render per-dataset validation reports on dynamical.org#82

Draft
mrshll wants to merge 19 commits into
mainfrom
feat/onsite-validation-reports
Draft

render per-dataset validation reports on dynamical.org#82
mrshll wants to merge 19 commits into
mainfrom
feat/onsite-validation-reports

Conversation

@mrshll
Copy link
Copy Markdown
Member

@mrshll mrshll commented May 19, 2026

Summary

  • Ports reformatters' src/scripts/validation/render.py pipeline into an 11ty page so dynamical.org/catalog/<id>/validation/ serves the same content as the standalone HTML on R2.
  • Adds _data/validationReports.js to fetch validation_summary.md for each STAC dataset (silently skipping 404s; only datasets with a published report get a page).
  • Surfaces the on-site link plus a "raw" fallback link from the catalog dataset page. Also passes validation_report_href from STAC assets.validation_report through _data/catalog.js for the deeper fallback path.
  • Page inherits site chrome (base.njk + main.css). TOC sits in a sticky in-flow sidebar to the left of the report body and collapses to an in-flow block on narrow viewports.

Test plan

  • `npm run build` succeeds; three pages emitted (noaa-hrrr-analysis, ecmwf-aifs-ens-forecast, ecmwf-ifs-ens-forecast-15-day-0-25-degree).
  • In dev server, navigate to `/catalog/noaa-hrrr-analysis/validation/` — verify nav, breadcrumb, sticky TOC, table-scroll wrappers, variable checkbox toggle (per-variable, all/none), per-variable plot images render from R2.
  • Verify `/catalog/noaa-hrrr-analysis/` surfaces "validation report (raw)" links; datasets without a report (e.g. noaa-gfs-analysis) don't.
  • Verify dark mode rendering.
  • Confirm narrow-viewport layout collapses TOC into flow.

mrshll added 3 commits May 16, 2026 14:45
- Add --radius-sm/md/lg tokens mirroring colors_and_type.css
- .hero-cta: drop uppercase, letter-spacing, and color transition
  (BRAND.md bans uppercase + wide tracking; hover is instant);
  align padding/font-size/radius to the ghost-button spec
- #latest-popup: 8px → --radius-md (4px) per spec
- Add og:image meta (icon-1024.png) and drop duplicate og:type
- Remove vendored brand assets in public/{favicon.ico,apple-touch-icon.png,
  assets/{favicon,lockup,lockup-white,icon}.svg} — BRAND.md says link to CDN,
  don't rehost. Site already references the CDN-hosted copies.

Nav lockup left on the <picture> light/dark swap: the brand spec suggests
neutral lockup.svg with currentColor, but currentColor doesn't propagate
into <img src>, so the swap is the correct working pattern.
Ports reformatters' src/scripts/validation/render.py pipeline into an
11ty page so /catalog/<id>/validation/ serves the same content as the
standalone HTML on R2 — markdown-it (commonmark+tables) plus the five
post-process transforms, sidebar TOC with per-variable checkbox toggle,
image refs resolved against the report's baseUrl.

Adds _data/validationReports.js that fetches validation_summary.md for
each STAC dataset (skipping 404s silently), and surfaces both the
on-site link and a "raw" fallback on the catalog dataset page.
The validation report page now extends base.njk and uses main.css for
typography, links, and base table styling. The TOC moves from a fixed
full-height overlay into the page flow as a sticky sidebar to the left
of the report body — it stays on-screen as you scroll, and collapses to
an in-flow block on narrow viewports. Drops the bespoke font/body/link
CSS that duplicated main.css.
@cloudflare-workers-and-pages
Copy link
Copy Markdown

cloudflare-workers-and-pages Bot commented May 19, 2026

Deploying dynamical-org with  Cloudflare Pages  Cloudflare Pages

Latest commit: ab30bcd
Status: ✅  Deploy successful!
Preview URL: https://1050315a.dynamical-org.pages.dev
Branch Preview URL: https://feat-onsite-validation-repor.dynamical-org.pages.dev

View logs

Comment thread content/catalog/validation.11ty.js Fixed
Comment thread content/catalog/validation.11ty.js Fixed
mrshll added 6 commits May 19, 2026 10:30
The TOC now sits to the left of the centered max-width report body on
wide viewports and stacks above it on narrow ones. Variable checkboxes
and all/none controls are removed — the variable list in the TOC stays
as plain anchor links. The toggle JS is gone with them.
Content body now uses the same max-width as every other catalog page
(78rem, centered) so it sits in the same horizontal position. The TOC
lives in a 'rail' anchored just outside the wrapper's right edge — it
takes no space in the main column, stays sticky as you scroll, and
collapses to an in-flow block above the content when there isn't room
for it beside the body (~1180px breakpoint).
Rail anchors outside the wrapper's left edge again; TOC items are
right-aligned so they sit flush against the content's left edge. Drops
the H2 bottom-border underline and the variable section top-border
that were running across the content area.
The site's main.css applies a perimeter border + horizontal margins to
<article> (intended for blog/update posts), so the report body was
picking up an unintended box. Use a plain <div> instead.

Also drops the right-align on the TOC text — items render left-aligned
as the user prefers.
Exports annotateHeadings(), buildTocHtml(), CSS, and JS. Given HTML
already produced by a markdown renderer, annotateHeadings adds ids to
every h2/h3 and returns the headings list in document order;
buildTocHtml turns that into a nested <ul> with h3s grouped under their
preceding h2.

CSS handles the nested tree, collapse-when-inactive on h3 children,
and a ▸ indicator on the active link with a reserved padding slot so
toggling .active doesn't reflow neighbours. JS uses an
IntersectionObserver with a top-30% active band to mark the deepest
in-view heading active and expand its parent h2.

Page templates put the article body inside .md-toc-content and the
TOC inside .md-toc; the JS keys off those classes.
Slims validation.11ty.js to validation-specific work — png link
target=_blank, per-variable plot injection, table-scroll wrapping,
asset-URL rewriting against the report's baseUrl — and delegates the
TOC build + scroll-spy + ▸ indicator to lib/markdown-toc.

Variable headings are now annotated with normal H3 ids (the slug of
the variable name), so they show up nested under 'Per-variable
details' in the TOC like any other H3. The old special 'Variables'
group and its custom anchor scheme are gone.
Comment thread lib/markdown-toc.js Fixed
mrshll added 10 commits May 19, 2026 13:10
- Generate the page H1 from the catalog entry name ('<name> validation
  report') and strip any H1 the rendered markdown produces. Anticipates
  reformatters#614 which drops the H1 from validation_summary.md;
  still looks right against the current (pre-#614) bucket output.
- Move the TOC rail inside .validation-body, after the breadcrumb and
  H1. On wide viewports the rail still absolute-positions out of flow
  into the left margin (containing block stays .validation-wrapper).
  On narrow viewports it goes static and renders directly below the
  H1 — not above the breadcrumb as before.
- Narrow mode drops the indicator and the nested subheaders: the TOC
  becomes a flat list of H2 links right under the title. The scroll-
  spy keeps running but its visual effects are CSS-suppressed; the TOC
  is off-screen once you scroll past it anyway, so the spy isn't
  useful there.
- TOC rail gets a right border to separate it from the body content.
- Long heading names ellipsis-truncate inside the narrow TOC column
  (e.g., 'downward_short_wav…' instead of overflowing).
- TOC right border moves from .validation-toc-rail (full wrapper
  height) to .md-toc itself, so the vertical line is only as tall as
  the visible TOC content.
- TOC list items get font-weight: inherit so the global
  'nav ul li:first-child { font-weight: 700 }' rule from main.css
  (intended for the brand lockup in the site nav) no longer leaks into
  the TOC and bold the first heading.
- Markdown tables now wrap in <div class="table-container">
  <table class="data"> — same pattern as the catalog dataset page.
  Drops the bespoke .table-scroll CSS in favour of main.css's shared
  table styling, so validation tables look consistent with the rest of
  the site.
… rail

Audit + minimize CSS introduced for the validation page:

- Replace .validation-wrapper with the site's existing .content class
  (already supplies max-width:78rem + centered margin).
- Drop .validation-body, .validation-breadcrumb, and the .validation-body
  h2/h3 margin overrides — the site's base type rules handle these.
- Move .md-toc-rail + .md-toc sticky/border/font-size + narrow-viewport
  collapse rules from validation.11ty.js into lib/markdown-toc.js so
  any future markdown-driven page that opts into .md-toc-content gets
  the rail behavior for free.
- Stop wrapping markdown tables in .table-container (main.css gives it
  overflow-x:auto, which was clipping/scrolling the validation tables);
  just apply class="data" to the <table> so tables overflow naturally.

Page-specific CSS shrinks to just the .plots block under per-variable
H3s, which is the only truly validation-specific styling.
.table-container previously clipped wide tables to .content's 78rem
max-width and scrolled inside even on wide viewports where the table
could be shown in full. Switch to width:max-content + viewport-based
max-width + relative+translateX(-50%) centering so:

- Narrow viewport (table > viewport): scroll horizontally inside the
  container, same as before.
- Wide viewport (table fits in viewport but not in .content): table
  renders at full width, visibly overflowing .content into the page
  margins.

Restore the .table-container wrapper on validation report tables —
the new wrapper behavior is what we want there too.

TOC rail tweaks per request:
- width 18rem -> 22rem
- gap to article 2rem -> 4rem
- right border uses --popup-border (lighter grey) instead of
  --border-muted-color so it reads as a quiet divider rather than a
  hard rule against the body text; padding-right 1rem -> 2rem to keep
  the toc text from sitting on top of the border.
It produced a worse outcome than the original problem: max-content
sizing forced tables to render at their unwrapped natural width, so
markdown tables with long URL values stretched all the way across the
viewport instead of wrapping to fit the column.

Back to plain overflow-x:auto. Tables wrap inside .content's 78rem
and scroll horizontally when their content truly doesn't fit.
max-width: 100% is the same value default block sizing already gives,
written explicitly so the intent — wrapper stays anchored to the
article column (.content), scrolls horizontally when the table inside
is wider — is obvious at the rule.

This is the catalog small-screen scroll aesthetic for any table that's
wider than .content: the wrapper clips at the column edge with native
horizontal scroll, rather than the table breaking out past .content
into the page margins.
.table-container now shows a 1px border + Bayer-style 1-bit dither
shadow at any edge where content overflows. Uses the local/scroll
background-attachment trick: content-anchored covers hide the
viewport-anchored border and dither image when the content edge sits
at the viewport edge. Shadow is baked into an inline SVG (14x4 tile,
mirrored via SVG transform for the right edge) so the same trick
applies without masks. Themed for light/dark via shadow-dither-l/-r
custom properties.
The single-pass regex could reconstitute a tag from nested input like
<scr<script>ipt> — flagged by CodeQL js/incomplete-multi-character-
sanitization. Loop until no more replacements happen so the stripped
text can't reintroduce a tag. No real exposure today (markdown-it
commonmark mode disables raw HTML and the validation input is trusted),
but the fix is cheap defense-in-depth.
The IntersectionObserver-based scroll-spy tracked transitions into a
`passed` map. Jump scrolls (e.g. clicking a TOC link from far down the
doc) move headings from above the viewport straight to below the active
line without crossing the intersection area — IO's isIntersecting stays
false, no callback fires, and stale `passed=true` flags survived. The
last passed heading in document order wins, so the marker stuck on the
section just before the user's prior position instead of moving to the
clicked target.

Compute the active heading directly from live bounding rects on each
scroll instead. Same top-30% active-line model, no transition tracking.
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