A configuration-first academic homepage template with unified section rendering, multi-language data, and plugin extensions. 🌐 Live Demo: https://phospheneser.github.io/Phospheneser-awesome-academic-template/
- 🎯 Config Driven – Navigation, sections, and content live in JSON; no HTML edits for day-to-day updates.
- 🧱 Unified Layout – All sections (except About) use the same paragraph-based renderer for consistent typography.
- 🔌 Plugin Ready – Search/sort plugins are bundled via
static/js/plugins.js; register your own with a single call. - 🌍 Multi-language – Per-language folders keep localized content while sharing the same metadata.
- 📱 Responsive – Bulma-based layout tuned for desktop, tablet, and mobile.
- 🎨 Theming – Configure backgrounds, hero avatar, socials, and theme colors in
data/meta.json.
-
Clone & enter the project
git clone <your-repo-url> cd academic-template
-
Configure
data/meta.json– the global source of truth. Minimal sketch:{ "defaultLanguage": "en", "availableLanguages": ["en", "zh"], "languageLabels": {"en": "English", "zh": "中文"}, "navbar": {"showLanguageDropdown": true}, "home": {"showHero": true, "avatar": "./media/personal.jpg"}, "backgrounds": { "default": {"light": "./media/occupacy.jpg"} }, "itemTypes": { "news": {"requiredKeys": ["date", "content"]}, "publication": {"requiredKeys": ["title", "conference", "authors", "description", "links"]} }, "sections": [ { "id": "news", "enabled": true, "itemType": "news", "dataSource": "news", "background": "default", "singlePage": {"enabled": true}, "multiPage": {"enabled": true, "plugins": ["search"]} } ], "socials": [{"icon": "fab fa-github", "url": "https://github.com/your-name"}], "emptyStates": {"news": "No news available."} } -
Add per-language content – each language uses
data/<lang>/.web_content.jsonholds homepage copy (title, subtitle, footer, etc.).<section>.jsonmirrors the sectionid/dataSourceinmeta.json.blogs.jsonlocalizes the blog list/detail chrome (archive_label,page_labels,post_labels).- Blog article bodies live under
blogs/posts/<slug>/<lang>.htmlwith automatic English fallback.
Example
data/en/news.json:{ "nav_label": "News", "title": "🔥 News", "news": [ {"date": "2025.01.01", "content": "Placeholder news item."} ] }Example
data/en/about.json:{ "nav_label": "About Me", "title": "🎄 About Me", "texts": [ "<strong>About me placeholder:</strong> introduce yourself.", "Add another paragraph with your interests or call-to-action." ]
}
Example `data/en/blogs.json`:
```json
{
"nav_label": "Blog",
"title": "Research Log",
"subtitle": "Stories, experiments, and longer reflections from ongoing projects.",
"archive_label": "View all posts",
"page_labels": {
"back": "Back to site",
"search": "Search posts...",
"allTags": "All topics",
"empty": "No posts published yet."
},
"post_labels": {
"backBlog": "Back to blog",
"bottomBackBlog": "Back to blog index",
"darkToLight": "Switch to light mode"
},
"blogs": [
{
"slug": "launching-the-research-blog",
"title": "Why This Blog Exists",
"summary": "Setting the tone for longer-form updates.",
"date": "2025-01-05",
"post_time": "2025-01-05 09:00",
"author": "Joshua Xu",
"tags": ["Meta"]
}
]
}
- Serve statically
Open
python3 -m http.server 8000 # or npx serve .
http://localhost:8000(single-page) orhttp://localhost:8000/multipage_index.html(multi-page).
availableLanguages/languageLabels– configure language dropdown options.backgrounds– reusable light/dark backgrounds; sections reference them viabackground.- Link icons are defined per link object via an
icon(oriconUrl) property; icons render only when specified. sections– declarative section list withid,dataSource,itemType, backgrounds, and plugin usage.itemTypes– optional checklist of expected fields per item type; handy for content QA.emptyStates– per-section fallback text when a list has no items.themes,socials,home– tweak palette, social icons, and hero avatar.
Each section file must expose at least:
{
"nav_label": "...",
"title": "...",
"<dataSource>": [ ... items ... ]
}where <dataSource> matches sections[].dataSource. Omit a language file to hide the section for that locale.
archive_labelcontrols the homepage call-to-action that links to the blog archive.page_labelslocalizes the blog index chrome (back link, search placeholder, tag filter label, empty state, dark-mode tooltip).post_labelsprovides the article page UI strings (navigation buttons, dark-mode tooltip, missing-article copy).blogs[]stores metadata for each post (slug,title,summary,date,post_time,author,tags, optionalimage).- Article bodies live in
blogs/posts/<slug>/<lang>.html; when a translation is missing, the English file is used automatically. - Configure card actions (e.g., "Read more") via
meta.json -> itemTypes.blog.templateOptions.actions.
index.html and multipage_index.html share the same text-first renderer:
- news –
date+content. - publication –
title,conference, optionaldate,authors,description,links. - project –
title,date,authors,description,links. - blog –
title,date,content,links. - timeline –
title,org,date,description.
Missing fields are skipped automatically. When no items exist, the renderer displays the localized emptyStates message.
Fine-tune how each item type maps onto the card layout through meta.json -> itemTypes[*].templateOptions and add styling via .meta-card--<itemType> selectors in CSS.
- Add images under
media/(or any static directory). - Register light/dark paths in
backgrounds. - Reference the key from the section definition. Home falls back to
backgrounds.home→backgrounds.default.
- Append the language code to
availableLanguagesand add a label underlanguageLabels. - Create
data/<lang>/with translatedweb_content.json, section files, and a localizedblogs.json(plus article HTML underblogs/posts/<slug>/<lang>.html). - Provide localized media if needed.
- Describe the section in
meta.json.sections(optionally addemptyStatestext). - Create
<lang>/<dataSource>.jsonin every language folder. - Map fields through
meta.json -> itemTypes(or register a template instatic/js/templates.js) and style with CSS classes such as.meta-card--<itemType>—no homepage HTML edits required.
For detailed changes, see the Usage Example.
- Section markup comes from
static/js/templates.jsand the helper functions inindex.html/multipage_index.html; extendTemplateRegistryor tweakitemTypes[*].templateOptionsfor new layouts without touching page HTML. - Register plugins in
static/js/plugins.jsviaPluginRegistry.registerand add the plugin name tomultiPage.plugins. - Tweak typography and spacing inside
static/css/index.css(.text-homepage-1,.section-text-block, etc.).
- Use a static server with live reload for faster iteration.
itemTypes.requiredKeysact as a checklist when preparing content PRs.- Multi-page mode exercises plugin hooks (search/sort); test both modes after changes.
- If a language file is missing, the corresponding section hides automatically—useful during translation rollouts.