wavelength-demo — app shell and pages¶
The component layer: routing, the two pages, and the shared presentational pieces. (The data and math they consume are covered in Content pipeline and Wave geometry.)
Routing¶
There is no router library. App holds the parsed hash route in state,
re-parses on hashchange, and scrolls to the top on every navigation
(src/App.tsx:6-21):
export default function App() {
const [route, setRoute] = useState(() =>
parseRoute(typeof window !== "undefined" ? window.location.hash : ""),
);
useEffect(() => {
const onHash = () => {
setRoute(parseRoute(window.location.hash));
window.scrollTo(0, 0);
};
window.addEventListener("hashchange", onHash);
return () => window.removeEventListener("hashchange", onHash);
}, []);
return route === "reference" ? <ReferencePage /> : <HomePage />;
}
parseRoute accepts #reference or #/reference, case-insensitively;
anything else is home (src/lib/route.ts:4-8). Because the deploy is a
static serve -s dist (package.json:14), hash routing means both pages
work with zero server configuration.
HomePage anatomy¶
src/pages/HomePage.tsx renders, in order (:45-204):
- Fixed wave stage behind everything: axis labels from
FIELD(energy high/low, ascending-attractive / descending-aversive,src/data/modes.ts:189-194) around aWaveForm(:48-56). - Top bar with the brand mark (linking to the course), a
#referencenav link, the philosophy link, and a "Get the App" button (:58-82). - Hero from
HOME_HERO(eyebrow / heading / intro / scroll cue), with CTAs "Explore the Course" and "Track Your Wave" (:85-117). - Origin section from
ORIGIN, including the original labeled wavelength diagram with a long descriptivealttext (:119-138). - Mode sections: one full-screen bar per mode (index
01 / 21, title, gloss, optionalafter <source>attribution) followed by a transparentrevealzone the scroll engine measures (:140-165). - Closing CTA from
CLOSING, with the{count}footnote replaced by the liveMODES.length(:167-199). MobileAppCta— the bottom-pinned app button on phones (:203).
The copy the wave carries is chosen per phase: the canonical phase blurbs
while panel.canonical is true, otherwise the active mode's phase strings
(src/pages/HomePage.tsx:39-43; PHASE_BLURBS at
src/data/modes.ts:90-97).
ReferencePage anatomy¶
src/pages/ReferencePage.tsx reuses "the home page's machinery: one fixed
wavelength behind everything, full-screen 'bars' … and transparent reveal
zones between them where the wave fills in that Mode's medicinal (Mode color)
and toxic (red) doses" (comment, :14-17). Differences from home:
- The wave gets
variant="ref"and a per-layertint— the active layer'scolorHexmultiplied onto the energy field only, never the wave stroke (src/pages/ReferencePage.tsx:50-55,src/components/WaveForm.tsx:21-27,:45-50). - Each phase card shows a two-line dosage: medicinal in the layer's readable
textHex, toxic always inTOXIC_HEX(src/pages/ReferencePage.tsx:27-39). - Each of the nine bars is themed at render time:
readableInk(colorHex)chooses the ink, and the background is a gradient toshade(colorHex, 0.2)(src/pages/ReferencePage.tsx:104-116). - Bars show spiral color name,
MODE (Orientation)heading, description, orientation gloss, and a scroll hint (:122-135).
Shared components¶
| Component | File | Contract |
|---|---|---|
WaveForm |
src/components/WaveForm.tsx:35-144 |
Props: bodyOf(phase) => ReactNode, cardsRef (opacity-driven copy layer), optional tint, variant: "home" \| "ref". Renders field, tint layer, SVG wave, and one absolutely-positioned card per WAVE_NODES entry with CSS variables --band / --ink / --accent (:124-141) |
useWaveReveal |
src/components/useWaveReveal.tsx:40-94 |
(count) => { revealRefs, cardsRef, panel }; see Wave geometry |
RichText / Lines |
src/components/RichText.tsx:22-38 |
Inline-token rendering / line-break preservation for copy |
MobileAppCta |
src/components/MobileAppCta.tsx:6-14 |
Fixed bottom "Get the App" link, hidden on desktop via .app-cta-float CSS |
Accessibility notes encoded in the markup¶
- The entire fixed wave stage is
aria-hidden="true"— it is decorative relative to the readable bars (src/pages/HomePage.tsx:48,src/pages/ReferencePage.tsx:44); each mode section instead carriesaria-label={m.title}(src/pages/HomePage.tsx:142) and each layer sectionaria-labelofMODE (Orientation)(src/pages/ReferencePage.tsx:114). - The origin diagram's
alttext narrates the full wave semantics rather than describing pixels (src/pages/HomePage.tsx:131). - Scroll listeners are
passive: true(src/components/useWaveReveal.tsx:83).
External links (complete)¶
Every outbound URL in the component layer:
| Constant | Value | Declared at |
|---|---|---|
COURSE_URL |
https://aptitude.guru/philosophy/archetypal-wavelength |
src/pages/HomePage.tsx:14, src/pages/ReferencePage.tsx:12 |
APP_URL |
https://github.com/Geoffe-Ga/WavelengthWatch |
src/pages/HomePage.tsx:15, src/pages/ReferencePage.tsx:11, src/components/MobileAppCta.tsx:1 |
Grounded in wavelength-demo@78c703e, 2026-07-31.