A personal portfolio and blog CMS built with Wagtail and Django.
This project started from the official Wagtail beginner and extended tutorials, and was then expanded with custom homepage sections, content blocks, rich text editor extensions, and production-focused configuration.
- Typography
- Colour Scheme
- Database Diagram
- Features
- Home page
- Blog
- Navigation
- Authentication & Authorisation
- Technologies Used
- Libraries Used
- Deployment
This project uses a clear two-font system to balance personality and technical clarity.
- Display / headings: Space Grotesk (Google Fonts)
- Body / utility text: JetBrains Mono (Google Fonts)
The visual identity is dark, modern, and editorial with cyan and amber accents.
--text:#bebebe--background:#1f2633--primary:#6abdd2--secondary:#1c3f4d--accent:#d1944b
- Primary color is used for links, navigation, button outlines, and section markers.
- Accent color is used for emphasis, stack labels, hover highlights, and custom rich text styling.
- The page uses a vertical background gradient from
--backgroundto--secondary. - Layered glass-style UI is achieved through transparency and blur effects in navigation/cards.
erDiagram
HOME_HOMEPAGE ||--o{ PORTFOLIO_PORTFOLIOPAGE : parent_page
BLOG_BLOGPAGE ||--o{ BLOG_BLOGPAGEGALLERYIMAGE : has_many
BLOG_BLOGPAGE }o--o{ BLOG_AUTHOR : authors_m2m
BLOG_BLOGPAGE ||--o{ BLOG_BLOGPAGETAG : tagged_items
TAGGIT_TAG ||--o{ BLOG_BLOGPAGETAG : tag_reference
BASE_FORMPAGE ||--o{ BASE_FORMFIELD : form_fields
BASE_NAVIGATIONSETTINGS {
int id PK
string linkedin_url
string github_url
string email
}
BASE_FOOTERTEXT {
int id PK
text body
bool live
datetime first_published_at
datetime latest_revision_created_at
}
BASE_FORMPAGE {
int id PK
text intro
text thank_you_text
string from_address
string to_address
string subject
}
BASE_FORMFIELD {
int id PK
int page_id FK
string label
string field_type
bool required
string choices
int sort_order
}
BLOG_BLOGINDEXPAGE {
int id PK
text intro
}
BLOG_BLOGPAGE {
int id PK
date date
string intro
json body_streamfield
}
BLOG_AUTHOR {
int id PK
string name
int author_image_id FK
}
BLOG_BLOGPAGEGALLERYIMAGE {
int id PK
int page_id FK
int image_id FK
string caption
int sort_order
}
BLOG_BLOGTAGINDEXPAGE {
int id PK
}
HOME_HOMEPAGE {
int id PK
int image_id FK
string hero_text
string hero_lead
string hero_cta
int hero_cta_link_id FK
text intro
text about
string top_project_title
text top_project_text
text top_project_stack
int top_project_image_id FK
string top_project_gh
string top_project_dep
json featured_projects_stream
json featured_posts_stream
}
PORTFOLIO_PORTFOLIOPAGE {
int id PK
json body_streamfield
}
- Wagtail-driven portfolio and blog CMS.
- Structured, editor-friendly content through StreamField and snippets.
- Homepage context automatically injects the latest published blog post.
CaptionedImageBlockHeadingBlockBaseStreamBlockheading_blockparagraph_blockimage_blockembed_block
CodeBlock- language selector: python, javascript, bash, html, css, json, sql
- code text body
ProjectCardBlock- title
- rich description
- tech stack
- repository link
- deployed link (optional)
CardBlockFeaturedPostsBlockPortfolioStreamBlock(extendsBaseStreamBlockand adds portfolio-specific sections)
rt_accentinline style (accented text viaspan.c-accent).colour-redinline style (red emphasis viaspan.rt-red).
NavigationSettings(LinkedIn, GitHub, Email) as site setting.FooterTextas snippet with draft/revision/preview workflow.- Template tags:
get_footer_textget_site_rootget_latest_cv_document
The homepage is a highly customized, section-based landing experience:
- Hero
- Intro copy, lead line, CTA label, CTA internal page link.
- About
- Rich text with custom accent style support.
- Profile image panel.
- Top project
- Showcase card with image, description, stack list, GitHub and live links.
- Featured projects
- StreamField-driven grid using
ProjectCardBlock.
- StreamField-driven grid using
- Featured articles
- Editor-selected blog posts + link to blog index.
- Contact area
- Social/email links from global settings.
- Contact form page CTA.
The blog subsystem includes:
BlogIndexPagewith reverse-chronological listing of live posts.BlogPagewith date, intro, StreamField body, authors, and gallery images.Authorsnippet model with optional avatar.BlogPageGalleryImageorderable relationship per post.
Frontend blog templates provide:
- Rich blog listing cards with optional hero image per post.
- Author chips, image gallery on detail pages.
- Custom code block rendering through
blog/blocks/code_block.html.
Navigation behavior is context-aware:
- Fixed, blurred top navbar.
- On homepage:
- local anchor links (
About,Work,Articles,Contact me) - plus in-menu child pages
- local anchor links (
- On non-home pages:
- in-menu child pages only
Additional navigation utilities:
- Resume button auto-resolves the newest Wagtail document containing “CV” or “Resume” in its title.
- Footer renders social links from
NavigationSettings. - Footer text is dynamically pulled from the live
FooterTextsnippet.
Current implementation relies on standard Django/Wagtail access control:
- Authentication: default Django + Wagtail admin login.
- Authorisation: role/permission management through Wagtail and Django admin groups.
- Public visitor access: controlled by Wagtail page publish state (
live).
No custom public user registration, custom auth backend, or member-only gated frontend is currently implemented.
- Python
- Django 6.0.3
- Wagtail 7.3.1
- SQLite (development)
- PostgreSQL (production target)
- Bootstrap 5
wagtailDjangodjango-modelclusterdjango-taggitdjango-filter
whitenoisedj-database-urlpsycopg2-binarygunicorndjango-cloudinary-storagecloudinary
Deployment architecture in this project includes:
- Build pipeline through
build.sh(install,collectstatic,migrate). - Static asset handling via WhiteNoise compressed manifest storage.
- Cloudinary-backed media storage in production.
- Database URL configuration via
dj-database-url. - HTTPS redirect and trusted CSRF origin support in production settings.
For the complete deployment walkthrough, use your published article:
- Deployment guide: Read the full deployment article
