Templates and frontend¶
Server-rendered Jinja2 + HTMX. No SPA, no client-side state library. The whole frontend is ~30 templates + one ~3000-line CSS file + a small set of <script> blocks inline in the templates that need them.
Templates¶
templates/ is flat. Convention:
| File | Surface |
|---|---|
base.html |
Layout, nav, theme toggle, feedback widget |
home.html |
/ Today page |
problem.html |
/problem/<slug> |
content.html |
/content/<slug> |
content_new.html |
/library/add form |
readiness.html |
/readiness |
voices.html |
/voices roster |
skills.html |
/skills per-axis breakdown |
library.html |
/library catalog |
settings.html |
/settings |
settings_reset.html |
/settings/reset confirmation |
jd_*.html / jds_index.html |
JD CRUD |
assess_*.html |
Assessment quiz |
credentials_*.html |
CV upload + review |
reflect.html / reflect_detail.html |
Reflect surface |
feedback_inbox.html |
/feedback/inbox |
writings*.html |
Writing CRUD |
_skill_chart.html |
Pentagon SVG include |
base.html defines:
{% block title %}— overridden per page for the browser tab{% block main_class %}— empty default; per-page can setcontainer--narrow(760px) for prose-heavy pages{% block body %}— page content
Custom Jinja filters¶
Registered in app/main.py:
| Filter | Purpose |
|---|---|
markdown |
Mistune render of body text |
code |
Pygments-rendered code block |
pentagon_geometry |
Computes SVG coordinates for the skills pentagon |
jd_title |
Detects URL-shaped JD titles + derives a clean title (heals legacy data at render time) |
CSS architecture¶
static/css/main.css is a single ~3000-line file. Organized by section:
- Custom properties (colors, spacing, typography) at the top
- A11y data-attribute overrides
- Typography, links, base
- Container, header, nav
- Component blocks (BEM-ish:
.next-action__title,.daily-progress__seg, etc.) - Per-surface blocks
- Media queries at the bottom
Theming is two CSS variable scopes: :root (light) and :root[data-theme="dark"] (dark). Dark mode is the platform default; the toggle persists to localStorage["theme"] + a brief inline script in <head> sets data-theme="dark" before paint to avoid flash.
HTMX patterns¶
The platform uses HTMX for:
- Feedback widget POSTs (
hx-post="/feedback") /hint/<slug>per-phase hint fetches (swap into per-phase slots)/submit/<slug>problem submission (swap result into#result)
HTMX-specific behaviors layered on top:
htmx:confirm→run_testsresubmit guardhtmx:configRequest→ re-sync feedback widget theme metadata at submit timehtmx:afterRequest→ success/failure rendering in the feedback widget
Accessibility¶
- Three a11y toggles (reduce-motion, larger-text, high-contrast) drive
data-*attributes on<html>set inbase.htmlfromusers.settings_json.a11y. - OS-level
prefers-reduced-motionmedia query also respected. - All form inputs have associated
<label>elements. - Focus rings: thicker + brand-color outline when high-contrast is on.
- ARIA live regions for hint slots and feedback widget status.