Readme
Bloggy
A lightweight, elegant blogging platform built with FastHTML that renders Markdown files into beautiful web pages with advanced features.
Architecture Overview
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
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
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
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.