2025-11-14 15:17:15 +10:00
2025-11-12 11:12:29 +10:00
🔧 site url
2025-11-13 15:07:30 +10:00
2025-11-14 15:07:04 +10:00
2025-11-13 15:23:38 +10:00
🐳 docker
2025-11-06 22:30:56 +10:00
2025-11-07 16:37:10 +10:00
2025-11-06 22:08:29 +10:00
2025-11-14 15:07:04 +10:00
2025-11-13 15:23:38 +10:00
🐳 docker
2025-11-06 22:30:56 +10:00
🐳 docker
2025-11-06 22:30:56 +10:00
2025-11-14 15:17:15 +10:00
2025-11-12 10:48:35 +10:00

Sunday Comics - Webcomic Flask App

A Flask-based webcomic website with server-side rendering using Jinja2 templates.

Features

  • Comic viewer with navigation (First, Previous, Next, Latest)
  • Client-side navigation using JSON API (no page reloads)
  • Archive page with thumbnail grid
  • RSS feed support
  • Markdown support for author notes and about page
  • Optional icon-based navigation (comic navigation, header, and social links)
  • Configurable logo and header/footer images
  • Mobile-responsive with optional mobile-specific comic images
  • Full-width and plain (headerless) display modes
  • JSON API for programmatic access
  • Open Graph and Twitter Card metadata for social sharing
  • Server-side rendering with Jinja2

Project Structure

sunday/
├── app.py                    # Main Flask application
├── comics_data.py            # Comic data and configuration
├── requirements.txt          # Python dependencies
├── Dockerfile                # Production Docker image
├── docker-compose.yml        # Docker Compose configuration
├── .dockerignore             # Docker build exclusions
├── scripts/                  # Utility scripts
│   ├── add_comic.py         # Script to add new comic entries
│   └── generate_rss.py      # Script to generate RSS feed
├── content/                  # Markdown content
│   ├── about.md             # About page content
│   └── author_notes/        # Author notes for comics (by date)
├── templates/                # Jinja2 templates
│   ├── base.html            # Base template with navigation
│   ├── index.html           # Latest comic page
│   ├── comic.html           # Individual comic viewer
│   ├── archive.html         # Archive grid
│   ├── page.html            # Generic markdown page template
│   └── 404.html             # Error page
└── static/                  # Static files
    ├── css/
    │   └── style.css        # Main stylesheet
    ├── js/
    │   └── comic-nav.js     # Client-side navigation
    ├── images/              # Image directory
    │   ├── comics/          # Comic images
    │   ├── thumbs/          # Thumbnail images for archive
    │   └── icons/           # Navigation and social icons (optional)
    └── feed.rss             # RSS feed (generated)

Setup

  1. Install dependencies:
pip install -r requirements.txt
  1. Run the application:
python app.py
  1. Visit http://127.0.0.1:3000 in your browser

Environment Variables

The app can be configured via environment variables:

  • SECRET_KEY - Flask secret key (defaults to 'your-secret-key')
  • PORT - Port to run on (defaults to 3000)
  • DEBUG - Enable debug mode (defaults to False)

Development:

export DEBUG=True
python app.py

Production:

export SECRET_KEY="your-secure-random-secret-key"
export PORT=3000
python app.py

Configuration

The comics_data.py file contains both comic data and global configuration options:

Global Settings

COMIC_NAME = 'Sunday Comics'              # Your comic/website name
SITE_URL = 'http://localhost:3000'        # Your domain (update for production)
FULL_WIDTH_DEFAULT = False                # Make all comics full-width by default
PLAIN_DEFAULT = False                     # Hide header/remove borders by default
LOGO_IMAGE = 'logo.png'                   # Path to logo (relative to static/images/)
LOGO_MODE = 'beside'                      # 'beside' or 'replace' title with logo
HEADER_IMAGE = None                       # Optional header image path
FOOTER_IMAGE = None                       # Optional footer image path
COMPACT_FOOTER = False                    # Display footer in compact mode
ARCHIVE_FULL_WIDTH = True                 # Full-width archive with 4 columns
USE_COMIC_NAV_ICONS = True                # Use icons for comic navigation buttons
USE_HEADER_NAV_ICONS = True               # Show icons in main header navigation
USE_FOOTER_SOCIAL_ICONS = True            # Use icons for social links
SOCIAL_INSTAGRAM = None                   # Instagram URL (or None)
SOCIAL_YOUTUBE = None                     # YouTube URL (or None)
SOCIAL_EMAIL = None                       # Email mailto link (or None)
API_SPEC_LINK = None                      # API documentation link (or None)

Adding Comics

Comics are stored in the COMICS list in comics_data.py. Each comic entry:

{
    'number': 1,                           # Comic number (required, sequential)
    'filename': 'comic-001.png',           # Image filename (required)
    'mobile_filename': 'comic-001-mobile.png',  # Optional mobile version
    'date': '2025-01-01',                  # Publication date (required)
    'alt_text': 'Alt text for comic',      # Accessibility text (required)
    'title': 'Comic Title',                # Title (optional, shows #X if absent)
    'author_note': 'Optional note',        # Author note (optional, plain text)
    'full_width': True,                    # Optional: override FULL_WIDTH_DEFAULT
    'plain': True                          # Optional: override PLAIN_DEFAULT
}

Adding a New Comic

Option 1: Use the script (recommended)

# Add comic entry only
python scripts/add_comic.py

