commit e4cd0735ef3aa29b7c68640f40b3b585946a2813 Author: dheerajgajula02 Date: Mon Nov 17 23:13:11 2025 -0700 creating index for portfolio sites diff --git a/README.md b/README.md new file mode 100644 index 0000000..7a38a31 --- /dev/null +++ b/README.md @@ -0,0 +1,55 @@ +# Portfolio Index + +A minimal, elegant landing page that lists portfolio subdomains in a tidy table: link, IPv4/IPv6 footprint, and high-level remarks. + +## Structure + +``` +index.html # Main entry point +assets/ + css/styles.css # Visual theme + js/data.js # Source of truth for site entries + js/main.js # Renders the table from data.js +``` + +## Customize the directory + +1. Open `assets/js/data.js`. +2. Append or edit objects in the `sitesDirectory` array using the shape: + +```js +{ + label: "status", + url: "https://status.example.com", + ipv4: ["203.0.113.5"], + ipv6: ["2001:db8::5"], + remarks: "Heartbeat dashboard and uptime feed.", +} +``` + +3. Refresh `index.html` in your browser to see the update. + +### Notes section + +Below the table you will find a "Notes & highlights" card. Swap the placeholder +paragraphs and bullet list in `index.html` with whatever narrative you want to +share (launch recaps, upcoming work, etc.). + +### Lab photo + +Add or replace `server.jpg` in the project root to refresh the showcase image at +the bottom of the page. Update the caption text in `index.html` if you want to +describe different hardware or capture dates. Use the built-in “Photo size” +controls on the page (Compact / Standard / Full) to preview different scaling +without editing CSS. + +## Preview locally + +You can simply open `index.html` in any modern browser, or serve the folder with a lightweight HTTP server: + +```bash +cd /home/dheeraj/Desktop/index +python -m http.server 8000 +``` + +Then visit . diff --git a/assets/css/styles.css b/assets/css/styles.css new file mode 100644 index 0000000..17536a2 --- /dev/null +++ b/assets/css/styles.css @@ -0,0 +1,348 @@ +:root { + color-scheme: light dark; + --bg: #0f1117; + --card: rgba(255, 255, 255, 0.03); + --card-border: rgba(255, 255, 255, 0.08); + --text: #f5f7fa; + --muted: rgba(245, 247, 250, 0.7); + --accent: #7dd3fc; + --accent-strong: #0ea5e9; + --ipv4: #f97316; + --ipv6: #22c55e; + font-size: 16px; +} + +* { + box-sizing: border-box; + margin: 0; + padding: 0; +} + +body { + font-family: "Space Grotesk", system-ui, -apple-system, BlinkMacSystemFont, + "Segoe UI", sans-serif; + background: radial-gradient(circle at top left, #1f2937, #0f172a 50%, #020617); + color: var(--text); + min-height: 100vh; + padding: clamp(1.5rem, 3vw, 3rem); +} + +.page-shell { + max-width: 1100px; + margin: 0 auto; + display: flex; + flex-direction: column; + gap: 2.5rem; +} + +.hero { + display: flex; + flex-wrap: wrap; + justify-content: space-between; + gap: 1.5rem; + border: 1px solid var(--card-border); + background: linear-gradient(135deg, rgba(255, 255, 255, 0.08), transparent); + border-radius: 24px; + padding: clamp(1.5rem, 4vw, 2.75rem); +} + +.hero h1 { + font-size: clamp(2.2rem, 6vw, 3.1rem); + letter-spacing: -0.02em; +} + +.hero .eyebrow { + text-transform: uppercase; + letter-spacing: 0.2em; + font-size: 0.75rem; + color: var(--muted); + margin-bottom: 0.5rem; +} + +.hero .lede { + margin-top: 0.75rem; + max-width: 550px; + color: var(--muted); + line-height: 1.6; +} + +.hero .tagline { + align-self: flex-end; + font-size: 0.95rem; + color: var(--muted); +} + +.table-card { + background: var(--card); + border: 1px solid var(--card-border); + border-radius: 24px; + padding: clamp(1rem, 3vw, 2rem); + backdrop-filter: blur(12px); + box-shadow: 0 30px 60px rgba(0, 0, 0, 0.35); +} + +.notes-card { + background: rgba(15, 23, 42, 0.65); + border: 1px solid rgba(125, 211, 252, 0.15); + border-radius: 24px; + padding: clamp(1.25rem, 3vw, 2.5rem); + box-shadow: 0 20px 45px rgba(0, 0, 0, 0.35); + display: flex; + flex-direction: column; + gap: 1.5rem; + line-height: 1.7; +} + +.notes-card__header { + display: flex; + flex-direction: column; + gap: 0.35rem; +} + +.notes-card__header h2 { + font-size: 1.25rem; +} + +.notes-card__hint { + color: var(--muted); + font-size: 0.95rem; +} + +.notes-card__body ul { + margin-top: 0.5rem; + padding-left: 1.2rem; + color: var(--muted); + display: flex; + flex-direction: column; + gap: 0.4rem; +} + +.notes-card__body strong { + color: var(--text); +} + +.visual-card { + border: 1px solid rgba(255, 255, 255, 0.08); + border-radius: 28px; + padding: clamp(1.5rem, 3vw, 2.75rem); + background: linear-gradient(120deg, rgba(15, 23, 42, 0.9), rgba(8, 145, 178, 0.15)); + box-shadow: 0 30px 60px rgba(0, 0, 0, 0.45); +} + +.visual-card__content { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); + gap: 2rem; + align-items: center; +} + +.visual-card__lede { + color: var(--muted); + margin-top: 0.75rem; + line-height: 1.7; +} + +.visual-card__controls { + margin-top: 1.25rem; + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + align-items: center; + font-size: 0.9rem; + color: var(--muted); +} + +.visual-card__control-buttons { + display: inline-flex; + gap: 0.5rem; +} + +.photo-size__button { + border: 1px solid rgba(255, 255, 255, 0.2); + background: rgba(255, 255, 255, 0.05); + color: var(--text); + padding: 0.35rem 0.9rem; + border-radius: 999px; + font-size: 0.85rem; + cursor: pointer; + transition: background 0.2s ease, color 0.2s ease, border-color 0.2s ease; +} + +.photo-size__button:hover { + border-color: var(--accent); +} + +.photo-size__button.is-active { + background: rgba(125, 211, 252, 0.15); + border-color: var(--accent); + color: var(--accent); +} + +.visual-card__media { + display: flex; + justify-content: center; +} + +.visual-card__figure { + margin: 0; + border-radius: 20px; + overflow: hidden; + border: 1px solid rgba(255, 255, 255, 0.15); + background: rgba(0, 0, 0, 0.25); + max-width: 100%; + transition: max-width 0.3s ease, max-height 0.3s ease; +} + +.visual-card__figure img { + display: block; + width: 100%; + height: auto; + object-fit: cover; + max-height: 420px; +} + +.visual-card__figure figcaption { + font-size: 0.85rem; + padding: 0.6rem 0.9rem; + color: var(--muted); + background: rgba(2, 6, 23, 0.65); +} + +.visual-card[data-photo-size="compact"] .visual-card__figure { + max-width: 280px; +} + +.visual-card[data-photo-size="compact"] .visual-card__figure img { + max-height: 240px; +} + +.visual-card[data-photo-size="standard"] .visual-card__figure { + max-width: 420px; +} + +.visual-card[data-photo-size="standard"] .visual-card__figure img { + max-height: 320px; +} + +.visual-card[data-photo-size="full"] .visual-card__figure { + max-width: 100%; +} + +.visual-card[data-photo-size="full"] .visual-card__figure img { + max-height: 520px; +} + +.table-card__header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 1rem; +} + +.table-card__header h2 { + font-size: 1.3rem; +} + +.table-wrapper { + overflow-x: auto; + border-radius: 16px; + border: 1px solid var(--card-border); +} + +table { + width: 100%; + border-collapse: collapse; + min-width: 600px; +} + +thead { + background: rgba(255, 255, 255, 0.04); +} + +thead th { + text-align: left; + padding: 0.85rem 1.25rem; + font-size: 0.9rem; + color: var(--muted); +} + +tbody tr { + border-bottom: 1px solid rgba(255, 255, 255, 0.06); + transition: background 200ms ease, transform 200ms ease; +} + +tbody tr:hover { + background: rgba(125, 211, 252, 0.08); + transform: translateX(4px); +} + +td { + padding: 1rem 1.25rem; + vertical-align: top; + line-height: 1.5; +} + +td:first-child a { + color: var(--accent); + font-weight: 500; + text-decoration: none; +} + +td:first-child a:hover { + color: var(--accent-strong); +} + +.ip-list { + display: flex; + flex-wrap: wrap; + gap: 0.35rem; +} + +.pill { + display: inline-flex; + align-items: center; + gap: 0.25rem; + border-radius: 999px; + padding: 0.3rem 0.75rem; + font-size: 0.78rem; + font-weight: 500; + background: rgba(255, 255, 255, 0.08); +} + +.pill--ipv4 { + color: var(--ipv4); +} + +.pill--ipv6 { + color: var(--ipv6); +} + +.remarks { + color: var(--muted); +} + +.footnote { + margin-top: 1rem; + font-size: 0.85rem; + color: var(--muted); +} + +.noscript { + text-align: center; + margin-top: 1rem; + color: #ef4444; +} + +@media (max-width: 640px) { + body { + padding: 1.5rem 1rem; + } + + .hero { + flex-direction: column; + } + + table { + min-width: 100%; + } +} diff --git a/assets/js/data.js b/assets/js/data.js new file mode 100644 index 0000000..d36da12 --- /dev/null +++ b/assets/js/data.js @@ -0,0 +1,40 @@ +const sitesDirectory = [ + { + label: "portfolio beta", + url: "https://beta.portfolio.dheerajg.me", + ipv6: ["IPV6 only site"], + remarks: "I'm not a frontend developer but I sometimes use AI for frontend tasks and I dont want to break whats working hence this.", + }, + { + label: "portfolio", + url: "https://portfolio.dheerajg.me", + ipv6: ["IPV6 only site"], + remarks: "This is the stable version of my portfolio site.", + }, + { + label: "git", + url: "https://git.dheerajg.me", + ipv6: ["IPV6 only site"], + remarks: "well I don't know what to write here apart from its my own git hosting service.", + }, + { + label: "resume", + url: "https://resume.dheerajg.me", + ipv4: ["This runs dual stack"], + remarks: "turns out lot of folks still run just IPV4, so made cloudflare tunnel.", + }, + { + label: "project documentation", + url: "https://projectdoc.dheerajg.me", + ipv6: ["IPV6 only site"], + remarks: "This is the documentation for my projects, I try to keep it updated I'll try to put some in public .", + }, + { + label: "portfolio minimal", + url: "https://v6.min.portfolio.dheerajg.me", + ipv6: ["IPV6 only site"], + remarks: "I have an 15 year old device that isnt able to render modern website so I decided to make a minimal version of my portfolio", + } +]; + +window.sitesDirectory = sitesDirectory; diff --git a/assets/js/main.js b/assets/js/main.js new file mode 100644 index 0000000..6f71b88 --- /dev/null +++ b/assets/js/main.js @@ -0,0 +1,88 @@ +(() => { + const tbody = document.getElementById("sitesBody"); + const lastUpdatedEl = document.getElementById("lastUpdated"); + + const formatTimestamp = () => { + const formatter = new Intl.DateTimeFormat("en", { + dateStyle: "medium", + timeStyle: "short", + }); + lastUpdatedEl.textContent = formatter.format(new Date()); + }; + + const renderRows = () => { + if (!Array.isArray(window.sitesDirectory) || !window.sitesDirectory.length) { + tbody.innerHTML = ` + + No entries yet. Update assets/js/data.js to get started. + `; + return; + } + + const rows = window.sitesDirectory + .map((site) => { + const { + label = "Site", + url = "#", + ipv4 = [], + ipv6 = [], + remarks = "", + } = site; + + const ipv4Badges = ipv4 + .map((ip) => `${ip}`) + .join(""); + const ipv6Badges = ipv6 + .map((ip) => `${ip}`) + .join(""); + const ipMarkup = + ipv4.length || ipv6.length + ? `
${ipv4Badges}${ipv6Badges}
` + : ``; + + return ` + + + + ${label} + +
${url.replace(/^https?:\/\//, "")}
+ + ${ipMarkup} + ${remarks} + `; + }) + .join(""); + + tbody.innerHTML = rows; + }; + + const initPhotoSizeControls = () => { + const card = document.querySelector(".visual-card"); + if (!card) return; + + const buttons = card.querySelectorAll("[data-photo-size-option]"); + if (!buttons.length) return; + + const setSize = (size) => { + card.setAttribute("data-photo-size", size); + buttons.forEach((btn) => { + btn.classList.toggle("is-active", btn.dataset.photoSizeOption === size); + }); + }; + + const initial = card.getAttribute("data-photo-size") || "standard"; + setSize(initial); + + buttons.forEach((button) => { + button.addEventListener("click", () => { + const size = button.dataset.photoSizeOption; + setSize(size); + }); + }); + }; + + formatTimestamp(); + renderRows(); + initPhotoSizeControls(); +})(); diff --git a/index.html b/index.html new file mode 100644 index 0000000..63ddb18 --- /dev/null +++ b/index.html @@ -0,0 +1,121 @@ + + + + + + + Portfolio Index | Subdomain Directory + + + + + + +
+
+
+

Portfolio Index

+

Subdomains at a glance

+

+ Bookmark-worthy overview of all live surfaces. Click straight into each + experience, spot their IP footprints, and note what makes them special. +

+
+
Last refreshed
+
+ +
+
+

Sites directory

+
+ IPv4 + IPv6 +
+
+
+ + + + + + + + + +
LinkIP footprintRemarks
+
+
+ +
+
+

Notes

+ +
+
+
    +
  • I like serving and hosting my own services, I have multiple backups running (always gotta take + backups for backups )
  • +
  • some of them obviously cant show here for security reasons
  • +
  • I run dynamic DNS as my ISP keeps changing my IP addresses
  • +
  • Most of my services are IPV6 only as my ISP provides IPV6 natively but not IPV4
  • +
  • I run Wireguard VPN on top of that so i can access my servers from anywhere in the world
  • +
  • I use Cloudflare as my DNS provider and also as a CDN for some of my sites
  • +
  • I use Let's Encrypt for SSL certificates
  • +
  • If your ISP still doenst support IPV6 then you should seriously consider switching ISPs
  • +
+
+
+ +
+
+
+

Lab hardware

+

Where the magic lives

+

+

  • Snapshot from the homelab stack that keeps these subdomains humming.
  • +
  • total 4 servers , one huge black one , second for reverse proxying and wireguard VPN communication server, the intelNUC thats lying on top of switch
  • +
    + 1. Black one in the picture , main server. 10 years old got it from one of the labs at university of colorado boulder at salvage sale still running strong with 32GB RAM , 4TB + 2TB HDD and i7-3930K and GPU: NVIDIA GeForce GTX 970 || GPU: AMD ATI Radeon HD 8570 / R5 430 / R7 240/340 yes I added one more GPU because I can, bought from the same sale

    +
    + 2. Intel NUC that handles all the reverse proxying using NGINX and also the Wireguard VPN server , specs are 16GB RAM , 250 GB SSD and i5 7th mobileprocessor

    +
    + 3. A powerful server with RTX 3050 in india, where I used to learn machine learning tasks but no use now because latency is too high, have to find out a proper usecase apart from storing backups and running OLLAMA server

    +
    + 4. And a small VM I bought from a cheap VPS that I paid like 3 dollars a year just to do health checks for all my servers and sites I'm gonna build grafana dashboards for it as soon as possible +
    + +
    +
    +
    +
  • If you are interested in learning a bit about my VPN setup read this docs note that this is IPV6 WireGuard Docs
  • +

    +
    + Photo size: +
    + + + +
    +
    +
    +
    +
    + Homelab server rack +
    Captured on
    +
    +
    +
    +
    +
    + + + + + + + \ No newline at end of file diff --git a/server.jpg b/server.jpg new file mode 100644 index 0000000..a5c3fe5 Binary files /dev/null and b/server.jpg differ