Last updated: 25th March 2026
Components¶
Info
Most of these have not been implemented yet and are subject to change.
Philosophy¶
Every visible UI element that appears in more than one place is a component — a self-contained, reusable template fragment. Nothing is duplicated. If a design change is needed, it happens in one file and propagates everywhere automatically.
This is Django's {% include %} tag put to disciplined use.
templates/
├── base.html # master layout — orchestrates everything below
├── components/ # structural UI — present on most/all pages
│ ├── _nav.html
│ ├── _footer.html
│ └── _cta.html
└── partials/ # content fragments — often HTMX targets
├── _project_card.html
├── _project_grid.html
├── _blog_card.html
├── _contact_form.html
└── _contact_success.html
components/ — structural chrome that wraps every page. Included statically in
base.html. Never an HTMX target.
partials/ — content fragments that can be rendered standalone. These are what
HTMX requests return. A GET /projects/?tag=django response is just
_project_grid.html, not a full page.
Component Inventory¶
_nav.html¶
The navigation bar. Included once in base.html. Contains:
- Site logo / name (links to
core:home) - Navigation links (About, Work, Projects, Blog)
- Language switcher
- Theme toggle
Keeping nav isolated means restyling or adding a link touches exactly one file.
_footer.html¶
Site footer. Included once in base.html. Contains:
- Social links (GitHub, LinkedIn)
- Copyright line
- Secondary navigation if needed
_cta.html¶
The "Contact me" call-to-action block. Appears at the bottom of the landing page and at the end of the About and Work pages. One file, three placements:
If the copy, styling, or destination URL ever changes, it changes once.
Content Partials¶
_project_card.html¶
Renders a single Project instance — title, description snippet, tag chips,
GitHub/live links. Used by _project_grid.html.
<!-- Expects a `project` variable in context -->
<div class="card">
<h3>{{ project.title }}</h3>
<p>{{ project.description }}</p>
{% for tag in project.tags.all %}
<span class="badge">{{ tag.name }}</span>
{% endfor %}
</div>
_project_grid.html¶
Loops over a projects queryset and renders a _project_card.html for each.
This is the HTMX target for tag filtering — the entire grid is swapped in one
operation without touching the nav, filters, or page heading.
{% for project in projects %}
{% include "partials/_project_card.html" %}
{% empty %}
<p>No projects found.</p>
{% endfor %}
_blog_card.html¶
The blog post preview — title, published date, opening paragraph or excerpt, and
a "Read more" link. The equivalent of _project_card.html for posts. Each entry
on the /blog/ listing page is one of these.
_contact_form.html / _contact_success.html¶
The two states of the contact form interaction. The form renders by default; on successful submission HTMX swaps it for the success confirmation. Both are partials so they can be returned directly from a view without a full page render.
The {% include %} + Context Pattern¶
Django's {% include %} passes the current template context down by default.
For cases where you need to pass specific data, use the with keyword:
<!-- Pass a specific object -->
{% include "partials/_project_card.html" with project=featured_project %}
<!-- Pass data and isolate context (only the passed vars are available) -->
{% include "partials/_project_card.html" with project=p only %}
The only keyword is useful for partials that should be fully self-contained —
it prevents accidental dependence on whatever happens to be in the parent context.
Why This Matters Long-Term¶
Without this discipline, a typical growth path looks like:
Week 1: Copy-paste the nav into home.html, about.html, work.html
Week 8: Add a new nav link — edit 6 files
Week 12: Redesign the project card — hunt down every place it's used
Week 16: The footer on the contact page is slightly different and nobody knows why
With components:
Week 1: Write _nav.html once, include it in base.html
Week 8: Add a new nav link — edit 1 file
Week 12: Redesign the project card — edit 1 file
Week 16: The footer is identical everywhere because there is only one footer
The upfront cost is naming discipline and one extra {% include %} tag.
The long-term payoff is a codebase where changes are local and auditable.