# Add comic entry AND create markdown file for author notes
python scripts/add_comic.py -m

This will automatically add a new entry with defaults. The -m flag creates a markdown file in content/author_notes/{date}.md with a template. Then edit comics_data.py to customize.

Option 2: Manual

  1. Save your comic image in static/images/comics/ (e.g., comic-001.png)
  2. Optionally, create a thumbnail in static/images/thumbs/ with the same filename
  3. Add the comic entry to the COMICS list in comics_data.py

Generating RSS Feed

After adding comics, regenerate the RSS feed:

python scripts/generate_rss.py

This creates/updates static/feed.rss

Markdown Support

Author Notes: Create markdown files in content/author_notes/{date}.md (e.g., 2025-01-01.md) for rich-formatted author notes. Markdown author notes take precedence over the author_note field in comics_data.py and render as HTML.

Example:

# Create author note for a specific comic
echo "# My First Comic\n\nThis is **bold** text!" > content/author_notes/2025-01-01.md

About Page: The /about route renders content/about.md as HTML. Edit this file to customize your about page with markdown formatting.

Note: Client-side navigation displays author notes as plain text. Markdown author notes only render properly on initial page load (server-side rendering). For full markdown rendering, users need to refresh the page or navigate directly to the comic URL.

Production Deployment

For production, you should NOT use Flask's built-in development server. Choose one of the following deployment methods:

1. Generate a secure secret key:

python -c "import secrets; print(secrets.token_hex(32))"

2. Create a .env file:

SECRET_KEY=your-generated-secret-key-here

3. Build and run with Docker Compose:

docker-compose up -d

Or build and run manually:

# Build the image
docker build -t sunday-comics .

# Run the container
docker run -d \
  -p 3000:3000 \
  -e SECRET_KEY="your-secret-key" \
  -v $(pwd)/comics_data.py:/app/comics_data.py:ro \
  -v $(pwd)/static/images:/app/static/images:ro \
  --name sunday-comics \
  sunday-comics

View logs:

docker-compose logs -f

Option 2: Manual Deployment with Gunicorn

1. Generate a Secure Secret Key

python -c "import secrets; print(secrets.token_hex(32))"

2. Set Environment Variables

export SECRET_KEY="generated-secret-key-from-above"
export DEBUG=False
export PORT=3000

3. Use a Production WSGI Server

Install Gunicorn:

pip install gunicorn

Run with Gunicorn:

gunicorn app:app --bind 0.0.0.0:3000 --workers 4

Set up Nginx or another reverse proxy in front of your app for:

  • HTTPS/SSL termination
  • Static file serving
  • Load balancing
  • Better security

Additional Production Considerations

  • Use a process manager (systemd, supervisor) for non-Docker deployments
  • Set appropriate file permissions
  • Enable HTTPS with Let's Encrypt
  • Consider using a CDN for static assets
  • Monitor logs and performance
  • Set up automated backups of comics_data.py

Upgrading to a Database

For larger comic archives, consider replacing the COMICS list with a database:

  • SQLite for simple setups
  • PostgreSQL/MySQL for production
  • Use Flask-SQLAlchemy for ORM support

Customization

Branding

  • Update COMIC_NAME in comics_data.py to change your comic's name
  • Update SITE_URL in comics_data.py for production deployment
  • Customize logo by setting LOGO_IMAGE and LOGO_MODE

Styling

  • Modify static/css/style.css to change colors, fonts, and layout
  • Current color scheme uses blue (#3498db) and dark blue-gray (#2c3e50)

About Page

  • Edit content/about.md to add your bio and comic information (supports markdown)

Icon Navigation

To use icon navigation:

  1. Set USE_COMIC_NAV_ICONS = True in comics_data.py
  2. Add icons to static/images/icons/:
    • Comic navigation: first.png, previous.png, next.png, latest.png
    • Header navigation: alert.png, archive.png, info.png
    • Social links: instagram.png, youtube.png, mail.png

Configure social media links in comics_data.py:

SOCIAL_INSTAGRAM = 'https://instagram.com/yourhandle'
SOCIAL_YOUTUBE = 'https://youtube.com/@yourchannel'
SOCIAL_EMAIL = 'mailto:your@email.com'

Pages

  • / - Shows the latest comic
  • /comic/<id> - View a specific comic by number
  • /archive - Browse all comics in a grid
  • /about - About the comic and author
  • /static/feed.rss - RSS feed

API Endpoints

The app exposes a JSON API for programmatic access:

  • GET /api/comics - Returns all comics as a JSON array

    [
      {
        "number": 1,
        "title": "First Comic",
        "filename": "comic-001.png",
        "date": "2025-01-01",
        "alt_text": "The very first comic",
        "author_note": "This is where your comic journey begins!"
      }
    ]
    
  • GET /api/comics/<id> - Returns a specific comic as JSON

    {
      "number": 1,
      "title": "First Comic",
      "filename": "comic-001.png",
      "date": "2025-01-01",
      "alt_text": "The very first comic",
      "author_note": "This is where your comic journey begins!"
    }
    

    Returns 404 with {"error": "Comic not found"} if the comic doesn't exist.

Credits

License

[Add your license here]

Description
Simple, stylish, and snappy template for hosting comics.
Readme MIT 2.2 MiB
Languages
Python 31.7%
HTML 23.7%
JavaScript 23.5%
CSS 20.3%
Dockerfile 0.8%