Skip to content

Commit ad73a67

Browse files
authored
Add files via upload
1 parent 7341d72 commit ad73a67

5 files changed

Lines changed: 478 additions & 75 deletions

File tree

README.md

Lines changed: 32 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -1,88 +1,45 @@
1-
# Yawar Hussain — personal site
1+
# Yawar Hussain — personal website
22

3-
A small Python static-site generator, no framework. Same design as before
4-
(craftzdog-homepage-inspired), now split into real pages so adding a blog
5-
post is a one-file change.
3+
Plain HTML/CSS/JS, no build step, no framework. Design based on Takuya
4+
Matsuyama's [craftzdog-homepage](https://github.com/craftzdog/craftzdog-homepage)
5+
(MIT-licensed source) — the 3D voxel-dog mascot is CC BY-NC-ND on that repo
6+
("you can't reuse it on your website"), so it's not included here; the photo
7+
and footprint icon replace it.
68

7-
## Adding a blog post
8-
9-
1. Copy `content/posts/_template.md` to `content/posts/YYYY-MM-DD-your-slug.md`.
10-
2. Fill in `Title`, `Date`, `Summary`, and write the post body in Markdown below the blank line.
11-
3. `python build.py` (or just `git push` — see Deploying below).
12-
13-
That's it — no other file changes, no registering the post anywhere. The
14-
filename becomes the URL (`/blog/your-slug/`), and it's sorted onto the Blog
15-
page automatically by date.
16-
17-
**Adding pictures to a post:** drop the image into `images/` (e.g.
18-
`images/my-post-photo.jpg` — anything in there gets published as-is) and
19-
reference it in the post's Markdown as:
20-
21-
```markdown
22-
![Caption for the image](../../images/my-post-photo.jpg)
23-
```
24-
25-
The `../../` is because a post page lives two folders deep
26-
(`blog/<slug>/index.html`); it's the same for every post, regardless of
27-
slug. Standard Markdown image syntax, so normal image sizes/formats (jpg,
28-
png, gif, svg) all work.
29-
30-
## Editing everything else
31-
32-
- `data.py` — your CV content: employment, fieldwork, projects, editorial
33-
roles, education, conferences, publications. Plain Python lists/dicts.
34-
- `theme.py` — colours, fonts, CSS, icons, the page shell (nav/footer). You
35-
shouldn't need to touch this for content changes.
36-
- `build.py` — the page layouts themselves (what goes on Home/Works/Fieldwork/Publications).
37-
- `images/profile.jpg` — your photo.
38-
39-
## Running it locally
9+
## Folder structure
4010

4111
```
42-
pip install -r requirements.txt
43-
python build.py
12+
index.html the page itself (About / Works / Publications, one file,
13+
JS shows/hides the right section — no page reloads)
14+
assets/css/main.css all styling, incl. the light/dark theme toggle
15+
assets/js/main.js page behaviour: theme toggle, mobile menu, section routing
16+
images/profile.jpg your photo (About section)
4417
```
4518

46-
That's it — `dist/index.html` opens directly in a browser (double-click it,
47-
or `open dist/index.html`), no server needed. Every link and asset in the
48-
site is a relative path, so it works the same over `file://` as it does once
49-
deployed. (A local server also works if you prefer one: `python -m
50-
http.server -d dist 8000`.)
19+
## Editing content
5120

52-
`build.py` writes everything into `dist/` (git-ignored — it's generated, not
53-
source).
21+
Open `index.html` and search for the section you want:
22+
- `id="page-home"` — About page (intro line, bio timeline, interests, links)
23+
- `id="page-works"` — Works page (employment, projects, editorial, education, conferences)
24+
- `id="page-fieldwork"` — Fieldwork page (field deployments, grid of entries)
25+
- `id="page-blog"` — Blog page (empty state for now — add post cards here later)
26+
- `id="page-pubs"` — Publications page (grouped, collapsible by topic)
5427

55-
## Deploying on GitHub Pages
56-
57-
This repo includes `.github/workflows/deploy.yml`, which builds the site and
58-
publishes it automatically on every push to `main`. One-time setup:
28+
## Swapping the photo
5929

