Skip to content

Latest commit

 

History

4 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ram0verflow — portfolio

Static, dependency-free portfolio site. Terminal/CRT aesthetic, an interactive command interpreter, and — the important part — all copy lives in one JSON file you can edit from your phone.

No build step. No framework. No backend. No AWS.


The one thing to understand: content.json

Every word on this site comes from content.json. The page fetches it in the browser at runtime, not at build time:

fetch(`https://github.com/ghraw/ram0verflow/portfolio/main/content.json?t=${Date.now()}`)

Because the copy is never baked into the deployed bundle, editing content.json on github.com updates the live site on the next page load — no redeploy, no CI run, no waiting on Vercel.

To change anything on this site

  1. Open content.json on github.com (web editor, or the GitHub mobile app).
  2. Edit. Commit directly to main.
  3. Reload the site. Done — usually within seconds.

That is the entire CMS. There is no database and no admin panel, on purpose.

Why the ?t= matters

github.com/ghraw sends caching headers. Without the cache-busting timestamp you would commit a change, reload, see the old copy, and reasonably conclude the commit failed. Do not remove it.

If the fetch fails

The loader falls back to the content.json shipped alongside the bundle, so the site still renders if GitHub raw is unreachable or you're offline. If both fail, a visible error state appears with direct contact details — the page never renders blank.

Moving the repo? Change the two constants at the top of src/content-loader.js:

export const CONTENT_REPO   = 'ram0verflow/portfolio';
export const CONTENT_BRANCH = 'main';

Local development

There is no build. Serve the directory over HTTP — file:// will not work, because ES modules and fetch both require a real origin.

python3 -m http.server 4173

Then open http://localhost:4173. Locally the remote fetch 404s until the repo exists and falls back to your local content.json, which is what you want while editing.


Deploying

Any static host works. Vercel and Cloudflare Pages are both free for this.

  • Framework preset: None / Other
  • Build command: (none)
  • Output directory: / (repo root)

TLS is automatic and free on every one of these hosts, including on a custom domain — you never buy a certificate. They provision and renew Let's Encrypt certs for you.


Two views, one data source

The shell is the site — it's what loads by default and where the personality lives. The page view is the same content laid out conventionally, one click away via the toggle top-right (or the gui command, or ?view=doc).

The page view is not an apology for the terminal. It exists because:

  1. someone skimming on a phone between meetings will not type commands, and
  2. crawlers and screen readers get real semantic markup.

Both views render from the same content object, so they physically cannot disagree. The choice is remembered in localStorage.

On SEO, honestly: the real constraint is not the terminal, it's the runtime fetch. Because content.json is fetched in the browser, the copy is not in the HTML source — Googlebot executes JS and will index it, but it's weaker than static HTML. That is the price of editing the site from your phone without a redeploy, and it is worth it for a personal site. What offsets it: static <title>/<meta description>, JSON-LD Person structured data in the source, and a <noscript> block with the essentials.

Structure

index.html            page shell + critical CSS + JSON-LD
content.json          ALL copy — the CMS
src/
  main.js             entry point; fetch, then wire shell + page to the content
  content-loader.js   remote fetch, local fallback, validation
  terminal.js         the shell: parser, pipelines, completion, history, boot
  commands.js         ~45 commands with man pages
  vfs.js              virtual filesystem, generated from content.json
  view.js             tty/doc toggle, themes, matrix toggle
  sections.js         renders the page view
  banner.js           generates the ASCII banner from identity.handle
  contact.js          modal + Web3Forms submission
  effects.js          Matrix rain (throttled, pauses when tab hidden)
  sound.js            key click sounds + persisted mute
  styles.css          full stylesheet
assets/               keyboard.mp3, button.mp3, glitch.mp3

Notable choices

  • The filesystem is generated from the content. ls, cat, tree, find and grep walk a tree built from content.json, so the shell can never drift from the page view — both read the same source.
  • The ASCII banner is generated, not hardcoded. The old site had ram0verflow baked into a static art string, which silently made identity.handle un-editable. src/banner.js renders it from the handle.
  • Output colour uses a control-character sentinel, not markup. Commands emit �a / �g / �d prefixes; the renderer turns them into spans via textContent. No amount of content in content.json can inject HTML. Sentinels are stripped when output is piped.
  • Pipes are real. Commands return strings and receive the previous stage's output as stdin, which is what makes cat about.txt | grep -i bitcoin | wc -l work rather than being faked per-command.

Contact form

Submits to Web3Forms — one POST, no backend, delivers to email. The access key lives in content.json under links.web3formsKey and is public by design: it identifies the destination inbox, it does not authorise anything.

To point the form at a different inbox, get a new key at web3forms.com and change that one field.

Fallback if Web3Forms ever goes away: Formspree has the same integration shape (free tier, 50 submissions/month) — swap the endpoint and the payload key name in src/contact.js.


The shell

group commands
portfolio about experience projects skills achievements contact open gui
files ls cd pwd cat tree find stat file du
text grep wc head tail sort uniq rev echo
system whoami id uname hostname env which uptime df ps date history
fun neofetch figlet banner cowsay fortune sl matrix theme sudo vim
shell help man mute clear exit

Flags work (ls -la, grep -in, head -n 3, sort -r). So do pipes, quoting, ~, .., cd -, and $VAR expansion in echo.

Keys: Tab completes commands and paths (longest common prefix, then a menu), ↑/↓ walks history (persisted to localStorage), Ctrl-L clears, Ctrl-C abandons the line, Ctrl-U wipes it.

man <command> gives a real manual page for any of them.

Writes are real and ephemeral. touch, mkdir -p, rm -r, cp, mv, tee, sed and > / >> all work against an IndexedDB-backed scratch layer that survives a reload and is discarded when the tab closes. Run fs to see the model, fs reset to clear it.

apt install fetches real code. apt install cowsay dynamic-imports the actual npm package from a CDN and registers a command backed by it — not by a reimplementation. apt search lists the catalogue.

lsusb is real WebUSB, not a mock. It lists devices you have granted this page; usb attach opens the browser's own chooser. Chromium only.

For exactly how far each command goes toward real UNIX, what it's backed by, and which public project to swap in to go further, see SHELL.md.

date fetches the live Bitcoin block height from mempool.space — a free, unauthenticated public API.

theme amber|green|ice|mono recolours the phosphor; the choice persists.

On phones a row of tappable command chips sits under the terminal, because typing neofetch on glass is a chore.


Performance notes

The Matrix rain is the only continuous work on the page. It runs on requestAnimationFrame with a ~20fps budget rather than the old setInterval(…, 50), stops entirely when the tab is hidden, and uses larger glyphs (fewer columns) on small screens. It's disabled outright under prefers-reduced-motion, along with every other animation.

About

Shell-first portfolio for ramoverflow.dev — content.json edited on GitHub, live without a redeploy

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages