A monochrome, pattern-rich personal-site theme for Ghost. Light-first with a full dark mode and six extra brand modes, built with Tailwind CSS and the Geist typeface. One codebase turns a single Ghost site into videos, a blog, projects, courses, a webseries, a travel log, a shop, timeline, changelog, newsletters, code snippets, prompts, a resume and sponsorship — all driven by internal tags, no code required from the publisher.
- Compatibility: Ghost 5.0+ (validated against Ghost 6.x with GScan — 0 errors, 0 warnings)
- Responsive: mobile-first; tested on the latest 2 versions of Chrome, Firefox, Safari and Edge
- Accessible: semantic HTML, image alt text, visible keyboard focus rings, respects
prefers-reduced-motion - PWA-ready: offline fallback + optional web-push, installable
- SEO: hand-rolled JSON-LD per content type, clean semantic markup, full internal linking
npm install
npm run build # CSS + JS → assets/built/ (both minified)
npm run dist # assembles dist/theme — templates minified, gscan clean
npm run zip # dist/swarnil.zip, ready to uploadThen in Ghost Admin:
- Settings → Theme — upload
dist/swarnil.zipand activate. - Settings → Labs → Beta features → Routes — upload
routes.yaml(required; it powers every collection and the PWA endpoints). - Settings → Labs → Redirects — upload
redirects.yaml(optional;ads.txt/llms.txtredirects). - Settings → Import/Export → Import — import the demo content from the
dummy-content/folder. It's split into one file per module (course.json,lesson.json,video.json,docs.json, …) plusnavigation.json; import the modules you want (seedummy-content/README.md). All video embeds point at a single YouTube URL. - Restart Ghost after the first deploy so new templates are discovered.
Every item on the site is a Ghost post. Its internal tag (a tag whose
slug starts with #) decides which collection it belongs to, its URL and its
layout:
| Tag | URL | Layout |
|---|---|---|
#video |
/videos/{slug}/ |
Cinema page + "Up next" rail |
#blog |
/blog/{slug}/ |
Article + table of contents |
#project |
/projects/{slug}/ |
Case study + build-log steps |
#course |
/courses/{slug}/ |
Course overview + syllabus |
#lesson |
/courses/{course}/{slug}/ |
Lesson player + pagination |
#webseries |
/webseries/{slug}/ |
Series page + episode list |
#episode |
/webseries/{series}/{slug}/ |
Episode player |
#trip |
/travel/{slug}/ |
Trip overview |
#travel |
/travel/{trip}/{slug}/ |
Travel story |
#product |
/products/{slug}/ |
Review (listing groups by #group-* tag) |
#shop |
/shop/{slug}/ |
Product card |
#snippet |
/snippets/{slug}/ |
Code snippet |
#prompt |
/prompts/{slug}/ |
Chat-styled prompt |
#experience |
/experiences/{slug}/ |
Photo + story moment |
#timeline |
/timeline/{slug}/ |
Timeline entry |
#changelog |
/changelog/{slug}/ |
Ship-log entry |
#newsletter |
/newsletters/{slug}/ |
Issue archive |
| (none of the above) | /blog/{slug}/ |
Article (catch-all) |
Container relationships (courses↔lessons, webseries↔episodes, trips↔travel posts, projects↔build-logs) use the first public tag: give each lesson a public tag equal to the course's public tag and the syllabus assembles itself. The same pattern wires episodes to a series and travel posts to a trip.
Standalone pages: create Ghost pages with slugs resume, about,
contact, collab, sponsor, guestbook, bucketlist and welcome to
activate the matching page-*.hbs layouts. /tags/, /sitemap-visual/ and
/archive/ are always available.
The homepage is assembled from independent partials in partials/home/ and
listed in home.hbs — reorder or remove any section by editing that one file.
Sections driven by posts hide themselves automatically when empty, so a brand
new site never shows a broken or empty block.
A built-in switcher cycles eight modes: Light, Dark, Salesforce, YouTube, Netflix, Claude, Twilio and Neubrutal. Each is a full design system (colours, radii, shadows, type) rather than a recolour. Set the default in Design → color scheme; visitors can switch and their choice persists.
Site-wide: color-scheme default, AdSense on/off + publisher/slot IDs (styled
placeholders render until set), YouTube channel URL, Topmate link, job title,
workplace + URL, GitHub repo (for the star button), hero image/video, sponsor
email, OneSignal app ID (web push).
Homepage group: newsletter title, footer tagline, webseries_intro story,
blog_quote epigraph.
The theme consumes Ghost's Brand → font settings via the --gh-font-heading
and --gh-font-body CSS variables, falling back to Geist. Pick fonts in
Design → Brand and they apply to headings and body across the site.
/manifest/ and /sw/ are served through routes.yaml (Ghost rejects dotted
route paths, so these are extension-less). The service worker caches assets
cache-first, pages network-first, and falls back to /offline/. The site icon
in Ghost settings becomes the app icon. Web push is optional — set a OneSignal
app ID to enable it.
Root service-worker scope needs a
Service-Worker-Allowed: /header on the/sw/response, set at your proxy/CDN. Until then the site works normally; only offline caching is inactive.
{{ghost_head}} plus hand-rolled JSON-LD: WebSite + Person on the home
page, and Article / VideoObject / Course / Review / TVSeries /
TVEpisode per post type, with BreadcrumbList on nested pages. Every
collection and tag is reachable through the navigation, footer and the visual
sitemap at /sitemap-visual/.
npm run dev— Tailwind watch + BrowserSync proxy of a local Ghost atlocalhost:2368, plus the component lab on:3002.npm run build— CSS through Tailwind and JS through uglify, both minified intoassets/built/.npm run dist— assemblesdist/theme: every template minified, the built assets,package.jsonwithout its authoring fields. Ends with gscan.npm run zip— the same, packaged asdist/swarnil.zip.npm run test/npm run test:fatal— GScan validation of the source tree.
| Source | Built | Never edit by hand |
|---|---|---|
assets/css/** |
assets/built/screen.css |
the built CSS |
assets/js/** |
assets/built/js/*.js |
the built JS |
*.hbs, partials/** |
dist/theme/** |
anything under dist/ |
assets/built/ is committed. The CSS and JS are compiled from the Creator
Design System — a local file: dependency that only exists on the authoring
machine — so the deploy cannot rebuild them and ships the artefacts instead.
Run npm run build and commit assets/built/ whenever the sources change.
The deploy (.github/workflows/deploy-theme.yml) installs nothing: it runs
scripts/build-dist.mjs, validates dist/theme and uploads that. Nothing in
the shipped theme refers to the design system.
- In-depth docs:
documentation.md(content model, collections, homepage, modes, dev workflow). - Running change log:
THEME-UPDATE.md. - Questions / issues: open an issue on the GitHub repository or email swarnilsinghaicse@gmail.com.
Released under the MIT License. Built by Swarnil Singhai.