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.
A self-contained, single-page personal portfolio for Ernest Njanké, founder of Njanké (creative & marketing agency).
- Mobile-first & responsive — fluid type (
clamp) and three layout breakpoints (40rem / 60rem). - Semantic & accessible — landmark elements, a skip link,
aria-labelledbyon every section, visible:focus-visiblerings, 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.
Rendering mermaid diagram...
| File | Role |
|---|---|
main.ts | HTTP entry — page render, headshot, guest book routes, /source |
index.html | Markup: header/nav · hero · work · about · guest book · footer |
styles.css | All styling (CSS custom properties, mobile-first) |
db.ts | SQLite schema + validated insert / list helpers |
escape.ts | HTML-escaping for user-generated content |
weather.ts | weather.gov fetch + 7-day summary + blob cache |
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,42was resolved once fromapi.weather.gov/points/40.7128,-74.0060and 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.
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 /guestbookalso returns the entries as JSON.
To moderate, delete rows directly:
DELETE FROM guestbook_entries_v1 WHERE id = ?
- Socials / email — update the links in the
<footer>ofindex.html(currently placeholder profile URLs andhello@njanke.com). - Projects — each is a
<article class="card">in the#worksection. - Copy — hero line, About paragraphs, and stats live in
index.html.