60-
1. Push this repo to GitHub — either as `<your-username>.github.io` (a user
61-
site at the domain root) or as any other repo name (a project site at
62-
`<username>.github.io/reponame/`). Both work with no configuration: every
63-
link and asset path in the site is relative, not root-absolute, so it
64-
doesn't care which path it's served from.
65-
2. Repo Settings → Pages → **Source: GitHub Actions**.
66-
3. Push to `main`. The Action builds with `python build.py` and deploys
67-
`dist/` — no need to commit generated HTML, and no Ruby/Jekyll involved
68-
(there's a `.nojekyll` file in the output so GitHub Pages serves it as-is).
30+
Overwrite `images/profile.jpg` with a similar-ish aspect ratio image (it's
31+
cropped into a circle, so a centred headshot works best) — no HTML changes
32+
needed.
6933

70-
After that, writing a post is: add the Markdown file, commit, push — the
71-
site rebuilds and redeploys on its own in ~30 seconds.
34+
## Attribution
7235

73-
## On "Jekyll" vs Python options
36+
Per the reference project's license, the footer keeps a credit link to
37+
https://www.craftz.dog/ — please leave it in place if you keep using this design.
7438

75-
GitHub Pages' *native*, zero-build option is Jekyll — but Jekyll is Ruby, not
76-
Python, so it's not what you asked for. If you want a fuller Python blogging
77-
engine instead of this hand-rolled one, **Pelican** is the closest
78-
equivalent (Markdown/reST posts, Jinja2 themes, RSS/Atom feeds, tags and
79-
categories built in, and a documented GitHub Pages deploy path via
80-
`ghp-import` or the same Actions approach used here). It's more powerful but
81-
also more machinery — a theme to install or write, a settings file, a
82-
plugin system — for what's currently a five-page personal site.
39+
## Deploying on GitHub Pages
8340

84-
This repo goes with the smaller option on purpose: ~250 lines of Python
85-
total, one dependency (`Markdown`), and the exact design you already have.
86-
If the blog grows into the main thing on the site and you want tags, feeds,
87-
or pagination, migrating this content into Pelican later is a straightforward
88-
step up — the Markdown posts themselves need no changes.
41+
1. Create a repo named `<your-username>.github.io`.
42+
2. Push this whole folder's contents to the repo root (so `index.html` sits
43+
at the top level, not inside a subfolder).
44+
3. Settings → Pages → should already serve from `main` / root for this repo
45+
name. Wait a minute, then visit `https://<your-username>.github.io`.

