Ghost v6 Theme for SREDevOps.org — Multi-locale, Tailwind CSS v3, responsive, dark-mode first and tag-based language filtering.
- Fix primary menu navigation, mobile views and small screens doesn't render correctly
- Improve responsive design for secondary navigation menu
- Create login and search forms for the main navigation menu, as optional features (Ghost Docs: https://docs.ghost.org/themes/custom-settings)
This theme implements a template inheritance + tag-based routing strategy to serve distinct content per locale without requiring separate Ghost instances. This approach aligns with community workarounds discussed in the Ghost Forum.
┌─────────────────────────────────────┐
│ routes.yaml │
│ • /en/* → home-en.hbs │
│ • /es/* → home-es.hbs │
│ • /br/* → home-br.hbs │
└─────────┬───────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ home-*.hbs (collection template) │
│ • Defines collection query/filter │
│ • Renders post list via partials │
│ • {{!< default-*.hbs}} inheritance │
└─────────┬───────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ custom-*.hbs (post/page template) │
│ • Locale-specific post layout │
│ • Localized metadata, TOC, comments │
│ • {{!< default-*.hbs}} inheritance │
└─────────┬───────────────────────────┘
│
▼
┌─────────────────────────────────────┐
│ default-*.hbs (layout shell) │
│ • <html lang="*"> attribute │
│ • Common <head>, assets, footer │
│ • {{{body}}} injection point │
└─────────────────────────────────────┘
💡 Key Insight: Each locale uses its own
default-*.hbslayout shell to ensure properlangattributes, meta tags, and localized UI strings. The{{{body}}}Handlebars placeholder indefault-*.hbsreceives the rendered output fromcustom-*.hbsorhome-*.hbs.
| Locale | URL Pattern | Required Tags | Exclusion Tags | Layout Shell |
|---|---|---|---|---|
| English (default) | / or /en/{slug}/ |
en, hash-en |
-es, -br |
default.hbs |
| Spanish | /es/{slug}/ |
es, hash-es |
-en, -br |
default-es.hbs |
| Portuguese (BR) | /br/{slug}/ |
br, hash-br |
-en, -es |
default-br.hbs |
⚠️ Critical: The default locale (English) is configured in Ghost Admin → Settings → General → Publication language. All root-level routes (/,/page/2/, etc.) serve English content unless explicitly routed otherwise.
collections:
/es/:
template: home-es
permalink: /es/{slug}/
filter: tag:es+tag:-en+tag:-br
data: tag.es
/en/:
template: home-en
permalink: /en/{slug}/
filter: tag:en+tag:-es+tag:-br
data: tag.en
/br/:
template: home-br
permalink: /br/{slug}/
filter: tag:br+tag:-es+tag:-en
data: tag.br
# Fallback: English as default locale (configured in Ghost Admin)
taxonomies:
tag: /tag/{slug}/
author: /author/{slug}/✅ Why this works: Ghost's filter syntax supports boolean logic (+ for AND, - for NOT), enabling precise content segregation per locale while maintaining a single content database. The template directive ensures each collection uses its locale-specific layout chain.
- Prerequisites
- Installation
- Development Workflow
- Locale Content Authoring
- Template Architecture
- Theme Configuration
- Testing & Validation
- Deployment
- Contributing
- License
| Dependency | Version | Purpose |
|---|---|---|
| Node.js | >=22 |
Runtime for build tooling |
| Yarn | >=1.22 |
Package management (preferred over npm) |
| Ghost | >=6.0 |
Local development server |
| Docker (optional) | Latest | Run Ghost via official container |
🐳 Ghost Local Setup: Follow the official Docker guide for a reproducible dev environment.
# Clone the repository
git clone https://github.com/sredevopsorg/sredevopsorg-ghost-theme.git
cd sredevopsorg-ghost-theme
# Install dependencies (Yarn required)
yarn install-
Start your local Ghost instance:
# If using Docker Compose (recommended) docker compose up -d # Or via Ghost-CLI ghost start
-
Upload
routes.yamlto Ghost Admin → Settings → Routing -
Upload the theme:
-
Via Admin: Settings → Design → Upload theme
-
Or symlink for development:
ln -s /path/to/sredevopsorg-ghost-theme \ /path/to/ghost/content/themes/sredevopsorg-ghost-theme
-
-
Critical: Set your default locale in Ghost Admin → Settings → General → Publication language (e.g.,
enfor English). This determines which templates serve root-level routes. -
Activate the theme in Ghost Admin → Design
yarn devThis triggers:
- Tailwind CSS compilation with
@tailwindcss/formsand@tailwindcss/typography - Asset bundling via Gulp
- LiveReload for template/CSS changes
🔁 Hot reload is enabled for
.hbs,.css, and.jsfiles. Browser refreshes automatically on save.
When creating content in Ghost Admin or via Markdown import:
---
title: "My Post Title"
slug: "my-post-slug"
tags:
- en # Primary language slug (required)
- hash-en # Required for filter consistency
- Kubernetes # Topic tags
- SRE
---
en/es/br or its hash-* counterpart will cause the post to not appear in locale-specific collections due to the filter logic in routes.yaml.
graph LR
A[Request: /es/my-post/] --> B[routes.yaml filter]
B --> C{tag:es AND NOT en/br?}
C -->|Yes| D[home-es.hbs]
D --> E[Inherits default-es.hbs]
E --> F[Render html lang='es']
F --> G[Inject body from custom-es.hbs]
-
Copy the base template:
cp default.hbs default-es.hbs cp custom.hbs custom-es.hbs cp home.hbs home-es.hbs
-
Update the layout shell (
default-es.hbs): -
Customize content templates (
custom-es.hbs):- Localize UI strings ("Autor", "Publicado", "Índice")
- Adjust date formats (
{{date published_at format="DD MMM YYYY"}}) - Conditionally render locale-specific components
| File | Role | Inheritance | Locale Scope |
|---|---|---|---|
default.hbs |
Base HTML shell for English | None (root) | English (default) |
default-es.hbs |
Base HTML shell for Spanish | None (root) | Spanish |
default-br.hbs |
Base HTML shell for Portuguese | None (root) | Portuguese (BR) |
custom.hbs |
Post/page content layout | {{!< default}} |
English |
custom-es.hbs |
Post/page content layout | {{!< default-es}} |
Spanish |
home.hbs |
Collection/listing template | {{!< default}} |
English |
home-es.hbs |
Collection/listing template | {{!< default-es}} |
Spanish |
post-card-es.hbs |
Post preview partial | Standalone | Spanish |
🔄 Inheritance Syntax:
{{!< filename}}at the top of a template tells Ghost: "Render this file's content inside the{{{body}}}placeholder offilename".
Customize behavior via Ghost Admin → Settings → Theme:
| Option | Type | Default | Description |
|---|---|---|---|
background_color |
Color | #0f172a |
Base background for dark theme |
lazy_images |
Boolean | false |
Enable native lazy-loading on homepage |
share_buttons |
Boolean | true |
Show social share UI on posts |
show_langs |
Boolean | false |
Display language switcher in sidebar |
show_sso |
Boolean | false |
Show SSO login option in sidebar |
Configured in package.json → config.image_sizes:
"image_sizes": {
"xs": { "width": 100 },
"s": { "width": 220 },
"m": { "width": 300 },
"l": { "width": 600 },
"xl": { "width": 900 }
}Use in templates: {{img_url feature_image size="l"}}
# Build production assets
yarn build
# Validate theme against Ghost spec
yarn test:dev # Verbose output
yarn test:ci # Fail on warnings (for CI)# Test Spanish routing locally
curl -I http://localhost:2368/es/ | grep "lang"
# Expected: <html lang="es">
The theme targets:
- ✅ Performance ≥ 90 (with lazy loading enabled)
- ✅ Accessibility ≥ 95 (proper
langattributes per locale) - ✅ SEO ≥ 100 (with localized meta tags and hreflang)
Run via Chrome DevTools
-
Build assets:
yarn build
-
Zip the theme:
zip -r sredevopsorg-ghost-theme.zip . \ -x "*.git*" "node_modules/*" ".github/*"
-
Upload via Ghost Admin → Design → Upload theme
This repo includes a Deploy Ghost Theme Action that:
- Builds assets on push to
main - Deploys via Ghost Admin API
- Supports environment-specific config (staging/prod)
Configure secrets:
GHOST_ADMIN_API_URLGHOST_ADMIN_API_KEY
We welcome contributions aligned with our Code of Conduct.
- Branching: Use feature branches (
feat/locale-switcher,fix/og-tags-es) - Commits: Follow Conventional Commits
- PRs: Include screenshots for UI changes; update README if behavior changes
- Testing: Run
yarn test:cibefore submitting
-
Create layout shell:
cp default.hbs default-pt.hbs # Edit: <html lang="pt">, OG locale, localized strings -
Create content templates:
cp custom.hbs custom-pt.hbs cp home.hbs home-pt.hbs # Localize UI text, date formats, component partials -
Update
routes.yaml:/pt/: template: home-pt permalink: /pt/{slug}/ filter: tag:pt+tag:-en+tag:-es+tag:-br data: tag.pt
-
Update documentation:
- Add row to the Language Routing table
- Document any locale-specific partials (e.g.,
nav-pt.hbs)
-
Test thoroughly:
- Verify
lang="pt"in rendered HTML - Confirm tag filtering excludes other locales
- Validate SEO meta tags with
og:locale="pt_PT"
- Verify
- Code: MIT License — use, modify, distribute freely
- Content: CC BY 4.0 (for SREDevOps.org editorial content)
- Third-party assets: Respect upstream licenses (Tailwind CSS: MIT, Ghost: MIT)
- Author: Nicolás Georger (@ngeorger) — SRE/DevOps practitioner, Santiago, Chile
- Inspiration:
- Community: Ghost Forum contributors for multi-locale pattern validation
- Tooling: Tailwind CSS, Gulp, PostCSS, GScan
This theme was built in Chile 🇨🇱 with a proud latin American identity and fully committed to the principles and philosophy of FLOSS (Free/Libre and Open Source Software) — We ought to minimize external dependencies, optimized asset delivery, and community-driven localization patterns. For questions about deploying in Chile/Argentina/Brazil contexts, open an issue or reach out via SREDevOps.org.
Last updated: July 2026 | Ghost v6 compatible