Skip to content

Latest commit

Β 

History

208 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

Bino Hlongwana | Portfolio

A portfolio built as one continuous scrolling journey β€” five destinations (Home, Experience, About, Certifications, Contact) connected by a morphing star map and a WebGL aurora, looping seamlessly back to the start.

β†’ binohlongwana.netlify.app

Live site Angular TypeScript WebGL CSS3

There is no router and there are no page loads. The whole site is a single scrolling loop whose position is a signal, read every frame by a single shared requestAnimationFrame pulse running outside Angular's zone. Everything below follows from that one decision.


Contents


πŸ“š Start Here: The Onboarding Doc

The best way to understand how everything works together is the interactive engineering onboarding guide β€” a self-contained HTML doc (no build step, no dependencies; just open docs/onboarding.html in any browser). It walks through:

  • Quick Start β€” prerequisites, install & run, script reference, deployment
  • Architecture β€” the star-map wayfinding concept, an interactive system diagram, the scroll loop that acts as the site's spine, signals and the 60fps hot path, and the theming model
  • Component Map β€” a directory tour, why there is no router, every component, core service, and data file
  • Modification Guide β€” step-by-step recipes for adding a project, updating certifications, tweaking the constellation, tuning the aurora, and adding a whole new destination

Supporting references:

  • CONTEXT.md β€” the design-language glossary
  • docs/adr β€” Architecture Decision Records for every significant design choice

πŸƒ Getting Started

Prerequisites

  • Node.js v18.19+
  • Angular CLI β€” npm install -g @angular/cli

Install and run

git clone https://github.com/Robotbino/PorfolioWebsite.git
cd PorfolioWebsite && npm install && npm start

Visit http://localhost:4200.

The repo also carries .claude/launch.json, which starts the same server on 4388 for the in-editor preview. Both are the same npm start.

Scripts

Script What it does
npm start Dev server with live reload on :4200
npm run build Production build into dist/professional-porfolio/browser, then generates the CSP into _headers
npm run watch Rebuilding development bundle, no server
npm test Unit tests in Karma + Jasmine (headless)
npm run lint ESLint over TypeScript and templates
npm run format Prettier over TypeScript and config

Tests

npm test

The pure logic lives in unit-tested modules with colocated .spec.ts files β€” scroll math, the constellation morph and its driver, the frame pulse, theme decisions, certifications math, destinations, the aurora palette, in-viewport observation, scroll lock, motion math, and the page-inert isolation behind both overlays. Anything that can be a pure function is one, precisely so it can be tested without a DOM.

Karma runs headless via karma.conf.js, so this works in CI and in a container.

No Chrome installed? Point Karma at another Chromium build β€” for example, on Windows:

CHROME_BIN="C:/Program Files (x86)/Microsoft/Edge/Application/msedge.exe" npm test

✨ Features

  • Looping Scroll β€” The page scrolls through all five sections and wraps invisibly back to the top via a cloned seam, so the journey never ends
  • Morphing Star Map β€” An SVG constellation interpolates between figures as you scroll, acting as wayfinding for where you are in the loop
  • WebGL Aurora β€” A GLSL simplex-noise shader (via OGL) renders a living aurora behind every section; coarse-pointer devices get an animated CSS-gradient fallback instead of a shader
  • Single Frame Loop β€” One shared requestAnimationFrame pulse runs outside Angular's zone and drives every animation, keeping change detection off the hot path
  • Horizontal Projects Showcase β€” The Experience section pins and scrolls its project cards sideways on the same rAF pulse, with keyboard framing and a progress indicator
  • Certification Spotlight β€” Each certification opens full-size in a focus-trapped overlay with a verify link; on fine pointers a floating preview tethers to the cursor
  • Marquee Email β€” The Contact finale features a large marquee email with one-click copy-to-clipboard, plus a colophon with a live SAST clock
  • Theme Toggle β€” Dark/light mode with system-preference detection, localStorage persistence, and a signals-based ThemeService the whole app reacts to
  • No Theme Flash β€” An inline pre-paint guard in index.html sets the theme class before Angular boots, mirroring theme.decision.ts
  • Accessible β€” Semantic HTML, ARIA labels, keyboard navigation, and full prefers-reduced-motion support (the morph and aurora settle down when asked)
  • Responsive β€” Optimized layouts for desktop, tablet, and mobile
  • CV Download β€” One click, straight to your downloads folder β€” recruiters, this one's for you ;)

πŸ› οΈ Tech Stack

Category Technologies
Framework Angular 21 (standalone components + signals)
Language TypeScript 5.6
Graphics OGL (WebGL2 shader), SVG morphing
Styling CSS3 (Custom Properties, Flexbox, Grid)
Typography Nohemi (display), Atkinson Hyperlegible (body/UI), Instrument Serif (accent) β€” all self-hosted
Icons Inline SVG (shared/icon)
Testing Jasmine + Karma
Hosting Netlify

Runtime dependencies are deliberately few: Angular, OGL and RxJS. No animation library, no GSAP, no UI kit, and no icon font β€” the motion is hand-rolled against the shared frame pulse, and the sixteen icons ship as path data rather than the 299 kB Font Awesome cost to draw them.


πŸ—οΈ How It Works