assets/css/main.css

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
2+
@import url('https://fonts.googleapis.com/css2?family=M+PLUS+Rounded+1c:wght@300;400;700&display=swap');
3+
4+
:root{
5+
--bg:#f0e7db; --fg:#1a202c; --fg-soft:#4a5568; --link:#3d7aed; --accent:#88ccca;
6+
--underline:#525252; --navbg:rgba(255,255,255,0.55); --card-bg:rgba(255,255,255,0.6);
7+
--card-border:rgba(0,0,0,0.08); --btn-fg:#1f7a72; --btn-bg:rgba(56,178,172,0.12); --btn-bg-hover:rgba(56,178,172,0.22);
8+
}
9+
@media (prefers-color-scheme: dark){
10+
:root:not([data-theme="light"]){
11+
--bg:#202023; --fg:#f7fafc; --fg-soft:#cbd5e0; --link:#ff63c3; --accent:#88ccca;
12+
--navbg:rgba(32,32,35,0.55); --card-bg:rgba(255,255,255,0.06); --card-border:rgba(255,255,255,0.12);
13+
--btn-fg:#7fe0d8; --btn-bg:rgba(127,224,216,0.12); --btn-bg-hover:rgba(127,224,216,0.22);
14+
}
15+
}
16+
:root[data-theme="dark"]{
17+
--bg:#202023; --fg:#f7fafc; --fg-soft:#cbd5e0; --link:#ff63c3; --accent:#88ccca;
18+
--navbg:rgba(32,32,35,0.55); --card-bg:rgba(255,255,255,0.06); --card-border:rgba(255,255,255,0.12);
19+
--btn-fg:#7fe0d8; --btn-bg:rgba(127,224,216,0.12); --btn-bg-hover:rgba(127,224,216,0.22);
20+
}
21+
22+
*{box-sizing:border-box}
23+
body{margin:0;background:var(--bg);color:var(--fg);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Helvetica,Arial,sans-serif;transition:background .25s ease,color .25s ease}
24+
h1,h2,h3,h4{font-family:'M PLUS Rounded 1c',sans-serif;font-weight:700;margin:0}
25+
a{color:var(--link);text-decoration:none;text-underline-offset:3px}
26+
a:hover{text-decoration:underline}
27+
.container{max-width:768px;margin:0 auto;padding:0 16px}
28+
29+
#navbar{position:fixed;top:0;left:0;right:0;z-index:20;background:var(--navbg);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px)}
30+
.nav-inner{max-width:768px;margin:0 auto;display:flex;align-items:center;justify-content:space-between;padding:8px 16px;flex-wrap:wrap}
31+
.logo{display:inline-flex;align-items:center;gap:10px;font-family:'M PLUS Rounded 1c',sans-serif;font-weight:700;font-size:18px;color:var(--fg);padding:6px 4px}
32+
.logo:hover{text-decoration:none}
33+
.logo .footprint{transition:transform .2s ease}
34+
.logo:hover .footprint{transform:rotate(20deg)}
35+
.nav-links{display:flex;align-items:center;gap:4px}
36+
.nav-link{padding:8px;border-radius:6px;color:var(--fg)}
37+
.nav-link:hover{text-decoration:none;background:var(--card-bg)}
38+
.nav-link.active{background:var(--accent);color:#20202b}
39+
.nav-contact{display:inline-flex;align-items:center;gap:5px}
40+
.nav-right{display:flex;align-items:center;gap:6px}
41+
.icon-btn{border:none;background:var(--card-bg);color:var(--fg);width:34px;height:34px;border-radius:8px;display:inline-flex;align-items:center;justify-content:center;cursor:pointer}
42+
.icon-btn:hover{background:var(--btn-bg-hover)}
43+
.menu-only{display:none}
44+
#mobile-menu{display:none;flex-direction:column;padding:8px 16px 14px;gap:2px}
45+
#mobile-menu a{padding:10px 8px;border-radius:6px;color:var(--fg)}
46+
#mobile-menu a:hover{background:var(--card-bg);text-decoration:none}
47+
48+
@media (max-width:680px){
49+
.nav-links{display:none}
50+
.menu-only{display:inline-flex}
51+
#mobile-menu:not([hidden]){display:flex}
52+
}
53+
54+
main.container{padding-top:70px;padding-bottom:48px}
55+
56+
.intro-box{border-radius:12px;margin-bottom:24px;padding:12px;text-align:center;background:var(--card-bg);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border:1px solid var(--card-border)}
57+
.hero-row{display:flex;align-items:center;gap:24px;margin-bottom:24px;flex-wrap:wrap}
58+
.hero-text{flex:1;min-width:200px}
59+
.page-title{font-size:28px}
60+
.subtitle{margin:6px 0 0;color:var(--fg-soft)}
61+
.avatar{flex-shrink:0}
62+
.avatar img{width:100px;height:100px;border-radius:999px;object-fit:cover;border:2px solid var(--card-border)}
63+
64+
.fade{margin-bottom:24px;animation:fadeIn .6s ease both;animation-delay:var(--d,0s)}
65+
@keyframes fadeIn{from{opacity:0;transform:translateY(10px)}to{opacity:1;transform:translateY(0)}}
66+
67+
.section-title{text-decoration:underline;font-size:20px;text-underline-offset:6px;text-decoration-color:var(--underline);text-decoration-thickness:4px;margin-top:12px;margin-bottom:16px}
68+
.page-heading{font-size:22px;margin-bottom:16px}
69+
.sub-heading{font-size:18px;margin:0 0 12px}
70+
.para{text-align:justify;text-indent:1em;hyphens:auto;line-height:1.6}
71+
.bio-row{padding-left:3.4em;text-indent:-3.4em;margin-bottom:6px}
72+
.bio-year{font-weight:bold;margin-right:1em}
73+
74+
.center-cta{text-align:center;margin:16px 0}
75+
.btn{display:inline-flex;align-items:center;gap:6px;padding:8px 16px;border-radius:8px;background:var(--btn-bg);color:var(--btn-fg);font-weight:600}
76+
.btn:hover{background:var(--btn-bg-hover);text-decoration:none}
77+
.btn-teal{}
78+
79+
.link-list{list-style:none;padding:0;margin:0 0 16px}
80+
.link-list li{margin-bottom:6px}
81+
.ghost-btn{display:inline-flex;align-items:center;gap:8px;padding:8px 12px;border-radius:8px;color:var(--btn-fg)}
82+
.ghost-btn:hover{background:var(--btn-bg-hover);text-decoration:none}
83+
84+
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:20px;margin:8px 0 20px}
85+
@media (max-width:560px){.grid2{grid-template-columns:1fr}}
86+
.grid-auto{display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));gap:16px;margin:8px 0 20px}
87+
.empty-state{text-align:center;padding:32px 20px;border:1px dashed var(--card-border);border-radius:12px;color:var(--fg-soft)}
88+
.grid-card{display:block;background:var(--card-bg);border:1px solid var(--card-border);border-radius:12px;padding:16px;text-align:center;color:var(--fg)}
89+
a.grid-card:hover{text-decoration:none;background:var(--btn-bg-hover)}
90+
.grid-card.static{text-align:left}
91+
.grid-thumb{width:44px;height:44px;border-radius:10px;display:flex;align-items:center;justify-content:center;background:var(--btn-bg);color:var(--btn-fg);margin:0 auto 10px}
92+
.grid-card.static .grid-thumb{margin:0 0 10px}
93+
.grid-title{font-weight:700;margin-bottom:4px}
94+
.grid-desc{font-size:14px;color:var(--fg-soft)}
95+
96+
.divider{border:none;border-top:1px solid var(--card-border);margin:24px 0}
97+
98+
.plain-list{padding-left:1.2em;margin:0 0 8px;line-height:1.7}
99+
.plain-list li{margin-bottom:4px}
100+
101+
.pub-cat{border:1px solid var(--card-border);border-radius:10px;padding:10px 14px;margin-bottom:10px;background:var(--card-bg)}
102+
.pub-cat summary{cursor:pointer;font-weight:700;list-style:none}
103+
.pub-cat summary::-webkit-details-marker{display:none}
104+
.pub-cat summary:before{content:'▸';display:inline-block;margin-right:8px;transition:transform .15s ease}
105+
.pub-cat[open] summary:before{transform:rotate(90deg)}
106+
.count{font-weight:400;color:var(--fg-soft);font-size:13px}
107+
.pub-list{padding-left:1.4em;margin:10px 0 2px;line-height:1.6}
108+
.pub-list li{margin-bottom:8px;font-size:15px}
109+
110+
footer{max-width:768px;margin:32px auto 0;padding:0 16px;text-align:center;opacity:.55;font-size:13px;line-height:1.6}
111+
112+
.page{display:none}
113+
.page.active{display:block}

