A new hero, a browser-mockup share image, and templates that are actually Supabase's
v1.7.1- —session: Supabase hero redesign, browser-mockup OG, demo wayfinding + authentic templates
- —changelog: v1.7.0 — RLS Verify demo refined
Design note
A share image should be a doorway, not a decal. The link preview is now the page itself rendered inside a browser window on Supabase green (the Varmply treatment), so the thumbnail reads as "a real thing you can open," not a logo. The demo's wayfinding follows the same restraint: no "start here" arrows on the canvas, just a one-line note and a slightly lifted "concept" badge, because pointer-arrows on a faithful Studio recreation would read as an annotation left on by mistake.
The RLS Verify demo, refined — worst-case runs, honest fixes, real Studio templates
v1.7.0- —session: Supabase policy-editor demo — page-level scenario control, authentic templates, message + chrome cleanup
- —feat(supabase): neutral controls, Supabase-style lint card, template layout
- —changelog: v1.6.0 — See who your RLS policy actually exposes, before you hit Save
Design note
A verification tool must never let a flattering view imply safety. Switching a policy always lands on the worst-case identity — the first caller it leaks to — so you never open on the one identity (e.g. anon) that makes a broken policy look fine, and a single always-visible policy verdict sits below the evidence so a clean single-identity view can't read as all-clear. In the same spirit, the fix action was reworded from "Quick fix" to "Suggest a scoped policy": a tool can't know your tenant column, so it scaffolds a suggestion, it doesn't claim magic.
See who your RLS policy actually exposes, before you hit Save
v1.6.0- —session: scenario-driven RLS policy-editor demo + Verify UX rework
- —feat(supabase): 1:1 policy-editor walkthrough + loud design framing
- —style(supabase): typographic hierarchy for concept vs demos
- —style(supabase): translucent badges, centered hero, full-width body text
- —refactor(supabase): plain hero + Linear-style code editor in demos
- —feat(supabase): Linear/Vercel-style ambient hero
- —feat(supabase): Studio table hero visual
- —style(supabase): use real dark-mode CTA green for filled buttons
- —style(supabase): exact Studio greys + green, Manrope/Inter fonts, drop hero underline
- —feat(tabby): scaffold /tabby pitch — decline-redesign demo
- —perf(assets): compress playground + craft media (public 290MB -> 135MB)
- —research: Tabby verification pass — facts corrected, problem confirmed, unowned
- —queue: Tabby decisioning-transparency pitch brief (verify-before-build)
- —changelog: v1.5.1 — Supabase RLS verification pitch
Design note
The Verify tab is a loud nudge, never a hard gate on Save. A leaking policy is still valid SQL — a public table legitimately wants using(true) — so blocking Save would punish correct policies and contradict the honest claim that no tool can read intent. The intervention makes exposure visible and testable, and it green-lights a correct policy just as clearly as it flags a leaking one.
Supabase pitch: proving an RLS policy is actually enforced
v1.5.1- —session: Supabase RLS pitch, verification demos and three-demo restructure
- —fix(bamboo): correct review-audit stats to verified sources (4.1/5, 202 reviews)
- —changelog: v1.5.0 — Bamboo pitch real app flows, one-tap direct buy, honest pricing
Design note
A policy can read "enabled" and still be lying. Supabase already shipped the tools that write policies and warn when RLS is off; the unowned gap is proving a policy that is on is actually correct. So instead of another builder, the demo is a verification panel: run the policy as each identity and show the rows they truly get. Three self-contained demos, one per failure mode, beat one panel with sixteen toggles.
Bamboo pitch: real app flows, one-tap direct buy, and honest pricing
v1.5.0- —session: Bamboo demo accuracy pass, direct-buy reframe, transparent cost framing
- —polish(bamboo): center hero vertically, center press logos, more spacing
- —feat(bamboo): full dark-green theme, interactive demos, Dangote IPO
- —feat(bamboo): branded review-audit cards + light/dark switcher
- —feat(bamboo): company pitch page — Bamboo trust-layer prototype
- —changelog: v1.4.1 — Case studies now lead with the design process
Design note
Exposing an FX spread as its own deduction reads as unmasking a partner's margin, which spooks the very people you are pitching. The fix was to show the effective rate and the net you actually get back, never a line that looks like profit. Transparent to the user, revenue-neutral to the business. Same principle Wise built a moat on.
Case studies now lead with the design process, not just the product
v1.4.1- —Varmply case study reordered around the design work: a contents nav telegraphs Research → Design decisions → Live product in the first screen, the decision log moved above the embed wall, and a real "How it evolved" section shows the actual earlier landing pages (recovered from old commits) before the live product.
- —GRC platform case study gained a per-screen drift gallery — Genesis → Surface System v1 → Design Language v2 tabs, each a live embed with the rationale for what changed and why — plus process artifacts (build timeline, system architecture, scoring matrix).
- —Project link previews now use the real pastel preview cards (screenshotted at 1200×630) instead of generated mockups; home and article covers went light-mode.
- —Fixed a Vercel deploy blocker: the 282MB `/public` asset dir was being traced into every serverless function, exceeding the 250MB limit.
Design note
A recruiter said the portfolio showed "only product shots, not process." The audit found the opposite — a 23-entry design-decision log already existed, just buried dead-last under a wall of live embeds. The fix was discoverability and framing, not writing new process: surface what was already there, and make the building the edge rather than the excuse.
Every project and article now has its own designed link preview
v1.4.0- —Each project (Varmply, ShipLock, GRC platform) now generates its own Open Graph image — the live product screenshot in a browser frame, with a unique title, description, and accent color.
- —Every article gets its name and description in the link preview, over one shared, designed "Writing" cover.
- —Projects now set explicit og/twitter titles instead of inheriting the site title, and a canonical `metadataBase` so image URLs resolve to kenepraise.com in production.
- —Fixed a latent double-suffix in project page titles.
Design note
Project link previews show the real product running in a browser frame, not a generic card — a shared link should prove the product, not just claim it. Articles share one designed "Writing" cover but each carries its own title and description.
Book a build call without leaving the page
v1.3.0- —The founders page moved from /build's old home to a clearer two-door funnel: a free, email-only teardown, or a call-first build inquiry
- —Ladder cards are the single entry point — click any card to drop into a form tailored to that offer (heading, intro, deliverables, and CTA all adapt)
- —Build inquiries open an inline Cal.com booking modal, prefilled with name, email, and project notes, then confirm with a "you're on the calendar" state
- —Added a subtle title dropdown in the form header to switch between offers without scrolling back up
- —Ladder cards now respond as one pressable button, with a scroll-driven active state on mobile
- —Fixed a transparent dark-mode header and refined the popover shadow and elevation tokens
Design note
Two doors, not one form. The free teardown stays strictly email — low friction, and the call gets pushed privately after the findings land. Every build tier is call-first: the form is an intake that opens a Cal.com booking right on the page, so scope is set on a call before anything's built. Pricing stays visible to qualify, Arc-style.
Launch: work with me, from a free teardown to a shipped MVP
v1.2.0- —New `/launch` page: a value ladder from a free teardown to a full product build (landing page → MVP slice → product build), with live embeds of shipped work — ShipLock, Varmply, and the GRC platform.
- —Added a "Launch" link to the header nav and a "Work with me" section on the home page.
Design note
The offer page keeps the full site nav instead of the stripped-down header a "landing page" usually gets. For a design hire, feeling like a trustworthy part of a real site beats the marginal conversion gain of removing escape routes — so the whole thing reuses the existing design system, and the shipped-work embeds sit right next to the pitch.
The design system documents itself — and versioning goes semantic
v1.1.0- —feat: design-system component docs, semver changelog scheme, footer & embed refinements
- —fix(confidential): remove vestigial logo overlay on dashboard embed
- —changelog: v1.0.32 — confidential GRC case study goes public with live product embeds
Design note
The first release cut under semantic versioning: feature sessions now bump the minor and fixes bump the patch, so the jump from 1.0.32 straight to 1.1.0 marks the scheme change rather than a glitch. Fittingly, the headline work is the design system documenting itself — every components/ui primitive now renders live on the /design-system page, imported from source rather than copied, so the showcase can't quietly drift from what the site actually ships.
Confidential GRC case study goes public — password gate off, live product screens embedded
v1.0.32- —refactor: remove shiplock cross-link from confidential case study & remove salary mentions
- —feat: confidential case study de-gating, live embeds refresh & card redesign
- —changelog: v1.0.31 — Varmply rebuild sprint, confidential GRC rewrite, live design-system embeds
Design note
De-gating the case study meant it had to survive being read cold by anyone, so anonymization carries the credibility instead of a password: one redaction bar and an "Under NDA" chip signal the client is real and protected, generic "cybersecurity consultancy" prose matches the public ShipLock vocabulary, and ₦50M becomes "eight-figure engagement" so an unnamed client never carries an exact contract value.
Varmply rebuild sprint, confidential GRC rewrite, live design-system embeds
v1.0.31- —session: confidential case study rewrite with live GRC embeds + varmply rebuild section
- —perf: persist nav by hoisting Header into the root layout
- —changelog: v1.0.30 — craft gallery without filters, nav expansion, email copy toast
Design note
The confidential page's live embeds sit after the turning-point narrative so they read as evidence rather than decoration: the case study claims the design system governs the product, then the product's own components render the proof, with fictional data standing in for client truth.
Craft gallery without filters, nav expansion, email copy toast
v1.0.30- —session: craft filter removal, nav updates, email copy toast, hydration fixes
- —fix: wrap Footer return in fragment for WhispersCallout sibling
- —feat: email copy toast in footer — green dot pill matching whisper style
- —feat: copy-to-clipboard email (hi@kenepraise.com) — no mailto links, Resend stays on praizzze4
- —fix: remove idle card shadows on craft page — too heavy in light mode
- —feat: update all emails to hi@kenepraise.com
- —fix: invalid <p> wrapping <div> in Divverse hover card — hydration error
- —feat: add Sidequest to nav, hide Craft on mobile
- —feat: add Craft to nav
- —fix: hydration mismatch in ThemeToggle — sync dark state after mount
- —feat: restore tasks-first order for ShipLock preview sections
- —style: white chrome header on ShipLock preview mockup
- —feat: re-enable ShipLock live iframe preview card
- —style: ShipLock card blue pastel background (no frame)
- —revert: remove ShipLock live iframe card pending refinement
- —changelog: v1.0.29 — live iframe preview cards for Varmply and ShipLock
Live iframe preview cards for Varmply and ShipLock
v1.0.29- —Built /preview?section= routes in ShipLock (dashboard, scope-changes, requirements, tasks) with seeded demo data and middleware bypass for unauthenticated access
- —Replaced both Varmply and ShipLock static gradient cards with live iframe embeds inside a Chrome browser frame (desktop) or iPhone 14 Pro frame (mobile)
- —Phase-based fall/eject transition: current screen drops down on hover, next ejects up from below; snaps back to screen 1 on mouse leave
- —PhoneFrame component ported from Varmply — compact status bar row aligned to dynamic island center, content pushed below island
Design note
Each project card now embeds the real product UI in a floating frame — Chrome browser on desktop, iPhone 14 Pro on mobile. Hovering triggers a fall-off/eject transition that cycles through key screens. The second screen is preloaded on mount so the first drop is instant. ShipLock required building a dedicated /preview route with seeded demo data and a middleware bypass before it could be embedded.
Light mode default — visitors land in light, dark still available
v1.0.28- —session: light mode as default landing theme
- —chore: remove push credential test file
- —test: verify push credential flow after token rotation
- —changelog: v1.0.27 — Varmply embed windowing + coherent case-study arc
Varmply embeds load in a sliding window; case study reads as one arc
v1.0.27- —Varmply embeds now load through a shared manager that keeps ~6 live at a time, biased ahead in scroll direction, instead of mounting and reloading each one independently
- —Added preconnect to both embed origins so the first iframe connection is already warm
- —Rebuilt the Varmply case study into one coherent arc: brief, problem, solution, how the approach was validated, the live screens, then a conclusion with honest next steps
- —Validation and results rewritten to be truthful for a pre-launch product — grounded in lived experience and conversations, no fabricated metrics
Design note
Embedding 20+ live iframes meant the case study could either hammer every origin at once or reload each embed cold on every scroll-back. A shared manager keeps only the six nearest the viewport live, biased ahead in the scroll direction, so the next ones warm up before you arrive — fast without keeping everything mounted.
Craft breathes on mobile; Varmply build decisions caught up
v1.0.26- —Craft page gets more top padding on mobile so content clears the floating header
- —Varmply Build decisions log brought current — added the May 22 calls on auth/onboarding flows, problem-space framing, and the frozen hero snapshot
- —Corrected the butler-relay article timeline — building for three months (not six), and the recording gap surfaced after weeks, not months
Design note
The 'project changelog' is the on-page Build decisions timeline, not a separate changelog file — it lives where the work is shown so the reasoning sits next to the result. Caught it up with this session's Varmply decisions.
Varmply embed sources corrected, project link points to varmply.com
v1.0.25- —session: fix embed URLs, project link, and auth/onboarding direct embeds
- —fix: revert embed srcs to varmply.vercel.app, update project link to varmply.com, direct section embeds for auth/onboarding
- —changelog: v1.0.24 — Varmply case study, combined flows, embed overhaul
Design note
Auth and onboarding embeds point directly to their first section — auth screens have built-in sign in/sign up links, onboarding has natural Next navigation. No tab switcher overlay needed.
Varmply case study — problem statements, combined auth/onboarding flows, embed overhaul
v1.0.24- —feat: problem statements section — 3 cards with problem, signal, and response for each core trust issue Varmply solves
- —feat: auth embed collapsed to single switcher (Sign Up ↔ Sign In) with floating pill tab strip
- —feat: onboarding embed collapsed to single step-through (Sign Up → Role → Creator Profile → Creator Platform → Sponsor Setup)
- —fix: hero portrait image — sponsor-hero-artist.png missing from preview app public folder
- —fix: CI failure — no-nested-ternary lint errors in Card.tsx and CampaignCards.tsx
- —refactor: varmply page reorganized — Website → Auth & Onboarding → Web App; duplicate blocks removed
- —refactor: all 14 embed descriptions rewritten with specific design decisions
- —refactor: marketing site embed URLs updated to varmply.com
Design note
Auth and onboarding collapsed from 8 separate static embeds into 2 interactive ones — a floating pill switcher for auth, and a step tab strip for onboarding. The problem statement section above the design breakdown is the case study frame: each card has a numbered problem, a Signal column (how it was observed in the market), and a Response column (what Varmply actually does about it).
Varmply landing embeds — routing fix, org sync, frozen hero with floating pills
v1.0.23- —fix: all marketing site embeds showing footer only — home blocks pointed at / which stripped ?section= params and section names mismatched data-section attributes; rewired each block directly to /sponsors or /creators with correct section names
- —feat: frozen hero snapshot in varmply-frontend-play — portrait glass panel + floating TikTok/Instagram OrbitChips; HeroBlock now points to preview app instead of live site
- —feat: synced How It Works, Why Varmply, Mobile App, Community, Testimonials, Footer from Varmply/varmply-landing org repo into kene-praise/varmply
Design note
The hero uses a frozen snapshot in the preview app rather than the live marketing site — this locks in the older design (portrait + floating TikTok/Instagram pills) permanently, while the remaining landing sections (How It Works, Why Varmply, Community, Testimonials, Footer) pull live from the org repo via varmply.vercel.app.
Varmply embed fixes — marketplace revert, onboarding flow, CSP + routing
v1.0.22- —fix: forward ?section= query params through homepage redirect — all marketing site embeds were landing on hero
- —fix: add kenepraise.com to Varmply CSP frame-ancestors — custom domain blocked all portfolio iframes
- —feat: 6 onboarding embed blocks — signup, signin, role selection, creator profile, platform connection, sponsor setup
- —refactor: campaign builder collapsed from 4 step embeds into single interactive flow
- —fix: MyCampaigns iframe crash — seeded wrong workspace type; replaced with properly-typed SponsorWorkspaceResponse + CreatorWorkspaceResponse mocks
- —fix: revert marketplace to 3d2643b — landscape TONES banner cards with outline budget number, campaign cards with SPOTS FILLED/REMAINING
Design note
The homepage redirect in the Varmply marketing site was silently stripping all query params — every section embed was landing on the default hero. Fixed by forwarding searchParams through the redirect. The marketplace was also reverted to the 3d2643b design with landscape TONES banner cards (outline budget number) and large cover campaign cards showing SPOTS FILLED/REMAINING. Onboarding adds 6 new embed blocks backed by a new-user mock profile with INCOMPLETE status so the pre-auth screens render without needing a real session.
Live Varmply app embeds — campaigns, campaign builder, and full onboarding flow
v1.0.21- —session: varmply web app + onboarding embeds
- —fix: OG image layout — bio below name, tags in bottom-left, Abuja tag
- —feat: dark OG image — ghost watermark, floating role badge, photo caption
- —fix: OG image — local fonts and pre-resized headshot, no runtime deps
- —feat: dynamic OG image — Geist, design tokens, headshot, og-preview page
- —fix: replace Vercel triangle favicon.ico with correct blue arrow PNG
- —fix: restore light iframe on mobile in overwrite demo — only dark layer uses static image
- —fix: remove icons metadata override — let file convention handle icon.svg and apple-icon.png
- —fix: apple-icon, favicon PNG, OG domain, domain expansion mobile image (overwrite only)
- —fix: restore blue arrow favicon, footer desktop spacing, varmply block removals
- —fix: varmply iframe lazy load, footer mobile 2x2 links, whispers toggle size
- —feat: craft page, footer craft link, availability date update to July 2026
- —fix: bump whisper overlay timings — pre-enable 2400ms, enabled 2000ms
- —fix: lock timezone cell height to prevent layout shift on quip swap
- —fix: show alpine world card on mobile, remove duplicate back link
- —fix: hide alpine world, apology, and youtube on mobile
- —fix: domain expansion URL bar no-wrap on mobile
- —changelog: v1.0.20 — sidequest mobile crash fixes
Design note
The campaign builder collapses from 4 separate step embeds into one interactive flow — portfolio visitors can walk through the entire creation process live. Mock workspace data uses the exact SponsorWorkspaceResponse / CreatorWorkspaceResponse API shapes so the campaigns screen renders without error.
Sidequest page mobile crash fixes — one demo at a time, no eager iframes
v1.0.20- —fix: mobile OOM crashes on sidequest page
- —feat: status dot in whispers callout — green on, grey off
- —fix: StatusRow px-4 spacing from left border, DeadCard lighter on dark
- —fix: StatusRow py-5, DeadCard uses danger-muted for reliable dark mode
- —fix: StatusRow padding + DeadCard dark mode via inline CSS vars
- —fix: remove stale html2canvas patch — build was failing in postinstall
Design note
The root crash was four full-app iframes loading simultaneously on mobile. The fix activates only the most-visible demo at a time via IntersectionObserver — so the renderer holds at most two iframes. Three.js never loads on mobile (desktop-only note instead), and YouTube only loads on click. Three independent memory ceilings, all dropped.
Mobile polish — commands scroll, decisions collapse, tablet view removed
v1.0.19- —session: tablet view removed, decisions collapsed, article mobile polish
- —changelog: v1.0.18 — ShipLock article facts corrected, tweet queue fully imaged and scheduled
Design note
Commands on mobile now scroll horizontally rather than wrapping — the whole row becomes the copy target, the icon disappears. Cleaner than truncating or shrinking the font.
ShipLock article facts corrected, tweet queue fully imaged and scheduled
v1.0.18- —session: fix ShipLock article scope figures, tweet queue images + schedule
- —feat: add apple-icon.png for iOS/Apple link preview icon
- —feat: OG image — static screenshot of homepage
- —feat: OG image — headshot + name + availability chips on dark grid background
- —fix: add patch-package to devDependencies — postinstall was failing on Vercel
- —changelog: v1.0.17 — Two experiment articles and an honest postmortem on Alpine World's sourcing log
Two experiment articles and an honest postmortem on Alpine World's sourcing log
v1.0.17- —session: two experiment articles + alpine sourcing log + corrected closing paragraph
- —changelog: v1.0.16 — Domain expansion that writhes from the iframe's own toggle
Design note
The closing paragraph in the alpine-sourcing article had to be fully rewritten — it described the current build as "simpler, without PBR splatting, without spatial audio, procedural terrain" when all of that is false. v2 has the real SRTM heightmap, full PBR shader, HRTF spatial audio, and instanced foliage. The trees still float because the CPU and GPU height pipelines produce different Y values from the same heightmap. Accuracy matters most when the article is literally about what failed.
Domain expansion that writhes from the iframe's own toggle
v1.0.16- —session: domain expansion playground demo + organic blob clip-path
- —revert: restore 720px max-width on homepage, about, header, footer
- —experiment: bump max-width to 936px across homepage, about, header, footer
- —fix: match ShipLock page max-width to Varmply (936px)
- —polish: restore original GitHub button style on ShipLock page
- —polish: skeuomorphic Button for ShipLock live CTA, full-width description on both project pages
- —revert: restore original homepage and about bios
- —refine: remove tagline tag, bake 48hr line into both bios, restore body length
- —refine: bio metrics baked in, about cut down, ShipLock CTAs on project page, How I work moved, Changelog removed from nav
- —feat: portfolio pre-application pass — positioning, metrics, ShipLock CTAs, how I work
- —feat: OG image, tighter description, Twitter card metadata
- —session: swap valley video — nocookie embed, autoplay, playlist
- —session: playground UX — audio control pill, headphones modal, zone label, about collapsed
- —changelog: v1.0.15 — Playground as inline card with expand, controls, and headphone hint
Design note
A smooth circle clip-path felt sterile — a domain expansion should writhe. Swapped CSS transitions for a rAF-driven path() blob with three layered sine frequencies, each at different phase speeds, so the boundary undulates while it grows. The trigger also moved off the chrome bar and onto the iframe's own ThemeToggle — embed-mode detection makes the in-iframe button postMessage `{type, x, y}` to the parent, which translates the coords by the iframe's scale factor and runs the animation from that exact spot. The demo now feels owned by the live homepage inside it, not by a fake browser frame around it.
Playground as inline card with expand, controls, and headphone hint
v1.0.15- —session: playground as card — inline layout, controls fix, expand/shrink
- —session: playground redesign — scrollable, compressing scene, apology + valley video
- —changelog: v1.0.14 — Whispers hidden interstitials, riddle, footer easter egg
Design note
Removed the full-screen scroll illusion entirely — the playground now lives as a proper card in the page layout, matching the project/tool card pattern. The 3D canvas stays mounted when expanding to full screen (position:fixed trick), so the scene never resets.
Whispers — hidden interstitials, riddle, and footer easter egg
v1.0.14- —session: Whispers system — first-visit quote, riddle, footer toggle
- —session: ShipLock project page, about image fixes, footer redesign
- —changelog: v1.0.13 — no-flash transitions, TransitionLink
Design note
First-visit quote is SSR-rendered via cookie detection so it is in the initial HTML at full opacity — zero flash possible. Riddle fires via a custom event dispatched before router.push(), matching the same no-flash pattern as the regular quote system. The footer toggle is intentionally unlabelled — hover only — as part of the footnoters joke.
No-flash page transitions — interstitial fires before the next page loads
v1.0.13Design note
The fix required intercepting navigation at the link level rather than reacting to pathname changes. TransitionLink fires the overlay on click, then calls router.push two animation frames later — by which point the overlay is already rendering and covers the page mount completely.
Page transitions that think — contextual interstitial quotes
v1.0.12Design note
The overlay fires independently of how fast the destination page loads — the 1600ms timer runs regardless. This prevents the subconscious association of "quote = slow page." The destination finishes loading under the overlay; it's revealed when the thought passes.
ClipSync tool, writing hub, persistent footer, and favicon
v1.0.11- —ClipSync added as a tool page with origin story, 4 feature cards, and live app link
- —ClipSync article: "A free weekend, a real problem, and forty-eight hours to ship."
- —ShipLock added to Featured Work with dark indigo gradient card
- —Standalone /writing page: featured article (ShipLock), all articles list, RSS feed
- —Recent Writing section added to homepage
- —Persistent Footer component extracted and applied across all pages (hidden on /playground)
- —Favicon updated from Vercel logo to cursor avatar circle (dark circle, hatch, blue arrow)
- —Global spacing rule applied: heading+body pairs tightly grouped, wider gap to next element
- —Font size bumped to 17px root; article and project card body text bumped to text-sm
- —Light mode nested containers now use --bg-subtle (near-white) instead of dark grey
- —About page: social links 2x2 grid, Recognition section removed, Building updated to Varmply
- —Theme toggle double-click bug fixed (state now initializes from DOM)
- —Writing nav link updated to /writing (standalone page)
- —Butler and Relay tool pages added with section spacing and typography improvements
Design note
Spacing rule formalized this session: title + body = one tight unit (gap-1 to gap-1.5 inside), with a wider gap to the next element. Applied globally across cards, sections, and article pages. Also distinguished nested containers (--bg-subtle, near-white) from top-level containers (--surface, white) to restore visual hierarchy that was lost when bg-subtle was set too dark.
Working contact form, Varmply-only projects, and real social links
v1.0.10- —session: working contact form, Varmply-only projects, real social links, success state
- —changelog: v1.0.9 — varmply block viewer, live iframes, scroll lock, decisions log
Varmply block viewer — live iframes, scroll lock, and 18-entry decisions log
v1.0.9- —session: varmply block viewer — iframe isolation, scroll lock, decisions log
- —feat: replace placeholder decisions log with full 18-entry build history
- —fix: smooth height transition on block-wrapper iframe container
- —feat: dynamic iframe height via postMessage, remove creator hero block
- —feat: section isolation — all blocks use ?section= params, scrolling=no on iframes
- —feat: curated block selection with section isolation
- —changelog: v1.0.8 — varmply block viewer expands across three pages and footer
Design note
Iframe section isolation required removing CSS !important injection from the layout inline script — it was hiding sections that had no matching id attribute. Lenis proved to be the real blocker for scroll prevention since it intercepts wheel/touch events before CSS overflow:hidden can act. Both were fixed independently, then ResizeObserver replaced one-shot rAF height reporting so iframes self-size as 3D and animated content settles.
Varmply block viewer expands across three pages and footer
v1.0.8- —session: varmply block viewer — sponsors, creators, footer sections
- —chore: track *.glb files with Git LFS
- —changelog: v1.0.7 — alpine world goes alive, free camera, weather, day cycle
Design note
Blocks grouped into labelled page sections (Home / Sponsors / Creators / Footer) using a SectionDivider component. The divider uses a hairline border + mono uppercase label rather than a full heading — keeps the information density high without competing with the block labels themselves. iframes are now fully interactive: pointer events on, scrolling enabled, so you can click and scroll within each embedded section to see the real responsive behaviour.
Alpine world goes alive — free camera, weather, and a full day cycle
v1.0.7- —[playground/terrain] fixed transparent floor — DoubleSide material + displacementBias: -3 to prevent camera clipping underground
- —[playground/terrain] added envMapIntensity: 0.15 and base color #5a4a2e to kill glassy HDR washout on the ground
- —[playground/camera] FreeCamera — PointerLockControls + WASD/Space/Shift movement; 1–6 digit keys teleport to zone viewpoints
- —[playground/snow] SnowSystem — 1800 falling particles with horizontal drift; resets above camera, follows player position
- —[playground/road] DirtRoad — ribbon geometry pinned to terrain height along camera spline, dark earthy color
- —[playground/clouds] CloudDrift — 18 puff-sprite clouds (canvas CanvasTexture), slow eastward drift, boundary reset
- —[playground/birds] BirdSystem — 6 flocks × 5 birds, canvas V-wing sprite, sine-wave orbit paths at altitude 16–38
- —[playground/time] TimeOfDay — AmbientLight + DirectionalLight driven by an 8-min day cycle; fog color shifts dawn→noon→dusk→night
- —[playground/hud] alive-mode controls hint added (click to capture / WASD / Esc)
- —[playground/canvas] FreeCamera, SnowSystem, DirtRoad wired into PlaygroundCanvas
- —[playground] Step 8 (Spotify) permanently removed per decision — world atmosphere replaces audio player
Design note
The playground shifted from a passive scroll-diorama to a first-person explorable space. The key decision was keeping weather and time as ambient systems — SnowSystem, CloudDrift, BirdSystem, and TimeOfDay all run silently in the background, not as UI features. They exist to reward players who actually enter alive mode and look around. The 8-minute day cycle length was tuned to feel perceptible without feeling like a clock. Spotify (Step 8) was permanently dropped — it was the right call; the world speaks through atmosphere, not a player.
Terrain splat, HRTF spatial audio, and ghost HUD for the alpine world
v1.0.6- —[shader] TerrainMesh — MeshStandardMaterial extended via onBeforeCompile; fragment shader blends grass/rock/dirt/snow by normalised height and finite-difference slope; tiling at 32×; snow peaks above h=0.58, rock on steep slopes, dirt at mid-altitude, grass elsewhere
- —[shader] WaterPlane — custom ShaderMaterial at lake zone (x=-2, y=2.2, z=52), 55×40 world units; 2-octave FBM wave function, shore fade via smoothstep edge mask, depth tint, shimmer highlight; uTime updated each frame
- —[shader] Wind foliage — makeWindMaterial() clones MeshStandardMaterial, injects vertex sway proportional to local Y (roots stay planted); applied to Flower_1–5_Clump, Grass_Large, Grass_Large_Extruded via InstancedModel wind prop
- —[audio] AudioEngine class — Web Audio API HRTF PannerNode per source (panningModel: HRTF, distanceModel: inverse); per-zone GainNode crossfade via linearRampToValueAtTime (2s); synthetic reverb from exponentially-decayed noise impulse response
- —[audio] 8D orbiting sources — field birds, insects, forest wind, mountain wind, frogs rotate at 0.2 rad/s around listener XZ at radius 14wu; reverb wet scales 0.10→0.40 with normalised camera altitude
- —[audio] AudioSystem.tsx — R3F component inside Canvas; boots AudioEngine on audioStarted; drives listener pose, orbit tick, zone crossfades, reverb each useFrame
- —[page] AudioPrompt — bottom-left "tap for audio" mono button; sets audioStarted on click or first keydown; respects browser autoplay policy
- —[component] PlaygroundHUD — ghost overlay; Framer Motion fade+slide on mousemove, 3s auto-hide; journey/alive mode toggle, speed slider (0.5–2×), volume slider + mute toggle, Spotify placeholder (greyed, Step 8 deferred)
- —[design] Headphone nudge — "use headphones" appears bottom-right for 4s after first audio unlock, then fades; mono 10px, 0.25 opacity — invites but doesn't demand
Design note
The three systems in this session are about making the world feel real without any single obvious trick. Texture splatting means the terrain reads as actual geography — snow caps, rocky slopes, grassy valley floor — without a single UV-painted texture. The audio system is the one detail most visitors won't consciously notice but will feel: HRTF panning means sounds have genuine left/right/behind positioning in 3D space, and orbiting sources create the "8D" movement without any fake stereo tricks. The HUD is invisible until you need it — ghost-mode by design, so the landscape isn't cluttered with controls. The "use headphones" nudge is the one moment of explicit invitation.
Hand-crafted brand icons and a first essay on designing systems
v1.0.5- —[ui] Redesigned the Toolkit section on the about page — switched from text abbreviations to native SVG brand marks, matching the visual pattern of the design system page
- —[ui] Hand-sourced all SVG paths from Simple Icons, Wikimedia Commons, and official brand assets — covers Claude (Anthropic "A" mark), OpenAI (interlocking rings), Gemini (4-pointed star), Figma (5-shape logo), Cursor (hexagon), Photoshop and Lightroom (Adobe app tile), DaVinci Resolve (3-circle mark), Google (G shape), Lovable (L-path), v0 (Vercel wordmark), CapCut (inner cut symbol)
- —[content] Published first writing post at /writing/designing-systems — "On designing systems before they were needed" — covers the four internal systems built for this portfolio: changelog pipeline, design token architecture, git automation, and content engine
- —[content] Writing post includes rich Framer Motion animations — reading progress bar, scroll-triggered section reveals, animated system diagrams (dual-track changelog flow, token cascade, git timeline, content pipeline), pull quotes with animated left-border accent, and animated stat counters
- —[infra] Linked first writing post from about page Writing section; remaining posts stay "Coming soon" until ready
Design note
Each toolkit SVG was sourced from Simple Icons, Wikimedia Commons, and official brand assets — no icon library. The goal was pixel-accurate brand marks rather than generic approximations. The first writing post was written before any project case studies shipped deliberately — the systems (changelog pipeline, token architecture, git workflow, content engine) were the actual first work, and documenting them first was honest sequencing, not filler.
Homepage rebuilt: bio, bento stats, timezone quips, and identity update
v1.0.4- —[content] Rewrote homepage bio from generic copy to experience-grounded copy — 5+ years, fintech/SaaS/cybersecurity context, building with AI as a core practice
- —[identity] Updated title from "Design Engineer" to "Product Designer × Builder" across homepage hero, about page badge, and experience entry — reflects the actual shape of the work
- —[ui] Replaced flat info rows on homepage with a bento-style stats grid — pulsing availability dot, experience count, current role, and live clock with animated hands
- —[ui] Added Starfire as the hero project card on homepage; updated portfolio card gradient to blue; replaced placeholder card with a styled "Something new" entry
- —[ui] Added per-project hover cards on homepage (Divverse Labs inline link with animated tooltip showing company context on hover)
- —[ui] Built timezone quip system — detects visitor's UTC offset vs Abuja (GMT+1) and shows a context-aware typewriter-animated string (e.g. "YOU'RE 6H BEHIND — ALREADY ON TASK 10 :)"), with quips that vary by time of day (morning / evening / night)
- —[ui] Increased homepage top padding from 150px to 186px to give the floating header more breathing room
- —[ui] Added social links section to about page with icon badges, handle labels, and arrow-out affordance
- —[ui] Added info grid to about page (location, experience, current project) matching homepage bento pattern
- —[ui] Added Writing section stub to about page — links to first post, remaining entries show "Coming soon"
Design note
The identity shift from "Design Engineer" to "Product Designer × Builder" was intentional — the portfolio needed copy that reflects the actual shape of the work (designing and building with AI) rather than a job title that implies backend engineering. The bento stats grid was chosen over flat info rows because it lets the page communicate availability, experience, and current focus at a glance without needing the visitor to scroll into the bio.
Glassmorphism header, animated signature, and homepage polish
v1.0.3- —session: glassmorphism header, animated signature, homepage + changelog polish
Headshot hover, About & Contact pages, homepage depth
v1.0.2- —Built /about page with personal story, 4 How I Work principles, Toolkit grid, Experience timeline, and Currently section
- —Built /contact page with availability badges, service cards, contact form with success state, and FAQ
- —Added avatar hover interaction on hero — cursor icon fades out as headshot photo fades and scales in
- —Extracted CursorIconSkeuo and CursorAvatar to shared component; added iconOffset prop for per-instance optical centering
- —Added "Now" section to homepage (building/learning/reading/thinking rows)
- —Added "Writing" section to homepage with 3 article teasers ready for real content
- —Simplified header: removed ⌘K search, nav text-xs for better pill proportion, theme toggle stays right
Design note
The CursorAvatar iconOffset prop solves a recurring problem — optical centering differs by size. The hero needs 2px, the 28px header slot only needs 1px. Making it a prop keeps the component honest rather than hard-coding a compromise.
Changelog page, floating header, and build pipeline
v1.0.1- —Changelog page built at /changelog — server component reads index.json, client component handles filtering and staggered animations
- —Every changelog entry card gets a permanent CardFooter strip for version, date, tags, and source metadata
- —Changelog build pipeline wired up — prebuild hook runs changelog:build before every next build automatically
- —Draft script updated to auto-generate entry titles from commit messages
- —CLAUDE.md created — documents design system tokens, card patterns, and hands-off changelog workflow
- —Header converted to floating pill with borderRadius 300px, surface fill, and double-ring outline
- —Scroll-triggered avatar added to header — slides in after 20px scroll, links back to home on click
- —Fixed Framer Motion v12 type conflicts in Button, page.tsx, and design-system page
Design note
The header was redesigned from a full-width sticky bar to a floating pill. Centering it with the same max-w-[640px] mx-auto px-6 wrapper as the page content — rather than a flex justify-center approach — is what keeps it from extending beyond the content column on wide screens. The double-ring treatment (border-strong + outline at outlineOffset 2px) matches the IconBadge chrome, making the header feel like a first-class design system element.
Initial portfolio foundation
v1.0.0- —Next.js + TypeScript project scaffolded with App Router
- —Design system page created at /design-system
- —Core UI component library built: Avatar, Badge, Button, Card, CodeBlock, IconBadge, Input, Separator, Tag, ThemeToggle, Toggle
- —Header component implemented with sticky nav, ⌘K trigger, and theme toggle
- —Global styles, design tokens, and layout established — light and dark mode
- —Homepage built with hero, featured projects, newsletter, and footer sections
- —Changelog infrastructure scaffolded — platform + per-project surfaces