Skip to content

Latest commit

 

History

History
503 lines (461 loc) · 16.7 KB

File metadata and controls

503 lines (461 loc) · 16.7 KB
layout page
title Timeline
permalink /timeline/
description PhD journey & life milestones
header-img img/bg-pingping2.jpg
<style> :root { --bg: #ffffff; --card: #ffffff; --text: #1a1a1a; --muted: #6b7280; --line: #e5e7eb; --accent: #2563eb; /* link/active */ --chip: #f3f4f6; --shadow: 0 10px 20px rgba(0,0,0,0.06), 0 2px 6px rgba(0,0,0,0.06); --radius: 16px; } /* Page shell */ .tl-wrap { max-width: 980px; margin: 0 auto; padding: 24px 18px 60px; color: var(--text); background: var(--bg); } .tl-header { display: grid; gap: 14px; margin-bottom: 18px; } .tl-title { font-size: clamp(28px, 2.6vw, 40px); font-weight: 800; letter-spacing: -0.01em; } .tl-sub { color: var(--muted); max-width: 70ch; } /* Controls */ .tl-controls { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-top: 6px; } .tl-search { flex: 1 1 280px; } .tl-search input { width: 100%; padding: 10px 12px; border: 1px solid var(--line); border-radius: 12px; outline: none; } .tl-chips { display: flex; flex-wrap: wrap; gap: 8px; } .chip { padding: 8px 12px; border-radius: 999px; background: var(--chip); border: 1px solid var(--line); font-size: 14px; cursor: pointer; user-select: none; transition: background 0.2s, color 0.2s, border 0.2s; } .chip.active { background: rgba(37,99,235,0.08); color: var(--accent); border-color: rgba(37,99,235,0.35); } /* Timeline */ .timeline { position: relative; margin-top: 24px; } .year-group { margin: 32px 0 10px; font-weight: 800; font-size: 22px; } .line { position: absolute; left: 26px; top: 0; bottom: 0; width: 2px; background: linear-gradient(180deg, var(--line), #fff); } .item { display: grid; grid-template-columns: 52px 1fr; gap: 14px; margin: 18px 0; } .dot { position: relative; width: 12px; height: 12px; border-radius: 50%; background: var(--accent); margin-top: 10px; box-shadow: 0 0 0 4px rgba(37,99,235,0.15); } .date { color: var(--muted); font-size: 13px; margin-top: 4px; } .card { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); box-shadow: var(--shadow); padding: 16px; display: grid; gap: 10px; } .card h3 { margin: 0; font-size: 18px; } .meta { color: var(--muted); font-size: 13px; display: flex; gap: 12px; flex-wrap: wrap; } .desc { line-height: 1.6; } .thumb { width: 100%; aspect-ratio: 16/9; object-fit: cover; border-radius: 12px; border: 1px solid var(--line); cursor: zoom-in; } .badge { font-size: 12px; padding: 6px 9px; background: var(--chip); border: 1px solid var(--line); border-radius: 999px; } .badges { display: flex; flex-wrap: wrap; gap: 6px; } .empty { text-align: center; color: var(--muted); padding: 28px; border: 1px dashed var(--line); border-radius: 12px; } /* Lightbox */ .lightbox { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: none; align-items: center; justify-content: center; padding: 20px; } .lightbox img { max-width: 95vw; max-height: 85vh; border-radius: 12px; border: 2px solid #fff; } @media (max-width: 560px) { .line { left: 22px; } .item { grid-template-columns: 44px 1fr; } } /* Clickable title styled like a link/button */ .link-title { all: unset; cursor: pointer; color: var(--accent); font-weight: 700; line-height: 1.2; } .link-title:hover { text-decoration: underline; } /* Small popup window */ .popup-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.35); display: none; /* toggled to flex in JS */ align-items: center; justify-content: center; z-index: 1000; } .popup-card { width: min(640px, 92vw); max-height: 80vh; overflow: auto; background: var(--card); border: 1px solid var(--line); border-radius: 16px; box-shadow: var(--shadow); padding: 18px 18px 12px; } .popup-card h3 { margin: 0 0 6px 0; font-size: 18px; } .popup-sub { color: var(--muted); font-size: 13px; margin-bottom: 10px; } .popup-body { line-height: 1.6; } .popup-body p { margin: 0 0 10px 0; } .popup-close { all: unset; float: right; font-size: 22px; line-height: 1; cursor: pointer; padding: 2px 8px; border-radius: 8px; } .popup-close:hover { background: var(--chip); } </style>
Personal Timeline
A visual log of my PhD journey and life milestones: research, publications, teaching, conferences, and the detours that shaped the path.
<div class="tl-controls">
  <div class="tl-search"><input id="searchInput" type="search" placeholder="Search (e.g., ‘paper’, ‘Trondheim’, ‘CCUS’)"></div>
  <div class="tl-chips" id="chipRow">
    <!-- Chips will be injected. Default set lives in JS below. -->
  </div>
</div>
Expanded timeline image
<script> // ===== 1) Editable data ===== // Replace with your real milestones. Keep ISO dates (YYYY-MM-DD) for proper sorting. const events = [ { date: "2022-08", title: "Started PhD & Journey in Vienna", location: "Vienna, AT", categories: ["PhD", "Journey"], description: "", image: "/img/timeline/Vienna2022.jpg", alt: "Vienna Donau" }, { date: "2022-11", title: "Visited DTU & Denmark finally", location: "Copenhagen, DK", categories: ["Travel", "Journey"], description: "", image: "/img/timeline/Vienna2022.jpg", alt: "Vienna Donau" }, { date: "2023-03", title: "Completed my first top-rope climbing lessons", location: "Vienna, AT", categories: ["Sports", "Personal","Journey"], description: "", image: "/img/timeline/Vienna2022.jpg", alt: "Vienna Donau" }, { date: "2023-05", title: "First GRC & GRS Conference about CCUS", location: "Les Diablerets, CH", categories: ["Conference", "Research","PhD","Journey"], description: "

Presented early CCS results and got feedback", image: "/img/post-bg-halting.jpg", alt: "Presenter at a conference podium" }, { date: "2023-07", title: "First Summer trips in Italy and Basque", location: "Italy", categories: ["Travel","Personal","Journey"], description: "", image: "/img/post-bg-web.jpg", alt: "Trip in Italy" }, { date: "2023-10", title: "Autumn School about Brightway & LCA ", location: "Energy & AI (under review)", categories: ["PhD", "Studying","Research","Journey"], description: "", image: "/img/post-bg-2015.jpg", alt: "Working photo" }, { date: "2024-01", title: "Travel in China with my boyfriend", location: "Beijing, CN", categories: ["Travel","Personal", "Journey"], description: "", image: "/img/post-bg-alitrip.jpg", alt: "Selfie in Beijing" }, { date: "2024-04", title: "Completed review paper about prospective assessments", location: "doi", categories: ["Publications", "PhD","Research","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2024-06", title: "First presentation at ESCAPE34", location: "Florence, IT", categories: ["Publications", "PhD","Research","Conference","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2024-08", title: "Bornout and 'Lost'", location: "Vienna, AT", categories: ["Personal","PhD",,"Bornout","Journey"], details: "

A hard stretch: poor focus, low mood. What helped:", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2024-10", title: "Autumn School about Energy systems modelling", location: "Gothenburg, SE", categories: ["Studying","PhD","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2025-02", title: "Completed my skiing lessons at Alps", location: "Bad Gastein, AT", categories: ["Sports","Personal","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2025-04", title: "First Conference and Trip in California", location: "Palm Springs, CA", categories: ["PhD","Conference","Personal","Journey"], description: "", image: "/img/timeline/USA2025.jpg", alt: "arXiv logo on a screen" }, { date: "2025-08", title: "Summer trip in Italy and Rebuilding", location: "Sardinia, IT", categories: ["Personal","Journey"], description: "", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" }, { date: "2025-10", title: "JuliaCon Paris and trip", location: "Paris, FR", categories: ["Conference","Travel","Personal","Journey"], description: "xxx", image: "/img/post-bg-digital-native.jpg", alt: "arXiv logo on a screen" } ]; // Default categories available as filter chips. const defaultChips = ["All", "Journey", "PhD", "Research", "Publications", "Conference", "Studying", "Workshop", "Travel", "Sports", "Personal", "Burnout"]; // ===== 2) Utility functions ===== const qs = (sel, el = document) => el.querySelector(sel); const qsa = (sel, el = document) => Array.from(el.querySelectorAll(sel)); // Robust date parsing + YYYY-MM display function parseDateParts(iso) { // Supports YYYY, YYYY-M, YYYY-MM, YYYY-MM-D, YYYY-MM-DD const m = String(iso).trim().match(/^(\d{4})(?:-(\d{1,2})(?:-(\d{1,2}))?)?$/); if (!m) return { y: NaN, m: NaN, d: NaN }; return { y: +m[1], m: +(m[2] || 1), d: +(m[3] || 1) }; } function toDate(iso) { const { y, m, d } = parseDateParts(iso); return isNaN(y) ? new Date(iso) : new Date(y, (m || 1) - 1, d || 1); } const pad2 = (n) => String(n).padStart(2, "0"); function fmtYYYYMM(iso) { const { y, m } = parseDateParts(iso); if (isNaN(y)) return String(iso); return `${y}-${pad2(m || 1)}`; } const fmtDate = fmtYYYYMM; // show "YYYY-MM" const byDateDesc = (a, b) => toDate(b.date) - toDate(a.date); const getYear = (iso) => toDate(iso).getFullYear(); // Parse URL params for deep-linking const url = new URL(window.location); const initCat = url.searchParams.get('cat') || 'All'; const initQuery = url.searchParams.get('q') || ''; // ===== 3) Render chips ===== const chipRow = qs('#chipRow'); let activeCat = initCat; defaultChips.forEach(cat => { const b = document.createElement('button'); b.className = 'chip' + (cat === activeCat ? ' active' : ''); b.textContent = cat; b.setAttribute('data-cat', cat); b.addEventListener('click', () => { activeCat = cat; qsa('.chip', chipRow).forEach(c => c.classList.remove('active')); b.classList.add('active'); render(); const p = new URLSearchParams(url.search); p.set('cat', activeCat); history.replaceState({}, '', `${url.pathname}?${p.toString()}`); }); chipRow.appendChild(b); }); // ===== 4) Search box ===== const searchInput = qs('#searchInput'); searchInput.value = initQuery; searchInput.addEventListener('input', () => { const p = new URLSearchParams(url.search); p.set('q', searchInput.value); history.replaceState({}, '', `${url.pathname}?${p.toString()}`); render(); }); // ===== 5) Lightbox ===== const lb = qs('#lightbox'); lb.addEventListener('click', () => { lb.style.display = 'none'; lb.setAttribute('aria-hidden', 'true'); }); function openLightbox(src, alt) { const img = lb.querySelector('img'); img.src = src; img.alt = alt || ''; lb.style.display = 'flex'; lb.setAttribute('aria-hidden', 'false'); }

×

// ===== 6) Main render ===== function openPopup(ev) { const pop = qs('#popup'); qs('#popupTitle').textContent = ev.title || ''; const subBits = [fmtDate(ev.date)]; if (ev.location) subBits.push(ev.location); qs('#popupSub').textContent = subBits.join(' · '); // Safe because it's your own content; allows simple markup qs('#popupBody').innerHTML = ev.details || (ev.description ? `

${ev.description}

` : '

No extra details.

'); pop.style.display = 'flex'; pop.setAttribute('aria-hidden', 'false'); } function closePopup() { const pop = qs('#popup'); pop.style.display = 'none'; pop.setAttribute('aria-hidden', 'true'); } // Close interactions qs('#popup').addEventListener('click', (e) => { // click on dimmed overlay closes; clicks inside card do not if (e.target.id === 'popup') closePopup(); }); qs('#popupClose').addEventListener('click', closePopup); document.addEventListener('keydown', (e) => { if (e.key === 'Escape') closePopup(); }); function render() { const root = qs('#timeline'); root.innerHTML = ''; const term = searchInput.value.trim().toLowerCase(); const filtered = events .slice() .sort(byDateDesc) .filter(ev => { const matchCat = (activeCat === 'All') || (ev.categories || []).includes(activeCat); if (!matchCat) return false; if (!term) return true; const hay = [ev.title, ev.location, ev.description, ...(ev.categories||[])].join(' ').toLowerCase(); return hay.includes(term); }); if (!filtered.length) { const empty = document.createElement('div'); empty.className = 'empty'; empty.textContent = 'No items match your filters. Try another category or search term.'; root.appendChild(empty); return; } let currentYear = null; filtered.forEach(ev => { const y = getYear(ev.date); if (y !== currentYear) { currentYear = y; const yg = document.createElement('div'); yg.className = 'year-group'; yg.textContent = y; root.appendChild(yg); } const item = document.createElement('div'); item.className = 'item'; const colL = document.createElement('div'); const dot = document.createElement('div'); dot.className = 'dot'; const date = document.createElement('div'); date.className = 'date'; date.textContent = fmtDate(ev.date); colL.appendChild(dot); colL.appendChild(date); const colR = document.createElement('div'); const card = document.createElement('div'); card.className = 'card'; const h3 = document.createElement('h3'); const tbtn = document.createElement('button'); tbtn.className = 'link-title'; tbtn.type = 'button'; tbtn.textContent = ev.title; tbtn.addEventListener('click', () => openPopup(ev)); h3.appendChild(tbtn); card.appendChild(h3); const meta = document.createElement('div'); meta.className = 'meta'; if (ev.location) { const loc = document.createElement('div'); loc.textContent = '📍 ' + ev.location; meta.appendChild(loc); } if (ev.categories && ev.categories.length) { const cats = document.createElement('div'); cats.className = 'badges'; ev.categories.forEach(c => { const b = document.createElement('span'); b.className = 'badge'; b.textContent = c; cats.appendChild(b); }); meta.appendChild(cats); } card.appendChild(meta); if (ev.image) { const img = document.createElement('img'); img.className = 'thumb'; img.src = ev.image; img.alt = ev.alt || ''; img.addEventListener('click', () => openLightbox(ev.image, ev.alt)); card.appendChild(img); } if (ev.description) { const p = document.createElement('div'); p.className = 'desc'; p.textContent = ev.description; card.appendChild(p); } colR.appendChild(card); item.appendChild(colL); item.appendChild(colR); root.appendChild(item); }); } // Initial render render(); </script>