Personal portfolio of Ehsan Salimi, served by GitHub Pages at https://ehsansl.github.io/.
It's a static site with no framework and no build step at deploy time. index.html holds the page layout and a set of <template> blocks. Small ES modules in js/ fill those blocks from the JSON files in data/. To update the site, you edit data, not HTML.
| To change… | Edit |
|---|---|
| Name, headline, tagline, about text | data/site.json |
| Availability line, location, email | data/site.json |
| GitHub / LinkedIn / email links | data/site.json → links |
| Projects | data/projects.json (or run npm run new:project) |
| Jobs, research and teaching roles | data/experience.json |
| Skills | data/skills.json |
| Degrees | data/education.json |
| CV | replace files/Ehsan_Salimi_CV.pdf (keep the name, or update cv in site.json) |
| Colours, spacing, fonts | scss/_tokens.scss, then npm run sass:build |
Field-by-field reference: docs/data-schema.md.
A few rules the renderer follows, so you know what to expect:
- Projects with
"featured": truego in the main grid. The rest go under "More things I've built". Within each group they're sorted byorder. "visible": falsehides an entry without deleting it.- Link buttons only appear for links that exist. Supported types:
live,demo,code,paper,slides,video. - The filter chips are built from
categoryvalues that at least two projects share. - Numbers like
99.8%or+5%in highlights are emphasized automatically. - An optional
"todo"field on any entry is never shown on the site.npm run validateprints it as a reminder.
npm run new:project # answers a few questions and appends to data/projects.jsonThen add an image at images/projects/<id>.webp (3:2, around 900×600) and set "image": { "src": "...", "alt": "..." }. Without an image, the card shows a patterned placeholder labelled with the project's first category.
npm install # once
npm run dev # serves the site at http://localhost:5173You can't open index.html by double-clicking. Browsers block fetch() for the data files on file://, so use npm run dev.
npm run validate # checks every data file: required fields, ids, dates, links, image pathsThe same check runs on GitHub (.github/workflows/validate.yml) for every push to main and every pull request.
scss/main.scssimports only the Bootstrap parts the site uses (reboot and containers) plus_tokens.scss,_base.scssand_components.scss. It compiles tocss/styles.cssvianpm run sass:build(ornpm run sass:watchwhile editing). Never editcss/styles.cssby hand.- Icons are an SVG sprite at
images/icons.svg, built from Font Awesome Free bynpm run icons. To add an icon, add its name to the list inscripts/build-icons.mjsand rerun the script. - Fonts are self-hosted in
fonts/(Poppins 400/600/700, Latin subset).
Merge to main and push. GitHub Pages serves the repository as-is (.nojekyll turns off Jekyll processing). Commit the compiled css/styles.css and images/icons.svg, because Pages doesn't run npm.
index.html page layout + <template> blocks
data/*.json all content
js/main.js entry: loads data, calls the renderers
js/data.js fetch + light checks
js/render/*.js one renderer per section
js/ui.js theme toggle, scroll reveal, nav highlight
js/utils/dom.js safe DOM helpers (text only, no innerHTML)
scss/ styles (source) → css/styles.css (compiled)
scripts/ validate-data, new-project, build-icons
images/ fonts/ files/ assets
404.html robots.txt sitemap.xml favicon.*