Public
Personal portfolio for Ernest Njanké, founder of Njanké
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

Ernest Njanké — Portfolio

A self-contained, single-page personal portfolio for Ernest Njanké, founder of Njanké (creative & marketing agency).

Principles

  • Mobile-first & responsive — fluid type (clamp) and three layout breakpoints (40rem / 60rem).
  • Semantic & accessible — landmark elements, a skip link, aria-labelledby on every section, visible :focus-visible rings, reduced-motion support.
  • Self-contained — system font stack, no external font CDNs, no third-party scripts. The only external include is Val Town's optional error-reporting shim (std/catch), which logs browser errors back to the val.
  • Dark, editorial design — high contrast, generous whitespace, strong type hierarchy, restrained hover states on links and cards.

Structure

Rendering mermaid diagram...
FileRole
main.tsHTTP entry — page render, headshot, guest book routes, /source
index.htmlMarkup: header/nav · hero · work · about · guest book · footer
styles.cssAll styling (CSS custom properties, mobile-first)
db.tsSQLite schema + validated insert / list helpers
escape.tsHTML-escaping for user-generated content
weather.tsweather.gov fetch + 7-day summary + blob cache

Weather (NYC, 7-day)

The #weather section shows a 7-day NYC forecast from the National Weather Service (api.weather.gov) — no API key required, just a descriptive User-Agent (per NWS policy).

  • Grid point OKX/33,42 was resolved once from api.weather.gov/points/40.7128,-74.0060 and hard-coded to skip a lookup round-trip on every render.
  • Day/night periods are collapsed into one row per calendar day (daytime high, nighttime low, headline summary, max precip chance).
  • Cached in blob storage for 30 minutes; on an API error it falls back to the last good cache, and if there's nothing it degrades to a friendly message — the rest of the page always renders.

Guest book

Anyone can sign: POST /guestbook with name + message (standard HTML form). Entries are stored in this val's project-scoped SQLite database and rendered server-side, newest first.

  • Safe by default — all user input is HTML-escaped before rendering (XSS attempts are shown as plain text), and inserts use parameterized queries.
  • Validation — name ≤ 60 chars, message ≤ 500 chars, both required.
  • PRG pattern — a successful sign-in redirects (303) so a refresh won't re-submit. GET /guestbook also returns the entries as JSON.

To moderate, delete rows directly: DELETE FROM guestbook_entries_v1 WHERE id = ?

Editing the content

  • Socials / email — update the links in the <footer> of index.html (currently placeholder profile URLs and hello@njanke.com).
  • Projects — each is a <article class="card"> in the #work section.
  • Copy — hero line, About paragraphs, and stats live in index.html.