A 100% static, GitHub Pages–compatible library of standalone HTML question papers.

No backend. No database. No build step. No frameworks. No npm install.
Just HTML + CSS + vanilla JS, with MathJax loaded from CDN inside papers only.
When you open the site you see a searchable, filterable catalogue; clicking Read paper opens that paper as a normal independent page (full navigation, not an iframe).
Live use: open index.html in a browser, or serve the folder with any
static server. Deploy by pushing to GitHub and enabling Pages.
- Features
- Live demo / preview
- Repository structure
- How it works (30-second version)
- Adding a new paper
- Metadata reference
- Paper authoring guide
- Homepage catalogue guide
- Styling and theme
- Deploying to GitHub Pages
- Local development
- Accessibility, responsive, print
- Troubleshooting / FAQ
- Further reading
- License / reuse
- Searchable catalogue — tokenised AND-search across title, category,
level, description, and difficulty (
assets/js/archive.js). - Subject filters — All, Physics, Mathematics, Computer Science,
Machine Learning, Electronics, Other. Matching is substring-based on
category + level, soMathematics / Machine Learningappears under both Mathematics and Machine Learning. - Auto-numbered entries —
01,02, … generated at render time; no manual numbering. - Live result counts —
N papers in archive/Showing X of Y papers, plus aNo papers match your searchempty state. - Standalone papers — every file in
papers/*.htmlembeds its own<style>, works offline (except MathJax CDN), prints cleanly, and links back with<a href="../index.html">← Back to Archive</a>. - MathJax per paper — LaTeX via
$…$/$$…$$(and\(…\)/\[…\]). The homepage itself loads no MathJax, so it stays fast. - Zero-dependency sync helper —
tools/sync_papers.pyscanspapers/, suggests title / description / problem count from the HTML, and inserts new entries intoindex.htmlidempotently. - Accessible + responsive + print-friendly — skip link,
aria-pressedfilters,aria-livecatalogue, keyboard focus rings, mobile layout, and@media printrules.
No build required. Pick one:
# fastest — just open the file
xdg-open index.html # Linux
open index.html # macOS
start index.html # Windows
# or serve locally (recommended — closer to GitHub Pages behaviour)
python3 -m http.server 8000
# then visit http://localhost:8000//
├── index.html # library homepage + PAPER DATABASE metadata array
├── papers/
│ ├── machines_olympiad.html
│ ├── logistic_regression.html
│ ├── linear_regression_advanced.html
│ ├── logistic_regression_advanced.html
│ ├── naive_bayes_quiz.html
│ └── example_paper.html # minimal template — copy it for new papers
├── assets/
│ ├── css/shared.css # homepage stylesheet (papers are self-contained)
│ ├── js/archive.js # catalogue rendering, search, filters
│ └── images/ # static images (currently empty / reserved)
├── tools/
│ └── sync_papers.py # scan papers/ → insert missing entries into index.html
├── sync_papers.py # duplicate of tools/sync_papers.py kept at root for convenience
├── ARCHITECTURE.md # how the system works internally
├── CONTRIBUTIONS.md # how to contribute papers, fixes, features
├── JOURNEY.md # how this project was built, step by step
└── README.md # this file
Key principle: paper content lives only in papers/*.html.
index.html holds just metadata (one object per paper). assets/ holds
only homepage chrome. Papers never import assets/ — they are portable.
index.htmldefines a globalconst papers = [ … ]array (markedPAPER DATABASE) with one object per paper:title,file,category,level,difficulty,problems,year,description.assets/js/archive.jsreads that array, renders an<li>card per paper into<ol id="catalogue">, and wires up the search box + filter buttons.- Clicking Read paper follows the relative
filelink verbatim, e.g.papers/machines_olympiad.html— a full standalone document. - Each paper embeds its own CSS + MathJax snippet, so it renders correctly with zero shared dependencies.
See ARCHITECTURE.md for the full data-flow, file contracts, and design decisions.
- Create the HTML file in
papers/, e.g.papers/newtonian_mechanics.html. Easiest: copy the template:Keep it standalone: embed itscp papers/example_paper.html papers/newtonian_mechanics.html
<style>, use the MathJax snippet below, and include the top nav:<a href="../index.html">← Back to Archive</a>
- Add one entry to the
papersarray inindex.html(markedPAPER DATABASE):{ title: "Newtonian Mechanics — Problem Set", file: "papers/newtonian_mechanics.html", category: "Physics", level: "Class 11 · Olympiad", difficulty: "Intermediate", problems: 10, year: "2026", description: "Short description shown on the homepage card." }
- Done. Refresh
index.html. Numbering, counts, search and filters update automatically. No other file needs editing.
Relative paths only. Use
papers/x.htmlandassets/…— never leading-slash paths like/papers/x.html— so the site works underhttps://<username>.github.io/<repository-name>/.
The helper finds HTML files in papers/ missing from index.html and
prompts for metadata, pre-filling suggestions parsed from each file
(<title>, <meta name="description">, Problem N headings).
# preview what would be added (no writes, no prompts)
python3 tools/sync_papers.py --dry-run
# interactively add missing papers
python3 tools/sync_papers.py
# also consider example_paper.html (skipped by default)
python3 tools/sync_papers.py --include-template
# run against a different checkout
python3 tools/sync_papers.py --root /path/to/problem-libraryBehaviour: scans papers/*.html (sorted by filename), compares against
file: values in const papers = […], prompts per new file, then inserts
new entries before the closing ] while preserving the rest of
index.html byte-for-byte. Re-runs are idempotent — already-registered
files are never duplicated. Ctrl-C aborts without writing.
Each object in the papers array:
| Field | Type | Required | Example | Notes |
|---|---|---|---|---|
title |
string | yes | "Machines — Olympiad Problem Set" |
Card heading; also used in search + aria-label. |
file |
string | yes | "papers/machines_olympiad.html" |
Relative path, verbatim into <a href>. Must match actual file. |
category |
string | yes | "Physics", "Mathematics / Machine Learning" |
Drives filter matching (substring, case-insensitive, on category + level). |
level |
string | no | "Class 10 · Olympiad", "Advanced" |
Shown in classification line; also filter-matched. |
difficulty |
string | no | "Advanced", "easy" |
Shown in facts line; searched. Free text, but keep values consistent. |
problems |
number | no | 12 |
Shown as 12 problems in facts line. Must be an integer if set. |
year |
string | no | "2026" |
Shown in facts line. |
description |
string | no | "Rigorous problems on …" |
Card body text; searched. Keep to 1–2 sentences. |
assets/js/archive.js → paperMatchesCategory():
Allmatches everything.Physics,Mathematics,Computer Science,Machine Learning,Electronicsmatch if the lowercasedcategory + " " + levelcontains that token. Socategory: "Mathematics / Machine Learning"joins both filters.Othermatches papers containing none of the five known tokens — e.g.category: "Other"or anything bespoke.
To add a new filter bucket, edit the FILTERS array in archive.js and
extend the known list in the Other branch.
Edit the corresponding object in the papers array in index.html —
title, category, level, difficulty, problems, year,
description. Save, refresh. Nothing else to rebuild.
<script>
MathJax = {
tex: {
inlineMath: [['$', '$'], ['\\(', '\\)']],
displayMath: [['$$', '$$'], ['\\[', '\\]']]
}
};
</script>
<script async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js"></script>The homepage itself needs no MathJax.
Start from papers/example_paper.html. It demonstrates every pattern:
- Document shell —
<!DOCTYPE html>,<meta charset>,<meta viewport>,<title>,<meta name="description">(reused bysync_papers.pyas the description suggestion). - Embedded stylesheet —
:roottokens (--ink,--paper,--muted,--hair), serif body,.sheetcolumn (max-width: 44rem), double-rule masthead,.problemblocks,.partssubparts(a)–(d),.solutionleft-border blocks,.marklabels, focus + responsive + print rules. Copy and adapt; do not linkassets/css/shared.css— papers must stay standalone. - Top nav —
<nav class="topnav">withBack to Archive(../index.html) + anchor to#solutions. - Header block —
.paper-headwith kicker,<h1>, sub-line (N problems · Difficulty · minutes), and anid-grid(Name / Date / Class / Score). - Instructions + Problems —
<h2 class="section">sections; each problem is<article class="problem"><h3>Problem N <span class="mark">with optional<ol class="parts">,<figure>/inline SVG, and display math. - Solutions —
<section id="solutions">with.solutiondivs. - Footer — back links +
End of paper.+Made by Reman Deycredit.
Conventions: one concept per problem, marks in [N marks], SVG for simple
diagrams (no external images), mjx-container horizontal-scroll guard for
long display equations on mobile.
index.htmlskeleton: skip link →.archive-headermasthead →main.archive-main→section.controls(search +<ul id="filters">) →<ol id="catalogue">(populated by JS) →#no-results+#archive-count→.archive-footer→PAPER DATABASEscript →archive.js.archive.js(IIFE, strict mode, no dependencies):FILTERSconstant,normalise()lowercasing,paperMatchesCategory()/paperMatchesQuery()(all query tokens must match),factsLine()/classificationLine(),render()(rebuilds<li class="paper-entry">cards with zero-padded numbers, title, classification, facts, description,Read paperlink),buildFilters()(generates buttons witharia-pressed).- Cards use
textContent(notinnerHTML), so metadata with<,&, quotes renders safely. - Search input listens on
inputevents; filters re-render on click. Catalogue hasaria-live="polite"; count usesrole="status".
assets/css/shared.css— single homepage stylesheet. Black-and-white academic theme: serif body, double-rule header/footer, bordered search, toggle filter buttons (inverted when active), grid catalogue entries (3rem 1frnumber + body), solid blackRead paperbuttons (full-width on mobile), muted italic metadata.- CSS variables (
--ink,--paper,--muted,--rule,--hairline,--max-width: 44rem,--serif,--sans) at the top make re-theming a one-place edit. - Papers intentionally do not use this file — each embeds its own copy of the sheet styles so a paper can be downloaded / emailed / hosted alone.
- Push this folder to a GitHub repository.
- Go to Settings → Pages → Build and deployment → Deploy from branch.
- Select branch (e.g.
main) and folder/ (root). Save. - Open
https://<username>.github.io/<repository-name>/.
No install, build, server, or environment variable is required. The only network dependency at runtime is the MathJax CDN inside papers.
git clone <your-repo-url> problem-library
cd problem-library
python3 -m http.server 8000
# edit papers/*.html, index.html, assets/…
# refresh the browser — no build step
python3 tools/sync_papers.py --dry-run # check catalogue syncValidate quickly: open DevTools console (no errors), test search
(e.g. logistic), click each filter, click every Read paper link,
resize to 360 px, print-preview one paper.
- Skip link, semantic landmarks (
header/main/footer/nav/section), labelled search,aria-pressedfilters,aria-livecatalogue, visible:focus-visibleoutlines, ≥44 px touch targets. - Single-column
44remmeasure; catalogue collapses to one column under 560 px; display equations scroll horizontally instead of overflowing. - Print stylesheets on both homepage and papers hide navigation/search/ buttons and reset to clean black-on-white output.
| Symptom | Cause / fix |
|---|---|
| Paper link 404 on GitHub Pages | file uses a leading slash or wrong case. Must be exactly papers/name.html, relative, case-matching. |
| New paper not listed | No entry in the papers array yet — add manually or run tools/sync_papers.py. |
| Math not rendering | Missing MathJax snippet, or offline. Homepage intentionally has no MathJax — check inside the paper file. |
| Filter shows paper in wrong bucket | Category/level text contains (or lacks) the filter token. See Filter routing. |
| Styles broken | index.html must reference assets/css/shared.css relatively; papers must embed their own <style>. |
sync_papers.py says const papers = [ not found |
index.html array was renamed/removed. Restore the const papers = [ marker. |
Duplicate root sync_papers.py vs tools/sync_papers.py |
Both copies are currently identical; canonical one is tools/sync_papers.py. Keep them in sync or remove the root copy. |
- ARCHITECTURE.md — components, data flow, contracts, constraints, scaling notes.
- CONTRIBUTIONS.md — how to add papers, report issues, propose features, style rules.
- JOURNEY.md — how this project was designed and built.
No license file is currently declared. If you plan to accept external
contributions or publish widely, add a LICENSE (e.g. MIT / CC-BY for
problem content) and reference it here. Until then, treat all content as
all-rights-reserved to the repository owner.