assets/js/main.js

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
2+
(function(){
3+
var root = document.documentElement;
4+
function setTheme(t){
5+
if (t) root.setAttribute('data-theme', t); else root.removeAttribute('data-theme');
6+
try { localStorage.setItem('theme', t || ''); } catch(e){}
7+
renderThemeBtn();
8+
}
9+
function currentIsDark(){
10+
var explicit = root.getAttribute('data-theme');
11+
if (explicit) return explicit === 'dark';
12+
return window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
13+
}
14+
function renderThemeBtn(){
15+
var btn = document.getElementById('theme-btn');
16+
btn.innerHTML = currentIsDark() ? ICON_SUN : ICON_MOON;
17+
}
18+
try {
19+
var saved = localStorage.getItem('theme');
20+
if (saved) root.setAttribute('data-theme', saved);
21+
} catch(e){}
22+
23+
var ICON_MOON = '<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><path d="M21 12.79A9 9 0 1 1 11.21 3a7 7 0 0 0 9.79 9.79Z"/></svg>';
24+
var ICON_SUN = '<svg viewBox="0 0 24 24" width="16" height="16" fill="currentColor"><circle cx="12" cy="12" r="4.2"/><g stroke="currentColor" stroke-width="1.8" stroke-linecap="round"><path d="M12 2v2.2M12 19.8V22M4.2 4.2l1.55 1.55M18.25 18.25l1.55 1.55M2 12h2.2M19.8 12H22M4.2 19.8l1.55-1.55M18.25 5.75l1.55-1.55"/></g></svg>';
25+
renderThemeBtn();
26+
27+
document.getElementById('theme-btn').addEventListener('click', function(){
28+
setTheme(currentIsDark() ? 'light' : 'dark');
29+
});
30+
31+
var menuBtn = document.getElementById('menu-btn');
32+
var mobileMenu = document.getElementById('mobile-menu');
33+
menuBtn.addEventListener('click', function(){ mobileMenu.hidden = !mobileMenu.hidden; });
34+
35+
var pages = document.querySelectorAll('.page');
36+
var navLinks = document.querySelectorAll('[data-page]');
37+
function show(page){
38+
pages.forEach(function(p){ p.classList.toggle('active', p.id === 'page-' + page); });
39+
navLinks.forEach(function(a){ a.classList.toggle('active', a.getAttribute('data-page') === page); });
40+
mobileMenu.hidden = true;
41+
window.scrollTo({top:0});
42+
}
43+
function routeFromHash(){
44+
var h = location.hash.replace('#/', '') || 'home';
45+
if (!document.getElementById('page-' + h)) h = 'home';
46+
show(h);
47+
}
48+
window.addEventListener('hashchange', routeFromHash);
49+
routeFromHash();
50+
})();

images/profile.jpg

27.8 KB
Loading

0 commit comments

Comments
 (0)