The scroll loop is the spine of the site. ScrollLoopService owns the reader's cycle position (0 = Home, 1 = Experience, … wrapping at the seam), with the arithmetic extracted into pure, unit-tested functions in scroll-loop.math.ts. The app shell feeds it raw scroll offsets; everything else β€” the constellation morph, the loop-aware nav muting, the projects showcase β€” reads the position signal from the shared frame pulse without ever triggering change detection at 60fps.

Three rules keep that hot path honest:

  1. One pulse. FramePulseService owns the only requestAnimationFrame loop; components subscribe to it rather than starting their own.
  2. One observer seam. InViewportService owns viewport observation, so offscreen work pauses instead of burning frames.
  3. Pure math, testable. Anything that can be a function of numbers is one: scroll-loop.math.ts, motion.math.ts, certifications.math.ts, constellation-morph.ts, theme.decision.ts.

There is deliberately no Angular Router and no NgModule: the app bootstraps through bootstrapApplication, and the five destinations are defined once in src/app/destinations.ts and composed into a single looping page by the app shell. Adding a destination means adding an entry there β€” the nav, the constellation, and the loop arithmetic all derive from that list.

Every component is OnPush, which the lint config enforces: with one shared frame loop driving the animation, a default-strategy component would be re-checked on every event it never needs.


πŸ“ Project Structure

src/app/
β”œβ”€β”€ aurora/                    # WebGL aurora background (OGL + GLSL shader, coarse-pointer CSS fallback)
β”œβ”€β”€ constellation/             # Morphing star map (figures, morph driver, interpolation)
β”œβ”€β”€ core/                      # Services: frame pulse (shared rAF), theme, motion settings,
β”‚                              #   in-viewport, scroll lock, nav transitions, aurora palette
β”œβ”€β”€ landingpage/               # Home / hero section + CV download
β”œβ”€β”€ layout/site-nav/           # Navigation with loop-aware muting
β”œβ”€β”€ pages/
β”‚   β”œβ”€β”€ work/                  # Experience timeline + horizontal showcase (work-data.ts)
β”‚   β”œβ”€β”€ about/
β”‚   β”œβ”€β”€ certifications/        # Spotlight overlay (certifications-data.ts, certifications.math.ts)
β”‚   └── contact/               # Marquee email, channels, colophon with live SAST clock
β”œβ”€β”€ shared/
β”‚   β”œβ”€β”€ icon/                  # Inline SVG icon set (path data + <app-icon>)
β”‚   └── theme-toggle/
β”œβ”€β”€ destinations.ts            # Single source of truth for the five destinations
β”œβ”€β”€ scroll-loop.service.ts     # Scroll cycle state + seam wrap
β”œβ”€β”€ scroll-loop.math.ts        # Pure scroll math (unit-tested)
β”œβ”€β”€ motion.math.ts             # Frame-rate-independent smoothing (unit-tested)
└── scroll-reveal.directive.ts # Fade-in-on-scroll behavior
src/
β”œβ”€β”€ assets/                    # CV, portrait, project shots, certificate images, self-hosted fonts
β”œβ”€β”€ index.html                 # Font preloads, pre-paint theme guard, JSON-LD
└── styles.css                 # Design tokens, @font-face, global type scale
public/                        # robots.txt, sitemap, manifest, icons, share card
scripts/gen-headers.mjs        # Generates the CSP into the built _headers
.github/workflows/ci.yml       # Lint, format, test, build on every push
docs/
β”œβ”€β”€ onboarding.html            # ⭐ Interactive engineering onboarding guide β€” start here
└── adr/                       # Architecture Decision Records
CONTEXT.md                     # Design-language glossary

πŸ“ Architecture Decisions

Every significant design decision is recorded as an ADR β€” including the ones that were tried and rejected, because the trail is the point.

ADR Decision Status
0001 Full multipage routing, immersion-first accepted, superseded by 0003
0002 Star-map wayfinding as the unifying concept accepted, revised by 0003
0003 Looping-scroll immersion with real constellations accepted
0004 Seamless one-direction loop via a cloned Home buffer accepted
0005 Loop-aware (Home-anchored) navigation muting accepted
0006 Frosted-glass surface for content cards rejected
0007 Deepen the Cycle into ScrollLoopService accepted
0008 Projects showcase β€” progress, focus, keyboard framing accepted
0009 Inline SVG icon set instead of an icon font accepted
0010 Standalone components on Angular 21 accepted

πŸš€ Deployment

Live at binohlongwana.netlify.app, deployed to Netlify via netlify.toml:

  • Build: npm run build (ng build, then scripts/gen-headers.mjs)
  • Publish directory: dist/professional-porfolio/browser
  • SPA fallback: /* β†’ /index.html 200 (also mirrored in src/_redirects)
  • Caching and security headers live in netlify.toml; the Content-Security-Policy is generated into _headers after each build, because it carries the hash of the pre-paint theme guard in index.html

πŸ“Έ Screenshots

Click to expand

Dark Mode

Portfolio in dark mode

Light Mode

Portfolio in light mode


πŸ“¬ Contact

Bino Hlongwana :)

LinkedIn GitHub Email


πŸ“„ License

This project is open source and available under the MIT License.

About

My portfolio: one looping scroll over a WebGL aurora and a morphing star map. Angular 21 + Signals, no router, documented with ADRs and gated by CI.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages