Skip to content

feat(landing): hero refinements from the design prototype - #21

Open
madansap wants to merge 3 commits into
mainfrom
feat/landing-hero
Open

madansap wants to merge 3 commits into
mainfrom
feat/landing-hero

Conversation

@madansap

Copy link
Copy Markdown
Collaborator

Brings the hero work from the DevNepalFront prototype into the portal. Both
repos already run the same stack — Base UI 1.8, Phosphor, cn, shadcn 4.21,
three 0.178, Tailwind 4, Next 16, React 19 — so this is a graft, not a port:
no new dependencies.

First of a section-by-section pass over the landing page. Navbar and the
government masthead follow in their own PR.

What changed

  • NepalMap — Nepal drawn as a feathered field of blueprint grid, from the
    same Natural Earth 50m boundary the globe already uses for its land mask.
    Pure SVG, no dependencies, sits behind the hero at -z-10.
  • lib/layout.tssectionGutter / sectionPadding: one definition of
    the gutter and vertical rhythm the landing sections already shared by hand.
    The hero was the only section on py-16 rather than py-12 lg:py-16; it now
    lines up with members-section and project-spotlight-section.
  • Globe height follows the viewport (clamp on 80svh) instead of a fixed
    26rem, so it fills tall screens without overflowing short ones.
  • Globe alignment is now explicit. It used to infer its composition from the
    shape of its own box — "wide box, sit right of centre, leave room for the copy
    on the left". Below lg the hero stacks and that box is h-64 w-full, short
    and wide, so the rule fired exactly where it shouldn't and pushed the sphere
    to the right edge of a column with nothing beside it. Box shape cannot tell
    the two compositions apart (the desktop box is landscape too), so the caller
    decides: GlobeScene.setAlignment, driven by a media query on the same lg
    breakpoint at which the hero switches to two columns.
  • Hero heading second line capitalized; page title and description updated
    to match.

Deliberately not taken from the prototype

  • Its hero copy, which frames the portal as multi-ministry.
  • Its CONTRIBUTORS naming for the globe waypoints.

Both were corrected on main and stay corrected. The prototype's
ui/button.tsx was also skipped — this repo's version is a superset, with the
nativeButton / render props the CTAs depend on.

Review fixes included

A review pass found and this PR fixes:

  • setAlignment cleared the WebGL drawing buffer without painting the frame
    that every other resize() call site pairs with it. Masked in practice
    because crossing lg also changes the canvas box, so the ResizeObserver
    repainted — but with the hero scrolled out of view the frame loop is stopped
    and the canvas would have stayed blank. Camera framing is now its own step,
    applyViewOffset(), which touches no GPU buffer: an alignment change costs a
    projection matrix instead of a reallocated canvas, and it paints itself. Also
    removes two of the three resize() passes that ran during hero mount on
    mobile, and adds the disposed guard its sibling methods already had.
  • NepalMap covered its box, so once the hero stacked, 76% of the map's
    width
    was cropped and what remained was an unreadable band of grid rather
    than Nepal. Measured at a 357×852 box before the fix; it fits the box now.

One design call worth a second opinion

Switching the map from slice to meet trades coverage for legibility: the
country is now whole at every width, but in the stacked layout it occupies a
band across ~28% of the hero's height rather than filling it. Correctness of the
shape won, since the component's whole premise is that this is Nepal — but if
you'd rather treat it as pure texture, it is one attribute to revert.

Known, deliberately left for later

  • sectionGutter has one consumer; ten other call sites still hardcode the same
    gutter. Converting the rest belongs with the navbar/masthead PR, which touches
    those files anyway.
  • The hero's calc(80svh - 15rem) encodes the combined height of the masthead,
    navbar and hero padding, with nothing linking them. A --site-chrome custom
    property is the right fix; also parked for the chrome PR.
  • NepalMap's SVG ids are document-global. Harmless with one instance; needs
    useId() before a second.

Verification

bun run lint, typecheck, test (135 passing) and build all green.

Rendered and measured at 375, 820 and 1440, plus a live lg crossing at
1440→820 without a reload — the case the ResizeObserver was previously papering
over. Globe repaints centred, WebGL context alive.

🤖 Generated with Claude Code

Ports the hero refinements from the DevNepalFront prototype into the
portal's own hero, taking only the visual layer.

- NepalMap: Nepal drawn as a feathered field of blueprint grid, from the
  same Natural Earth 50m boundary the globe uses for its land mask. Pure
  SVG, no dependencies, sits behind the hero at -z-10.
- sectionPadding / sectionGutter in lib/layout.ts: one definition of the
  gutter and vertical rhythm the landing sections already share by hand.
  The hero was the only section on py-16 rather than py-12 lg:py-16; it
  now lines up with the rest.
- The globe's lower bound follows the viewport (clamp on 80svh) instead
  of a fixed 26rem, so it fills tall screens without overflowing short
  ones.

Deliberately not taken from the prototype: its hero copy (which frames
the portal as multi-ministry) and its CONTRIBUTORS naming for the globe
waypoints. Both were corrected on main and stay corrected.
The globe chose its horizontal position from the shape of its own box:
wide box, sit right of centre and leave room for the copy on the left.
Below `lg` the hero stacks, and the globe's box is `h-64 w-full` — short
and wide — so the same rule pushed the sphere to the right edge of a
column that has no copy beside it.

Box shape cannot tell the two compositions apart, so the caller decides
now: `GlobeScene.setAlignment`, driven by a media query on the same `lg`
breakpoint at which the hero switches to two columns. Above it nothing
changes.

Also capitalizes the second line of the hero heading, which is a
sentence of its own.
Three review findings.

The globe's alignment setter ran the full resize(), which ends in
renderer.setSize() and so clears the drawing buffer, but did not paint
the frame that every other resize() call site pairs with it. Nothing was
visibly wrong only because crossing `lg` also changes the canvas box, so
the ResizeObserver repainted a moment later; with the hero scrolled out
of view the frame loop is stopped and the canvas would have stayed
blank. Camera framing is now its own step, applyViewOffset(), which
touches no GPU buffer: an alignment change costs a projection matrix
instead of a reallocated canvas, and it paints itself.

NepalMap covered its box, so once the hero stacked and the box went
narrow and tall, three quarters of the map's width was cropped and what
remained was an unreadable band of grid rather than Nepal. It fits the
box now.

The hero heading's second line was capitalized last commit; the page
title and description still carried the lower-case form.
@madansap
madansap requested a review from voidash as a code owner September 21, 2026 15:29
@rosanyonghang
rosanyonghang force-pushed the feat/landing-hero branch 2 times, most recently from 282fde6 to 6cebfc6 Compare September 21, 2026 17:22
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