| .devcontainer | ||
| .github/workflows | ||
| .vscode | ||
| assets | ||
| dist | ||
| fixtures | ||
| lib/vite | ||
| members | ||
| partials | ||
| scripts | ||
| .editorconfig | ||
| .env.template | ||
| .gitignore | ||
| author.hbs | ||
| biome.json | ||
| components.json | ||
| default.hbs | ||
| DESIGN.md | ||
| error.hbs | ||
| index.hbs | ||
| LICENSE | ||
| MIGRATION.md | ||
| ORIGINAL_README.md | ||
| package-lock.json | ||
| package.json | ||
| page-accessibility.hbs | ||
| page-calendar.hbs | ||
| page-chapter-bodies.hbs | ||
| page-code-of-conduct.hbs | ||
| page-having-trouble.hbs | ||
| page-join.hbs | ||
| page-news.hbs | ||
| page-priorities.hbs | ||
| page-who-we-are.hbs | ||
| page.hbs | ||
| post.hbs | ||
| README.md | ||
| renovate.json | ||
| tag.hbs | ||
| tsconfig.json | ||
| vite.config.js | ||
DSA SF Ghost Theme
Overview
A Ghost theme for DSA San Francisco built with Vite, React Islands, and Tailwind CSS.
Basic tech stack
- Ghost CMS: Server-side rendering using Handlebars templates.
- Vite & React: Fast asset bundling and island architecture for interactive components embedded in static HTML.
- Tailwind CSS: Utility-first styling configured with a custom Neobrutalism design system (see Design).
- Python: Automation scripts for seeding content and configuring the instance.
- DevPod: Containerized development workflow.
Branches
main: Current development branch with React Islands integration and full Neobrutalism design system.clean-vapour-dev: Pre-React baseline. This branch contains the original Vapour theme (Handlebars-only) with verified DevPod support and automated testing. Use this as a clean starting point for developers or to test the base theme without React complexity.
Design
This theme uses a Neobrutalist aesthetic with components from neobrutalism.dev. See DESIGN.md for grid layout, color variables, and component guidelines.
Changes
Here's how to make changes to various parts of the content in the theme. In general, this follows the conventions from Ghost, e.g. various pages are templated using Handlebars with the automatic naming conventions outlined in the Ghost documentation.
Styling changes
The styling is handled by Tailwind CSS. The configuration is in vite.config.js. The design system is based on neobrutalism.dev. See DESIGN.md for further details about conventions.
Navigation
The theme supports nested menus in the navigation bar. To create a nested menu item, use the Ghost Admin navigation settings and prefix the label with a dash and a space (- ). The item will be rendered as a child of the preceding top-level item.
Example:
- Resources
-
- Technology
-
- Wiki
There is a working example of this configuration in the fixtures, which is applied by the setup script (scripts/setup_ghost.py).
Calendar
The calendar page displays a calendar built with FullCalendar. It uses the Google Calendar plugin to fetch events from a Google Calendar. The calendar ID is set in the .env file, along with the Google Calendar API key. These can also be set in the Ghost admin panel under the custom theme settings variables.
Who We Are page
The template for the "Who We Are" page is set in page-who-we-are.hbs. The copy is hard-coded in that template. To edit it, simply look at that file and change the text. You don't need to modify anything else.
News
The template for the news page is set in page-news.hbs. It is filtered to display only posts with the tag news. The most recent post is displayed in on the main page.
Development setup
Quick start with DevPod (Recommended)
DevPod creates reproducible development environments with robust setup, automatic file syncing, and health checks. Theme changes auto-sync to Ghost.
Architecture
Two-container setup:
- Ghost container: Runs Ghost CMS 6.13.2 with theme files auto-synced from workspace
- App container: Runs Vite dev server for hot-reloading React/CSS assets
1. Install DevPod
First, install DevPod. You'll want to set Docker as the backend.
Install Docker (if needed):
- Docker Desktop
- On macOS, consider using OrbStack as an alternative
2. Configure Docker backend
devpod provider add docker
devpod provider use docker
3. Start DevPod
devpod up .
What happens:
- Builds custom Ghost image with rsync and file watcher
- Starts Ghost with theme directory pre-synced
- Waits for Ghost health check (Admin API ready)
- Runs fixture setup automatically (first run only)
- Starts Vite dev server
Startup time: ~1-2 minutes on first run, ~30 seconds on subsequent starts
4. Access the application
- Site:
http://localhost:2368 - Vite dev server:
http://localhost:5173 - Admin:
http://localhost:2368/ghost - Default credentials:
admin@example.com/Th1sIsAStr0ng!Pass
5. Development workflow
Editing files:
- React/CSS: Auto hot-reload via Vite
- Handlebars templates: File watcher syncs → browser refresh
- New templates: File watcher syncs → restart Ghost
Available commands (from inside devcontainer):
npm run setup:ghost # Re-run fixture setup (if needed)
npm run dev # Start Vite dev server (runs automatically)
npm run build # Build for production
npm run test # Validate theme with gscan
Available commands (from host machine):
# Restart Ghost container (after adding new templates)
docker compose -f .devcontainer/docker-compose.yml restart ghost
# View Ghost logs (watch file sync activity)
docker compose -f .devcontainer/docker-compose.yml logs -f ghost
# Access Ghost container shell
docker compose -f .devcontainer/docker-compose.yml exec ghost sh
Resetting the environment:
From host machine:
# Stop workspace
devpod stop <workspace-name>
# Delete everything (including database and volumes)
devpod delete <workspace-name>
# Start fresh
devpod up .
6. Troubleshooting
Ghost won't start:
# Check Ghost logs
docker compose -f .devcontainer/docker-compose.yml logs ghost
Theme not appearing:
# Check theme files synced
docker compose -f .devcontainer/docker-compose.yml exec ghost ls /var/lib/ghost/content/themes/dsasf-ghost-theme/
# Restart Ghost
docker compose -f .devcontainer/docker-compose.yml restart ghost
File watcher not working:
# Check watcher process
docker compose -f .devcontainer/docker-compose.yml exec ghost ps aux | grep inotify
# Check watcher logs
docker compose -f .devcontainer/docker-compose.yml logs ghost | grep "Watching"
Changes not syncing:
# Make a test change and watch logs
docker compose -f .devcontainer/docker-compose.yml logs -f ghost
# Expected: "Detected change: modify file.hbs in /workspace" → "Theme synced"
Clean slate:
devpod delete .
devpod up .
7. (Optional) Configure IDE
You can have DevPod spin up an IDE that's already open inside of the container. See the DevPod IDE documentation for details.
Manual setup (Not recommended)
Prerequisites:
- Node 20+
- Ghost CLI:
npm install -g ghost-cli
Steps:
-
Install Ghost locally:
ghost install local -
Install dependencies and start dev server:
npm install npm run dev # Vite dev server on :5173 -
Link theme to Ghost:
ln -s $(pwd) /path/to/ghost/content/themes/dsasf-ghost-theme -
Restart Ghost and activate the theme in admin.
Fixtures
The scripts/setup_ghost.py script seeds content from fixtures/ directory. It runs automatically on first DevPod startup.
Re-running manually (if needed):
npm run setup:ghost
What it does:
- Creates posts from
fixtures/posts/*.json - Creates pages from
fixtures/pages/*.json - Configures site settings, navigation, and theme options
- Uploads logo/icon assets
- Activates the theme
Run for remote instances:
Step 1 - Configure environment:
Copy .env.template to .env and set your Ghost URL and credentials:
GHOST_URL=https://yourdomain.com
GHOST_ADMIN_EMAIL=admin@example.com
GHOST_ADMIN_PASSWORD=yourpassword
GOOGLE_CALENDAR_API_KEY=your_api_key_here
GOOGLE_CALENDAR_ID=your-calendar@group.calendar.google.com
Note: The calendar feature requires valid Google Calendar API credentials. Without them, the calendar page won't load events.
Step 2 - Run the script:
From your local machine:
export $(cat .env | xargs) && python3 scripts/setup_ghost.py
Building
npm run build # Outputs to assets/built/
Deployment
Push to main triggers automatic deployment via GitHub Actions.