No description
  • Astro 48.4%
  • JavaScript 25.8%
  • CSS 8.6%
  • TypeScript 7.7%
  • Shell 3.6%
  • Other 5.9%
Find a file
Prashanth Bairu 5584c19fad trigger ci
2026-08-23 23:58:33 +05:30
deploy/bare-metal pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
public pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
scripts pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
src pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
.dockerignore pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
.env.example pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
.gitignore pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
.woodpecker.yaml exposed secrets in comments (#1) 2026-08-23 17:59:08 +00:00
astro.config.mjs pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
compose.yaml pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
DEPLOY.md pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
Dockerfile pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
Makefile pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
package-lock.json pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
package.json pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
README.md pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
SECURITY.md pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30
tsconfig.json pbdev: static site, container stack, CI pipeline 2026-08-23 23:12:46 +05:30

pbminimal

A minimal, dark-first personal site for Prashanth Bairu — Astro, no framework, one page: hero → timeline → about → skills → contact.

Built in the spirit of a clean engineering portfolio, but the markup, CSS and the animated P mark are original, and every word of content is Prashanth's own.

Run

npm install
npm run dev        # http://localhost:4321
npm run build      # static output in dist/
npm run preview

npm run security   # gate a deploy: asserts zero off-origin requests, live CSP
npm run csp        # regenerate the CSP hashes after changing an inline script

DEPLOY.md is the runbook: container stack in /opt/stacks/pbdev, Woodpecker pipeline, Forgejo registry, and the redeploy loop. SECURITY.md covers the security posture and the limits of "nothing traceable". Read both before the first deploy — in particular the note that CSP script hashes go stale whenever an inline script changes.

Output is fully static and ships as a container: a Node stage builds it, a Caddy stage serves it, and the runtime image holds no Node, no npm, and no source. make run builds and runs it locally on :8080.

Where things live

Path What
src/data/profile.ts All content. Name, links, timeline entries, about paragraphs, skill groups. Edit here first.
src/styles/global.css Design tokens (dark + light), layout, shared chip/section styles.
src/layouts/Base.astro <head>, meta/OG tags, pre-paint theme script, reveal + scroll-spy observers.
src/components/Logo.astro The animated P mark.
src/components/Nav.astro Sticky nav, scroll-spy underline, theme toggle, reading-progress bar.
src/components/Hero.astro Name, lede, facts row, large P.
src/components/Timeline.astro Career as a vertical spine that fills as you scroll.
src/components/About.astro Three paragraphs + a facts sidebar.
src/components/Skills.astro Six separated cards, each with a blurb and tag chips.
src/components/Contact.astro Email / LinkedIn / GitHub rows.
public/favicon.svg Static version of the P mark.
public/fonts/ Self-hosted Inter + JetBrains Mono (SIL OFL, licences included).
Dockerfile Two-stage build; generates the runtime Caddyfile from the dist it bakes in.
compose.yaml, .env.example The stack for /opt/stacks/pbdev. Real .env never leaves the server.
.woodpecker.yaml CI: verify, publish to the registry, deploy over SSH. All values from secrets.
Makefile make run / make check / make image for local work.
deploy/bare-metal/ The non-container alternative: nginx vhost, Caddyfile, rsync script.
DEPLOY.md, SECURITY.md Deploy runbook; security posture and its limits.
scripts/csp.mjs Regenerates the CSP script hashes from the build.
scripts/security-check.mjs Deploy gate: asserts zero off-origin requests.
scripts/verify.mjs Overflow / console-error sweep at three viewports, both themes.

The P mark

Logo.astro draws a geometric P from three stroked paths — stem, bowl, base. Each has pathLength="1", so a single stroke-dashoffset: 1 → 0 keyframe draws them in on load, staggered 50 / 250 / 500 ms.

On hover or keyboard focus of the enclosing link, the three parts translate along their own axes into an exploded view and a pin dot appears in the counter. Add the class pmark-burst to any ancestor to trigger the same state without a pointer.

Everything is disabled under prefers-reduced-motion.

Theming

Every colour is an RGB triple custom property in global.css, so :root and :root[data-theme='light'] swap the whole palette in one place. An inline script in Base.astro applies the stored choice before first paint (no flash); the site follows the OS until the visitor clicks the toggle, then remembers.

Retune a colour by changing the triple, not the call sites. The accent is --accent: 255 106 61 (dark) / 199 63 18 (light) — the light value is darkened so it clears 4.5:1 on the light background.

Accessibility

  • Skip link, visible focus rings on everything interactive.
  • Scroll-spy sets aria-current on the active nav link.
  • Sections carry scroll-margin-top so anchors don't land under the sticky nav.
  • All motion — draw-in, reveal, spine fill, explode — respects prefers-reduced-motion.
  • Verified: no horizontal overflow at 1440 / 834 / 390, no page errors, both themes.

Notes

  • profile.github is empty on purpose — set it and the GitHub row appears in Contact and nowhere else needs touching. Leave it empty and the row is omitted rather than linking somewhere dead.
  • Content is deliberately free of employer project detail, client names, and metrics. Keep it that way when editing profile.ts.
  • Timezone is deliberately absent. It appeared twice — a live IST clock in the hero and an Asia/Kolkata row in the About sidebar that only restated Location — and both were cut.
  • Skill groups carry no 01-06 codes. Skills aren't a sequence the way the timeline is, so numbering them would encode nothing. The section eyebrows (01 / Timeline … 04 / Contact) stay, because page sections genuinely are ordered.
  • External links open in a new tab. Contact.astro sets target="_blank" and rel="me noopener noreferrer" in markup for any http(s) href, so it works with JS off; mailto: and in-page anchors are deliberately excluded (a mailto: with target="_blank" leaves a dead tab behind). Each one also carries a visually-hidden "(opens in a new tab)" for screen readers. Base.astro has a small runtime pass that applies the same attributes to any off-origin link added later that forgot them — belt and braces, not the mechanism.
  • Fonts are self-hosted from public/fonts/ — latin-subset variable WOFF2, declared with @font-face at the top of global.css. The page makes no off-origin requests at all, which is asserted by npm run security. Don't reintroduce a <link> to a font CDN; it would hand every visitor's IP to a third party on page load.
  • astro.config.mjs sets site: 'https://me.prashanthbairu.dev'. It is baked into the build as the canonical link and the Open Graph URLs, so change it there and rebuild before deploying anywhere else.