The Page-Load Flash, and a Team Roster Correction
The logo stopped animating itself into place on every page load — plus a person off the roster, a pronoun fix, and three scripts that had been 404ing in production the whole time.
Changelog — 2026-08-23_01
Summary
Two unrelated batches shipped together: a roster correction on /the-people, and
the fix for the page-load flash that had been affecting all 80 pages of the site.
The flash was never a team-page problem. It lived in Header.astro and the mode
script, so it happened everywhere — the team page just made it obvious by being
long and image-heavy.
The page-load flash
Root cause — the logo animated into position on every load
Header.astro server-rendered the logo wrapper in the homepage's starting pose:
<div id="logo-wrapper"
class="... p-8 ... transition-all duration-300 absolute"
style="top: 4rem; left: 0;">A dark #042632 box, 2rem of padding, absolutely positioned 4rem down the page.
Then a DOMContentLoaded handler rewrote twelve inline styles on every non-index
page to put it back where it belonged. Because the element carried
transition-all duration-300, that correction was animated — a dark rounded box
visibly sliding up and shrinking, on every single page view. Mobile homepage had
it too.
Fixed
Which pose applies is now decided at build time from
Astro.url.pathnameand expressed as CSS, so the correct pose is painted immediately and nothing needs correcting afterward.The resting pose is the default for every page and every viewport. The homepage opts into the tall drop-in pose via a
logo-wrapper--indexclass, scoped to@media (min-width: 1024px).The CSS values are copied exactly from the JavaScript end-states, so the homepage's drop-in and scroll behaviour are unchanged.
Deleted the twenty lines of load-time style rewriting. Non-index and mobile now return immediately.
Verified in the build: logo-wrapper--index appears in exactly one of 80 pages.
Contributing cause — the anti-FOUC script was not early
The script in BoilerPlateHTML.astro was commented "Early mode application to
avoid FOUC" but sat at the end of <body>. Measured in the built HTML:
136,110 characters of content before it, 1,258 after. The page fully painted in
the hardcoded data-mode="dark" before the script ran, so a stored light
preference repainted the entire page after paint.
Moved into <head>, immediately after <meta charset>.
Contributing cause — mode applied three times per load
The inline script applied it, the ModeSwitcher constructor applied it again at
module load, and a third DOMContentLoaded handler applied it once more. That
third call omitted initialLoad, so it defaulted to false and wrote to
localStorage — directly contradicting the inline script's own
"do not write to localStorage here" comment.
Removed the
DOMContentLoadedre-application fromsrc/utils/mode-switcher.js.Removed
html, bodyfrom thetransition: var(--transition-smooth, all 0.3s ease)rule inBaseThemeLayout.astro. Transitioning them meant every mode application cross-faded the whole page over 300ms instead of swapping instantly.
Scripts that were 404ing in production
Three script references used raw /src/ paths. Vite serves those in dev, so they
worked locally; dist/src does not exist, so they 404'd on the live site. The mode
toggle has therefore been dead in production.
| File | Reference | Resolution |
BoilerPlateHTML.astro | import '/src/utils/mode-switcher.js' | Converted to a bundled relative import |
BaseThemeLayout.astro | <script src="/src/utils/bio-modal.js"> | Removed — PersonCard.astro already ships an identical window.showBio |
ModeToggle.astro | await import('/src/utils/mode-switcher.js') | Removed; it was a fallback that could never have succeeded |
grep '"/src/' across all 80 built pages now returns nothing.
Team / People data updates
Removed
Removed one person from the roster, at the client's request:
src/content/people/team.json(20 → 19 entries)src/content/people/team-extended.json(21 → 20 entries)
Scrubbed the same name from
changelog/2026-04-09_01.mdandcontext-v/Updates-for-April-2026.mdso it does not surface in search. Git history was deliberately left intact.This also retired the last use of the string
TWF Strategy Institute, which had only ever appeared in that one person'srolefield. Every remaining Institute role now readsTWF Institute Partner.
Corrected
Ineeleng Molete — bio was written throughout in she/her; corrected to he/him in both
team.jsonandteam-extended.json. He remainsTWF Ambassador South Africa.
Notes for whoever touches this next
src/content/people/team-extended.jsonis dead. Nothing imports it; eleven files importteam.json. It survived this change only because it was already being edited. Worth deleting so there is one source of truth.Header.astro's logo now depends onAstro.url.pathnameat build time. If the homepage ever moves off/, update theisIndexPagecheck or the logo will render in the wrong pose.The two poses in CSS are ID-anchored (
#logo-wrapper.logo-wrapper--index) specifically to outrank the resting pose. Do not weaken those selectors to classes without re-checking specificity.
Verification
pnpm build— clean, 80 pages.pnpm exec astro check— 64 errors, unchanged from the pre-change baseline; all pre-existing and unrelated. Hints dropped 202 → 199 from the deletions.Verified structurally against the production build: script placement in
<head>, emitted CSS for both logo poses, bundle contents, and absence of/src/paths.Not visually verified — no browser tooling wired in this repo. The rendered result still wants a human walk-through of
/the-peopleand the homepage.
Parked
Two findings from the build audit were written up rather than fixed:
context-v/issues/Issue-of-Empty-Fellowship-Program-Page.md—/our-fellowship-programbuilds to a zero-byte HTML file.context-v/issues/Issue-of-Slide-Decks-Bypassing-the-Mode-System.md— the two deck layouts declare their own<html>and ignore light/dark entirely.
