OBE Dashboard — Site Architecture Reference

Repo: haiderejaz6/ChE_Department
Live as: GitHub Pages, single-page static app (no build step, no bundler)

Directory structure

ChE_Department/
├── index.html          # shell only: <head>, body markup, script/link tags
├── css/
│   ├── base.css         # tokens, header, tabbar, shared layout/components
│   ├── dashboard.css    # OBE Dashboard tab styles
│   ├── graph.css        # Curriculum Graph tab styles
│   ├── ontology.css     # ChE Ontology tab styles
│   └── responsive.css   # the one @media(max-width:720px) block — MUST load
│                         # last so its overrides win at narrow widths
├── js/
│   ├── data.js           # pure data: PLOS_DEF, COURSES_EMBEDDED, Bloom's
│   │                      # level defs, prereq edges — no logic
│   ├── shared.js          # init(), switchTab(), semester filters, tooltip,
│   │                       # markdown-viewer modal (used by graph panel)
│   ├── dashboard.js       # Course Explorer, PLO Coverage Matrix, Bloom's charts
│   ├── graph.js            # Curriculum Graph tab (cg* functions, prereq
│   │                        # arrows, course detail panel)
│   └── ontology.js         # ChE Ontology tab (cm*/kc* functions: curriculum
│                            # view, knowledge graph, compare, knowledge-area
│                            # overlap cloud)
├── courses/              # per-course outline markdown files (referenced by
│                          # path pointer from data.json, not embedded)
├── data.json              # generated by excel_to_json.py from Qalam_CLOs.xlsx
└── excel_to_json.py        # pipeline script (lives outside this repo dir
                             # structure notes if applicable — confirm path)

Key architectural facts (don't relitigate these)

  1. This is still one single-page app, not multiple pages. css/ and js/
    are just source-file organization for editing — they are not routes and
    there is no navigation between them. Clicking a tab (Curriculum Graph /
    OBE Dashboard / ChE Ontology) never changes the URL; it toggles the
    hidden class on #tab-graph / #tab-dash / #tab-concepts via
    switchTab() in shared.js.

  2. Deliberately NOT split into separate HTML pages per tab. Considered
    and rejected: it would break shared state (the semester filter updates
    all three tabs from one click via setFilter() in shared.js), force
    full page reloads instead of instant tab switches, reset D3 force-sim/
    zoom state in the ontology view on every visit, and refetch data.json
    three times. Revisit only if tabs need to be independently linkable/
    bookmarkable, or the app outgrows loading all JS upfront — not the case
    currently.

  3. Plain <script src> / <link> tags, no bundler. All JS files share
    one global scope, exactly like the original single-file version — a
    function in dashboard.js can freely call a function defined in
    graph.js or ontology.js. This means:

    • Load order in index.html matters for anything that reassigns the
      same global identifier at load time (rare — only happened once, see
      below). It generally does NOT matter for normal function calls, since
      those resolve at call time, long after all scripts have loaded.
    • Current <script> order in index.html:
      data.js → shared.js → dashboard.js → graph.js → ontology.js,
      followed by an inline <script>init();</script> as the very last tag
      (so init() can safely call functions defined in any of the other
      files).
  4. CSS load order matters for one reason: responsive.css must load
    last.
    It contains the single @media(max-width:720px) block from the
    original file, which touches selectors across all four other CSS files.
    It has to come after base.css/dashboard.css/graph.css/ontology.css
    in index.html for its overrides to win on narrow viewports.

  5. data.json and courses/ stay exactly where they were. Fetch paths
    (fetch("./data.json"), course markdown paths) are relative to
    index.html's location, which hasn't moved — only the CSS/JS got pulled
    into subfolders.

  6. One piece of dead code was removed during the split, on purpose: the
    original file had two conflicting showTip definitions (a 5-arg version
    and a 2-arg version) where the second silently overwrote the first at
    load time. Only the live 2-arg version was kept in shared.js. Zero
    behavior change — just removed a shadowed, unreachable definition.

Where to add new code

Known open items (carried over, unrelated to the split)

Powered by Forestry.md