In this guide, I'll show you how I set up my dev environment for creating a custom Ghost theme. The key part of this environment is Rollup, which I use to compile, bundle, and minify my JavaScript and CSS files.
My Rollup config–in addition to some other goodies like a script to start a LiveReload server and one to zip theme files–are available as a template on GitHub. The template includes the basic Handlebars files needed for a Ghost theme, Rollup, PostCSS, basic JS and CSS, along with some explanatory comments. The template builds off of the official Ghost theme Starter.
Below, I walk through the template and show how to use it.
Prerequisites
This guide assumes you have a local version of Ghost installed. If you don't, the official documentation should help you get set up. You'll need some basic familiarity with the command line and a code editor like VS Code.
If you've never built a Ghost theme before, you'll want to read through the docs to understand how the template layer works. You can also look at my custom themes, Smart and Mel, or the default Ghost theme, Casper, to see real-world examples.
If you're brand new to building Ghost themes and would like more articles walking you through the basics–let me know.
Get the Template
Go to my ghost-theme-starter and click Use the template
. (You can also just clone the repo directly.) This will create a copy of the template in your GitHub account. You can then clone the repo to your local machine.
Go to the directory where you installed the theme starter and install dependencies with npm install
. Then, create a symlink from the theme starter to your Ghost's themes
folder ( content/themes
). The symlink allows you to work on your theme in a folder that's independent of your Ghost install, yet still links to it directly. This way you can reinstall Ghost without having to worry about your theme files or upload a production version of the theme to your local install without worrying about overwriting your local development copy.
Run ghost restart
so Ghost recognizes your new theme. Activate your theme and you're off to the races.
Make It Pretty
Styles live in src/css
, and files are organized into a 7-1 architecture. While the 7-1 architecture was originally made popular with SASS, it's used here with several plain CSS files split across seven categories with one main app.css
file. I find that CSS has evolved enough with custom properties and PostCSS that SASS is now unnecessary and sometimes more of a burden than a convenience.
Folder | Description | Example |
---|---|---|
abstracts/ | Variables used across the project | variables.css |
base/ | Base styles for the project | reset.css |
layout/ | Elements found on every page | navbar.css |
pages/ | Styling for particular templates like post.hbs or home.hbs | post.css |
components/ | Components used frequently like buttons or forms | buttons.css |
vendor/ | Styling for 3rd party elements | ghost.css |
themes/ | Multiple color or typography schemes | Not used |
Use the command npm run dev
to watch for changes in src/css
, compile your CSS, and provide a sourcemap, so when you inspect your styles in the dev console, you'll see the exact file and line of code responsible for breaking your design.
In production, initiated with npm run zip
, stylelint will lint your CSS, automatically ordering properties according to a recess logic. CSS will be autoprefixed and polyfilled with postcss-preset-env (like a Babel for CSS). One example of this polyfill is automatically writing fallbacks for custom properties. Finally, the CSS is minified and optimized with cssnano. It's just up to you to ship those styles 🛳️.
Put It to Work
Every KB counts
For JS, the story's the same. Rollup compiles your JS files in src/js
. What's different here is that there are two different output bundles: app.js
and post.js
. App.js
is loaded globally on every page, whereas post.js
is only loaded on posts and pages. The thinking here is that you likely have scripts that are only needed on posts and pages (like responsive embeds) that aren't needed on index pages, so you can reduce your homepage's bundle size by excluding this code from the app's main JS file.
Babel is also active to help compatibility across browsers along with a handful of other Rollup plugins to make it easier to work with next-gen JS and third-party libraries.
Reload It Like It's Hot
Rollup has a built-in ability to watch your files and compile them when you save edits. The problem is those changes won't show up in your browser without a hard refresh.
The theme starter template uses a custom implementation of LiveReload to automatically refresh your browser whenever you make changes to your CSS or JS. This makes it easy to see your changes in real-time, without having to lift a finger.
This reloading is limited to JS and CSS changes and not to Handlebars files. If you make a change to a template file, you're stuck having to reload the browser. (It may be possible to tell LiveReload to watch for changes to .hbs files, too, but I haven't got it to work yet.) You also need to restart your Ghost instance if you add a new template file altogether.
Zip, Zip, and Away
The final piece of this puzzle is a custom script for zipping your files up into a file that is suitable to upload to Ghost. If you add new template files, you'll need to adjust zip.js
to include them.
It's also possible, instead of zipping your files and uploading yourself, to just use Ghost's Github action, which will automatically deploy your theme whenever you push a change to your repo's main remote branch.
The ghost-theme-starter will set you up with a great working environment to build your next Ghost theme. Try it out and let me know what you think.