Ghost v6 Theme for SREDevOps.org β Multi-locale, Tailwind CSS v3, responsive, dark-mode first, with SVG icons, sidebar navigation, and tag-based language filtering.
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
π LatAm Note: This theme was built with LatAm infrastructure constraints in mind β minimal 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: May 2026 | Ghost v6 compatible