qsgen3/THEMES-HOWTO.md
Stig-Ørjan Smelror 81ffa53d70 Add comprehensive theme documentation and improve migration script
- Add THEMES-HOWTO.md: Complete guide for creating and customizing themes
- Remove theme sections from how-it-works.md to avoid duplication
- Update migration script to place all blog posts in single directory
- Streamline documentation structure for better organization
2025-05-31 03:00:50 +02:00

823 lines
19 KiB
Markdown

# qsgen3 Themes How-To Guide
This guide explains how to create and customize themes for qsgen3, based on how the code actually interprets and processes themes.
## Table of Contents
1. [Theme Basics](#theme-basics)
2. [Theme Directory Structure](#theme-directory-structure)
3. [Creating Your First Theme](#creating-your-first-theme)
4. [Layout Templates](#layout-templates)
5. [Static Assets (CSS, JS, Images)](#static-assets-css-js-images)
6. [Theme Configuration](#theme-configuration)
7. [Advanced Theme Features](#advanced-theme-features)
8. [Theme Best Practices](#theme-best-practices)
9. [Troubleshooting](#troubleshooting)
## Theme Basics
### What is a Theme?
A qsgen3 theme is a collection of layout templates and static assets (CSS, JavaScript, images) that define the visual appearance and structure of your generated website. Themes allow you to:
- Customize the HTML structure of your pages
- Apply custom CSS styling
- Include JavaScript functionality
- Override default layouts with theme-specific designs
### How qsgen3 Processes Themes
When you specify a theme in your `site.conf`, qsgen3 follows this processing order:
1. **Theme Detection**: Looks for the theme directory at `themes/{theme_name}/`
2. **Layout Override**: If the theme has a `layouts/` directory, it replaces the default layouts
3. **Static File Copying**: Copies static files from both root `static/` and theme `static/` directories
4. **CSS Linking**: Automatically links the theme's main CSS file in generated HTML
## Theme Directory Structure
### Standard Theme Structure
```
themes/
└── your-theme-name/
├── layouts/ # Optional: Custom layout templates
│ ├── index.html # Homepage layout
│ ├── post.html # Blog post layout
│ ├── page.html # Static page layout
│ └── rss.xml # RSS feed template
└── static/ # Theme's static assets
├── css/
│ └── style.css # Main theme CSS
├── js/
│ └── theme.js # Theme JavaScript
└── images/
└── logo.png # Theme images
```
### Alternative Structure (Legacy Support)
For themes that don't use the `static/` subdirectory:
```
themes/
└── your-theme-name/
├── layouts/ # Optional: Custom layout templates
├── css/ # CSS files directly in theme root
│ └── style.css
├── js/ # JavaScript files
└── images/ # Image files
```
## Creating Your First Theme
### Step 1: Create the Theme Directory
```bash
mkdir -p themes/my-theme/static/css
mkdir -p themes/my-theme/static/js
mkdir -p themes/my-theme/layouts
```
### Step 2: Create a Basic CSS File
Create `themes/my-theme/static/css/style.css`:
```css
/* Basic theme styles */
body {
font-family: 'Arial', sans-serif;
line-height: 1.6;
margin: 0;
padding: 0;
background-color: #f4f4f4;
}
header {
background: #333;
color: white;
padding: 1rem;
text-align: center;
}
header h1 a {
color: white;
text-decoration: none;
}
main {
max-width: 800px;
margin: 2rem auto;
padding: 0 1rem;
background: white;
border-radius: 8px;
box-shadow: 0 2px 5px rgba(0,0,0,0.1);
}
article {
padding: 2rem;
}
footer {
text-align: center;
padding: 1rem;
background: #333;
color: white;
margin-top: 2rem;
}
```
### Step 3: Configure Your Site
Update your `site.conf`:
```bash
# Theme configuration
site_theme="my-theme"
site_theme_css_file="css/style.css"
```
### Step 4: Test Your Theme
```bash
./bin/qsgen3
```
Your site should now use your custom theme!
## Layout Templates
### Understanding Pandoc Templates
qsgen3 uses Pandoc templates with special variable syntax:
- `$variable$` - Simple variable substitution
- `$if(variable)$...$endif$` - Conditional blocks
- `$for(list)$...$endfor$` - Loop over lists
- `$body$` - The main content (converted from Markdown)
### Available Variables
#### Site-wide Variables (from site.conf)
- `$site_name$` - Your site's name
- `$site_tagline$` - Your site's tagline
- `$site_url$` - Your site's URL
- `$current_year$` - Current year (auto-generated)
#### Content Variables (from Markdown frontmatter)
- `$title$` - Page/post title
- `$author$` - Content author
- `$date$` - Publication date
- `$description$` - Page description
- `$body$` - The converted Markdown content
#### Special Variables
- `$css$` - CSS file paths (handled automatically)
- `$math$` - Math rendering support (if enabled)
### Creating Custom Layouts
#### Basic Post Layout (`layouts/post.html`)
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>$site_name$ - $title$</title>
<meta name="author" content="$author$">
<meta name="description" content="$description$">
$if(date)$<meta name="date" content="$date$">$endif$
$for(css)$
<link rel="stylesheet" href="$css$">
$endfor$
</head>
<body>
<header>
<h1><a href="/">$site_name$</a></h1>
<p>$site_tagline$</p>
</header>
<main>
<article>
<header>
<h1>$title$</h1>
$if(author)$<p class="author">By: $author$</p>$endif$
$if(date)$<p class="date">Published: $date$</p>$endif$
</header>
$body$
</article>
</main>
<footer>
<p>&copy; $current_year$ $site_name$. Generated by qsgen3.</p>
</footer>
</body>
</html>
```
#### Index Page Layout (`layouts/index.html`)
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>$site_name$ - $site_tagline$</title>
<meta name="description" content="$site_tagline$">
$for(css)$
<link rel="stylesheet" href="$css$">
$endfor$
</head>
<body>
<header>
<h1>$site_name$</h1>
<p>$site_tagline$</p>
</header>
<main>
$body$
</main>
<footer>
<p>&copy; $current_year$ $site_name$. Generated by qsgen3.</p>
</footer>
</body>
</html>
```
## Static Assets (CSS, JS, Images)
### How Static Files Are Processed
qsgen3 copies static files in this order:
1. **Root Static Files**: Copies from `static/` to `output/static/`
2. **Theme Static Files**: Copies from `themes/{theme}/static/` to `output/static/` (overwrites root files)
This means theme files take precedence over root static files.
### CSS File Linking
The main theme CSS file is automatically linked in all generated HTML pages using the `--css` flag passed to Pandoc. The CSS path is determined by:
1. `site_theme_css_file` setting in `site.conf` (recommended)
2. `site_theme_css_path` setting (legacy, deprecated)
Example configuration:
```bash
site_theme="my-theme"
site_theme_css_file="css/style.css" # Relative to theme's static/ directory
```
This results in:
- CSS file copied to: `output/static/css/style.css`
- HTML links to: `/static/css/style.css`
### Adding Additional Assets
#### JavaScript Files
Create `themes/my-theme/static/js/theme.js`:
```javascript
// Theme-specific JavaScript
document.addEventListener('DOMContentLoaded', function() {
console.log('My theme loaded!');
// Add smooth scrolling
document.querySelectorAll('a[href^="#"]').forEach(anchor => {
anchor.addEventListener('click', function (e) {
e.preventDefault();
document.querySelector(this.getAttribute('href')).scrollIntoView({
behavior: 'smooth'
});
});
});
});
```
Include in your layout:
```html
<script src="/static/js/theme.js"></script>
```
#### Images and Other Assets
Place images in `themes/my-theme/static/images/` and reference them in your CSS or templates:
```css
.logo {
background-image: url('/static/images/logo.png');
}
```
```html
<img src="/static/images/hero.jpg" alt="Hero image">
```
## Theme Configuration
### Required Configuration
In your `site.conf`:
```bash
# Minimum theme configuration
site_theme="theme-name" # Name of theme directory
site_theme_css_file="css/main.css" # Path to main CSS file
```
### Optional Configuration
```bash
# Optional: Override default paths
paths_layouts_dir="layouts" # Will be overridden if theme has layouts/
paths_static_dir="static" # Root static directory
paths_output_dir="output" # Output directory
```
### Theme-Specific Variables
You can add custom variables to your `site.conf` and use them in templates:
```bash
# Custom theme variables
theme_color_primary="#3498db"
theme_color_secondary="#2c3e50"
theme_font_family="'Roboto', sans-serif"
```
Use in templates:
```html
<style>
:root {
--primary-color: $theme_color_primary$;
--secondary-color: $theme_color_secondary$;
--font-family: $theme_font_family$;
}
</style>
```
## Advanced Theme Features
### Responsive Design
Create responsive themes using CSS media queries:
```css
/* Mobile-first approach */
.container {
max-width: 100%;
padding: 1rem;
}
@media (min-width: 768px) {
.container {
max-width: 750px;
margin: 0 auto;
}
}
@media (min-width: 1024px) {
.container {
max-width: 970px;
}
}
```
### Dark Mode Support
```css
/* Default (light) theme */
:root {
--bg-color: #ffffff;
--text-color: #333333;
--accent-color: #3498db;
}
/* Dark mode */
@media (prefers-color-scheme: dark) {
:root {
--bg-color: #1a1a1a;
--text-color: #e0e0e0;
--accent-color: #5dade2;
}
}
body {
background-color: var(--bg-color);
color: var(--text-color);
}
```
### Typography and Web Fonts
```css
/* Import Google Fonts */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap');
body {
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
font-weight: 400;
line-height: 1.6;
}
h1, h2, h3, h4, h5, h6 {
font-weight: 600;
line-height: 1.2;
}
```
### Custom RSS Template
Create `layouts/rss.xml` for custom RSS styling:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title>$site_name$</title>
<description>$site_tagline$</description>
<link>$site_url$</link>
<atom:link href="$site_url$/rss.xml" rel="self" type="application/rss+xml"/>
<language>en-us</language>
<lastBuildDate>$build_date$</lastBuildDate>
$for(posts)$
<item>
<title>$it.post_title$</title>
<description>$it.post_description$</description>
<link>$it.post_url$</link>
<guid>$it.post_url$</guid>
<pubDate>$it.post_date_rfc$</pubDate>
</item>
$endfor$
</channel>
</rss>
```
## Theme Best Practices
### 1. Design Principles
- **Mobile-First**: Design for mobile devices first, then enhance for larger screens
- **Accessibility**: Use semantic HTML, proper contrast ratios, and keyboard navigation
- **Performance**: Optimize images, minimize CSS/JS, use efficient selectors
- **Consistency**: Maintain consistent spacing, typography, and color schemes
### 2. File Organization
```
themes/my-theme/
├── static/
│ ├── css/
│ │ ├── main.css # Main theme styles
│ │ ├── components.css # Component-specific styles
│ │ └── utilities.css # Utility classes
│ ├── js/
│ │ ├── theme.js # Main theme JavaScript
│ │ └── components/ # Component-specific JS
│ └── images/
│ ├── icons/ # Icon files
│ └── backgrounds/ # Background images
└── layouts/
├── index.html # Homepage layout
├── post.html # Blog post layout
├── page.html # Static page layout
└── rss.xml # RSS feed template
```
### 3. CSS Architecture
Use a modular approach:
```css
/* main.css */
@import url('base.css'); /* Reset and base styles */
@import url('layout.css'); /* Layout components */
@import url('components.css'); /* UI components */
@import url('utilities.css'); /* Utility classes */
```
### 4. Performance Optimization
- **Minimize HTTP Requests**: Combine CSS/JS files when possible
- **Optimize Images**: Use appropriate formats (WebP, SVG) and sizes
- **Use CSS Custom Properties**: For maintainable theming
- **Lazy Load**: Implement lazy loading for images and non-critical resources
### 5. Browser Compatibility
Test your theme across different browsers and devices:
```css
/* Provide fallbacks for modern CSS features */
.card {
background: #ffffff; /* Fallback */
background: var(--card-bg, #ffffff); /* Custom property */
}
.grid {
display: block; /* Fallback */
display: grid; /* Modern */
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
}
```
## Troubleshooting
### Common Issues
#### 1. CSS Not Loading
**Problem**: Your theme's CSS isn't being applied.
**Solutions**:
- Check that `site_theme_css_file` path is correct relative to theme's `static/` directory
- Verify the CSS file exists at `themes/{theme}/static/{css_path}`
- Check qsgen3 logs for CSS-related warnings
- Ensure the theme directory name matches `site_theme` setting
#### 2. Layouts Not Being Used
**Problem**: Your custom layouts aren't being applied.
**Solutions**:
- Verify layouts exist in `themes/{theme}/layouts/`
- Check that layout filenames match expected names (`index.html`, `post.html`, `page.html`)
- Review qsgen3 logs for layout-related messages
#### 3. Static Files Not Copying
**Problem**: Images, JS, or other static files aren't appearing in output.
**Solutions**:
- Check that files are in `themes/{theme}/static/` directory
- Verify file permissions are readable
- Look for rsync/cp errors in qsgen3 logs
- Ensure no .gitignore rules are excluding files
#### 4. Template Variables Not Working
**Problem**: Variables like `$site_name$` aren't being substituted.
**Solutions**:
- Check that variables are defined in `site.conf`
- Verify variable names match exactly (case-sensitive)
- Ensure Pandoc template syntax is correct
- Test with a minimal template to isolate issues
### Debugging Tips
#### 1. Enable Debug Logging
Run qsgen3 with verbose output to see detailed processing information:
```bash
# Enable debug logging (if supported by your qsgen3 version)
QSG_LOG_LEVEL=DEBUG ./bin/qsgen3
```
#### 2. Test with Minimal Theme
Create a minimal theme to isolate issues:
```html
<!-- minimal-test.html -->
<!DOCTYPE html>
<html>
<head>
<title>$title$</title>
<style>body { font-family: Arial; margin: 2rem; }</style>
</head>
<body>
<h1>$title$</h1>
$body$
</body>
</html>
```
#### 3. Validate Generated HTML
Check that your generated HTML is valid:
```bash
# Use HTML validator tools
htmlhint output/*.html
# or
w3c-validator output/index.html
```
#### 4. Check File Permissions
Ensure qsgen3 can read your theme files:
```bash
find themes/my-theme -type f -not -perm -644
find themes/my-theme -type d -not -perm -755
```
### Getting Help
If you encounter issues not covered here:
1. Check the qsgen3 documentation and examples
2. Review existing themes in the `themes/` directory
3. Examine the qsgen3 source code for theme processing logic
4. Create a minimal reproduction case
5. Report issues with detailed logs and configuration
---
## Example: Complete Theme Creation
Here's a complete example of creating a modern, responsive theme called "modern-blog":
### 1. Create Directory Structure
```bash
mkdir -p themes/modern-blog/{layouts,static/{css,js,images}}
```
### 2. Create Main CSS (`themes/modern-blog/static/css/style.css`)
```css
/* Modern Blog Theme */
:root {
--primary-color: #2563eb;
--secondary-color: #64748b;
--background-color: #ffffff;
--text-color: #1e293b;
--border-color: #e2e8f0;
--shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1);
}
* {
box-sizing: border-box;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
line-height: 1.6;
color: var(--text-color);
background-color: var(--background-color);
margin: 0;
padding: 0;
}
.container {
max-width: 800px;
margin: 0 auto;
padding: 0 1rem;
}
header {
background: var(--background-color);
border-bottom: 1px solid var(--border-color);
padding: 2rem 0;
}
header h1 {
margin: 0;
font-size: 2rem;
font-weight: 700;
}
header h1 a {
color: var(--text-color);
text-decoration: none;
}
header p {
margin: 0.5rem 0 0 0;
color: var(--secondary-color);
}
main {
padding: 3rem 0;
}
article {
background: var(--background-color);
border-radius: 8px;
box-shadow: var(--shadow);
padding: 2rem;
margin-bottom: 2rem;
}
article header h1 {
margin: 0 0 1rem 0;
color: var(--primary-color);
}
.meta {
color: var(--secondary-color);
font-size: 0.9rem;
margin-bottom: 1.5rem;
}
.meta .author,
.meta .date {
display: inline-block;
margin-right: 1rem;
}
footer {
background: var(--text-color);
color: var(--background-color);
text-align: center;
padding: 2rem 0;
margin-top: 3rem;
}
footer a {
color: var(--primary-color);
}
/* Responsive design */
@media (max-width: 768px) {
.container {
padding: 0 0.5rem;
}
header {
padding: 1rem 0;
}
main {
padding: 1.5rem 0;
}
article {
padding: 1.5rem;
margin-bottom: 1rem;
}
}
```
### 3. Create Post Layout (`themes/modern-blog/layouts/post.html`)
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>$site_name$ - $title$</title>
<meta name="author" content="$author$">
<meta name="description" content="$description$">
$if(date)$<meta name="date" content="$date$">$endif$
$for(css)$
<link rel="stylesheet" href="$css$">
$endfor$
</head>
<body>
<header>
<div class="container">
<h1><a href="/">$site_name$</a></h1>
<p>$site_tagline$</p>
</div>
</header>
<main>
<div class="container">
<article>
<header>
<h1>$title$</h1>
<div class="meta">
$if(author)$<span class="author">By: $author$</span>$endif$
$if(date)$<span class="date">Published: $date$</span>$endif$
</div>
</header>
$body$
</article>
</div>
</main>
<footer>
<div class="container">
<p>&copy; $current_year$ $site_name$. Generated by qsgen3.</p>
<p><a href="$site_url$">$site_url$</a></p>
</div>
</footer>
</body>
</html>
```
### 4. Configure Site (`site.conf`)
```bash
site_theme="modern-blog"
site_theme_css_file="css/style.css"
```
### 5. Generate Site
```bash
./bin/qsgen3
```
Your modern, responsive blog theme is now ready!
---
This guide covers everything you need to know about creating themes for qsgen3. Remember that themes are processed in a specific order, and understanding this processing flow is key to creating effective themes that work reliably across different configurations.