A lightweight, elegant blogging platform built with FastHTML
Project description
Bloggy
A lightweight, elegant blogging platform built with FastHTML that renders Markdown files into beautiful web pages with advanced features.
Architecture Overview
---
width: 80vw
---
graph TB
subgraph "User Interface"
Browser[Web Browser]
Theme[Light/Dark Theme Toggle]
end
subgraph "FastHTML Application"
App[FastHTML App<br/>core.py]
Router[URL Router]
Layout[Layout Engine]
subgraph "Route Handlers"
Index[Index Route<br/>/]
PostDetail[Post Detail Route<br/>/posts/path]
end
end
subgraph "Markdown Processing"
MDParser[Mistletoe Parser]
Renderer[ContentRenderer]
subgraph "Custom Renderers"
Footnotes[Footnote Renderer<br/>Sidenotes]
Mermaid[Mermaid Diagram Renderer<br/>Zoom/Pan Controls]
Links[Link Renderer<br/>HTMX Integration]
end
end
subgraph "File System"
MDFiles[Markdown Files<br/>.md]
Tree[Folder Tree Builder<br/>build_post_tree]
end
subgraph "Frontend Assets"
Static[Static Files]
JS[scripts.js<br/>Mermaid + Interactions]
CSS[Styles<br/>TailwindCSS + MonsterUI]
end
Browser -->|HTTP Request| Router
Theme -->|Toggle Dark Mode| JS
Router --> Index
Router --> PostDetail
Index --> Tree
Tree --> MDFiles
Index --> Layout
PostDetail --> MDFiles
PostDetail --> MDParser
MDParser --> Renderer
Renderer --> Footnotes
Renderer --> Mermaid
Renderer --> Links
Footnotes -->|Marginal Notes| Layout
Mermaid -->|Interactive Diagrams| Layout
Links -->|HTMX Navigation| Layout
Layout --> Browser
JS -->|Theme Change| Mermaid
JS -->|Zoom/Pan/Reset| Mermaid
Static --> CSS
Static --> JS
App -.->|Serves| Static
style Browser fill:#e1f5ff
style App fill:#fff3cd
style MDParser fill:#d4edda
style Static fill:#f8d7da
style Mermaid fill:#cce5ff
style Footnotes fill:#cce5ff
How Bloggy Works
1. Request Flow
---
width: 80vw
---
sequenceDiagram
participant User
participant Browser
participant FastHTML
participant Router
participant FileSystem
participant Renderer
participant HTMX
User->>Browser: Visit /
Browser->>FastHTML: GET /
FastHTML->>Router: Route to index()
Router->>FileSystem: Scan for .md files
FileSystem-->>Router: Return file tree
Router->>Browser: Render post list + layout
User->>Browser: Click post link
Browser->>HTMX: hx-get="/posts/demo"
HTMX->>FastHTML: GET /posts/demo
FastHTML->>Router: Route to post_detail()
Router->>FileSystem: Read demo.md
FileSystem-->>Router: Markdown content
Router->>Renderer: Parse & render markdown
rect rgb(200, 220, 250)
Note over Renderer: Process custom syntax:<br/>- Footnotes [^1]<br/>- Mermaid diagrams<br/>- Internal links
end
Renderer-->>Router: Rendered HTML
Router->>HTMX: Return HTML fragment
HTMX->>Browser: Swap content (#main-content)
Browser->>User: Display post
2. Markdown Processing Pipeline
---
width: 80vw
---
flowchart LR
A[Raw Markdown] --> B{Extract Footnotes}
B -->|Content| C[Mistletoe Parser]
B -->|Footnote Defs| D[Store in Dict]
C --> E[Token Stream]
E --> F{ContentRenderer}
F -->|BlockCode + 'mermaid'| G[Mermaid Renderer]
F -->|Link| H[Link Renderer]
F -->|FootnoteRef| I[Footnote Renderer]
F -->|Other| J[Standard HTML]
G --> K[Diagram with Controls]
H --> L{Internal Link?}
L -->|Yes| M[Add HTMX attrs]
L -->|No| N[Add target=_blank]
I --> O[Sidenote Component]
D --> O
K --> P[Apply CSS Classes]
M --> P
N --> P
O --> P
J --> P
P --> Q[Final HTML]
style G fill:#ffe6cc
style I fill:#d1ecf1
style L fill:#fff3cd
3. Mermaid Diagram Lifecycle
---
width: 60vw
---
stateDiagram-v2
[*] --> Rendered: Page Load
state Rendered {
[*] --> Initialize
Initialize --> AddControls: Create buttons
AddControls --> StoreCode: Save original code
StoreCode --> EnableInteraction: Mouse events
}
state EnableInteraction {
[*] --> Idle
Idle --> Panning: Mouse drag
Idle --> Zooming: Mouse wheel
Idle --> ButtonZoom: +/- buttons
ButtonZoom --> Idle
Zooming --> Idle
Panning --> Idle
state "Reset Button" as Reset
Idle --> Reset: Click reset
Reset --> Idle: Restore defaults
}
Rendered --> ThemeChange: Dark/Light toggle
state ThemeChange {
[*] --> DetectTheme
DetectTheme --> GetOriginalCode: Read data attribute
GetOriginalCode --> ClearWrapper
ClearWrapper --> ReinitMermaid: New theme
ReinitMermaid --> ReRender: mermaid.run()
}
ThemeChange --> Rendered: Re-rendered
note right of ThemeChange
MutationObserver watches
HTML class changes
end note
note right of EnableInteraction
Transform state stored
per diagram ID
end note
Key Features
✨ Advanced Markdown Features
- Footnotes as Sidenotes:
[^1]references become elegant margin notes on desktop, expandable on mobile - Mermaid Diagrams: Full support for flowcharts, sequence diagrams, state diagrams, etc.
- Interactive Diagrams: Built-in zoom, pan, and reset controls for all mermaid diagrams
- Theme-aware Rendering: Diagrams automatically re-render when switching light/dark mode
🎨 Modern UI
- Responsive Design: Works beautifully on all screen sizes
- Dark Mode: Automatic theme switching with localStorage persistence
- HTMX Navigation: Fast, SPA-like navigation without full page reloads
- Collapsible Folders: Organize posts in nested directories
🚀 Technical Highlights
- Built on FastHTML for modern Python web development
- Uses Mistletoe for extensible Markdown parsing
- TailwindCSS + MonsterUI for styling
- Hyperscript for interactive behaviors
- Mermaid.js v11 for diagram rendering
Project Structure
graph LR
subgraph bloggy/
A[__init__.py]
B[core.py<br/>Main App Logic]
C[main.py<br/>Entry Point]
subgraph static/
D[scripts.js<br/>Mermaid + Interactions]
E[sidenote.css<br/>Footnote Styles]
F[favicon.png]
end
end
subgraph demo/
G[*.md Files<br/>Your Blog Posts]
subgraph guides/
H[*.md Files<br/>Nested Content]
end
end
B --> D
B --> E
B --> F
B -.reads.-> G
B -.reads.-> H
style B fill:#ffe6cc
style D fill:#d1ecf1
style G fill:#d4edda
Installation
From PyPI (recommended)
pip install bloggy
From source
git clone https://github.com/yeshwanth/bloggy.git
cd bloggy
pip install -e .
Quick Start
-
Create a directory with your markdown files:
mkdir my-blog cd my-blog echo "# Hello World" > hello.md
-
Run Bloggy:
bloggy . -
Open your browser at
http://127.0.0.1:5001
Configuration
Bloggy supports three ways to configure your blog (in priority order):
.bloggyconfiguration file (TOML format)- Environment variables
- Default values
Using a .bloggy Configuration File
Create a .bloggy file in your blog directory or current directory:
# Blog title (default: derives from root folder name)
title = "My Awesome Blog"
# Root folder containing markdown files (default: current directory)
root = "."
# Server host (default: 127.0.0.1)
# Use "0.0.0.0" to make the server accessible from network
host = "127.0.0.1"
# Server port (default: 5001)
port = 5001
All settings in the .bloggy file are optional. See .bloggy.example for a full example.
Environment Variables
You can also use environment variables as a fallback:
BLOGGY_ROOT: Path to your markdown files (default: current directory)BLOGGY_TITLE: Your blog's title (default: folder name)BLOGGY_HOST: Server host (default: 127.0.0.1)BLOGGY_PORT: Server port (default: 5001)
Examples
Using a .bloggy file:
# Create a .bloggy file in your blog directory
cd /path/to/your/blog
cat > .bloggy << EOF
title = "My Tech Blog"
port = 8000
EOF
bloggy
Using environment variables:
export BLOGGY_ROOT=/path/to/your/markdown/files
export BLOGGY_TITLE="My Awesome Blog"
export BLOGGY_PORT=8000
bloggy
Passing directory as argument:
bloggy /path/to/your/markdown/files
Configuration priority example:
If you have both a .bloggy file with port = 8000 and an environment variable BLOGGY_PORT=9000, the .bloggy file takes precedence and port 8000 will be used.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file bloggy-0.1.6.tar.gz.
File metadata
- Download URL: bloggy-0.1.6.tar.gz
- Upload date:
- Size: 23.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fce0a56e10586749ab131d07162310a228cf55dda54dab03d2ddaa247dac9cef
|
|
| MD5 |
1708ca884ec416c1bc5fff66cbe3ff74
|
|
| BLAKE2b-256 |
a7a628f09f3eec999f9e5c09a872e09999ba666eb3d98125fa1e78ad008a0235
|
File details
Details for the file bloggy-0.1.6-py3-none-any.whl.
File metadata
- Download URL: bloggy-0.1.6-py3-none-any.whl
- Upload date:
- Size: 20.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.10
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
307d3f814c9c04bb96d84dec094c901feef2daec5bbba79c37d0e13defb67929
|
|
| MD5 |
08f3c952cd7102b7f0a1b72cb48cd180
|
|
| BLAKE2b-256 |
27b9bcc3966ccaedb58bd85131e8db554d2f9056b1358bb234a94b2c004f9bf0
|