Skip to main content

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

  1. Create a directory with your markdown files:

    mkdir my-blog
    cd my-blog
    echo "# Hello World" > hello.md
    
  2. Run Bloggy:

    bloggy .
    
  3. Open your browser at http://127.0.0.1:5001

Configuration

Bloggy supports three ways to configure your blog (in priority order):

  1. .bloggy configuration file (TOML format)
  2. Environment variables
  3. 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

bloggy-0.1.8.tar.gz (24.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

bloggy-0.1.8-py3-none-any.whl (20.2 kB view details)

Uploaded Python 3

File details

Details for the file bloggy-0.1.8.tar.gz.

File metadata

  • Download URL: bloggy-0.1.8.tar.gz
  • Upload date:
  • Size: 24.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for bloggy-0.1.8.tar.gz
Algorithm Hash digest
SHA256 64785fe1b8589bb22122594bb1444c083ee0045ba8a1418c85f41d60bd715189
MD5 23184690e6666a37b359681e5bc1237e
BLAKE2b-256 a390838a30ada2bda0861ebb08012ff3f566d42902c8d586cc2293f1c88883a2

See more details on using hashes here.

File details

Details for the file bloggy-0.1.8-py3-none-any.whl.

File metadata

  • Download URL: bloggy-0.1.8-py3-none-any.whl
  • Upload date:
  • Size: 20.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.10

File hashes

Hashes for bloggy-0.1.8-py3-none-any.whl
Algorithm Hash digest
SHA256 0dc54104706b2868da7eb74a685631f21a693a2aa84315694865fd575d6a8d0b
MD5 63933c62af86ed83a1ff5e523520ebeb
BLAKE2b-256 ee3ce60ecc899950b7d41c339a630cf872c79dff468fe5befb67523b2d0ea88c

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page