Ghost Theme Development: How to Customise Your Ghost Publication | Christopher Dodd | Skillshare

Playback Speed

  • 0.5x
  • 1x (Normal)
  • 1.25x
  • 1.5x
  • 2x

Ghost Theme Development: How to Customise Your Ghost Publication

teacher avatar Christopher Dodd, Web Developer / Educator

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Watch this class and thousands more

Get unlimited access to every class
Taught by industry leaders & working professionals
Topics include illustration, design, photography, and more

Lessons in This Class

    • 1.



    • 2.

      Why Use Ghost


    • 3.

      Required Tools and Knowledge


    • 4.

      How to Install Ghost


    • 5.

      Ghost Basics


    • 6.

      Theme Structure & The Default Template


    • 7.

      Optional Templates and Basic Routing


    • 8.

      Custom Routing


    • 9.

      Coding within the Post Context


    • 10.

      Coding within Author and Tag Contexts


    • 11.

      Custom Queries and Partials


    • 12.

      Completing the Theme


    • 13.

      How to Publish your Theme Live


    • 14.

      Bonus: Setting up the Github Integration


    • 15.

      Conclusion & Class Project


  • --
  • Beginner level
  • Intermediate level
  • Advanced level
  • All levels

Community Generated

The level is determined by a majority opinion of students who have reviewed this class. The teacher's recommendation is shown until at least 5 student responses are collected.





About This Class

In today’s class, we’re gonna learn the fundamentals of Ghost Theme development in order to give you full control over the look and feel of your Ghost publication.

For those of you who are unfamiliar with Ghost. I would simply describe it as the more modern, more efficient alternative to Wordpress. It’s super fast and comes without the extended bloat of Wordpress, making it an ideal platform to host a blog or other online publication.

In this class, we'll create a new Ghost theme from scratch, learning how it all works as we move through the lessons.

Meet Your Teacher

Teacher Profile Image

Christopher Dodd

Web Developer / Educator

Top Teacher

Christopher Dodd is a self-taught web developer, YouTuber and blogger with a mission to help individuals learn the skills to freelance and make a living independently.

Chris learned web development in 2015 in order to work remotely and travel the world and has done so for the past 2 years.

Through his YouTube channel, blog and Instagram, Chris inspires and educates newbie 'digital nomads' to chase their passions and pursue a location independent career.

See full profile

Level: Intermediate

Class Ratings

Expectations Met?
  • 0%
  • Yes
  • 0%
  • Somewhat
  • 0%
  • Not really
  • 0%

Why Join Skillshare?

Take award-winning Skillshare Original Classes

Each class has short lessons, hands-on projects

Your membership supports Skillshare teachers

Learn From Anywhere

Take classes on the go with the Skillshare app. Stream or download to watch on the plane, the subway, or wherever you learn best.


1. Introduction: Hello and welcome to Ghost Theme Development: How to Customize Your Ghost Publication. I'm Christopher Dodd, I'm a freelance web developer and top teacher here on, covering all things web development and online freelancing. In today's class, we're going to learn the fundamentals of Ghost theme development in order to give you full control of the look and feel of your Ghost publication. For those of you who are unfamiliar with Ghost, I would simply describe it as the modern, more efficient alternative to WordPress. It's super fast and comes without the extended bloat of WordPress, making it an ideal platform to host a blog or other online publication. This is precisely why I decided to move my own personal website to Ghost in 2021, and saw a massive speed improvements when compared to WordPress. So if you're ready to learn how to develop and customize Ghost themes, click on the next video and I'll see you on the inside. 2. Why Use Ghost: Before I show you how to install Ghost on your computer, I want to quickly talk about why I recommend Ghost, as I'm sure many of you watching have not heard much about this platform before. Ghost was founded in April 2013, approximately 10 years after WordPress, after a very successful Kickstarter campaign to create a new platform focused solely on professional publishing. The keywords there are focused solely on professional publishing. This is important because it shows that Ghost is specialized on doing one thing very well. Personally, since switching from WordPress to Ghost in 2021, I can comfortably say that Ghost is a much better platform for online publishing. One of the biggest benefits to Ghost is how fast it is, 1900 percent faster than WordPress in fact, according to independent studies. Why? Because Ghost, unlike WordPress, is built on a modern technology stack. When I moved my website from WordPress to Ghost, my speed grade according to GT Metrix dramatically improved from an F and a few Bs to all As. The same can be said for interacting within the admin side of Ghost as well. The admin is a single page web application, meaning that interactivity is smoother and faster. Not to mention, the interface is clean and simple. Unlike WordPress, with all its various menus. The Ghost admin doesn't change depending on which theme or integration you add to your website, meaning the experience is a lot more consistent than on a platform like WordPress. The trade-off, however, is that modifications to your website are more difficult to do without coding knowledge. Luckily for you, that won't be an issue after taking this class. To conclude, you weren't likely find Ghost reaching mainstream notability, given it is such a develop a focus platform, but for those of you who know how to harness the power of Ghost, you should find the experience for both yourself and the user is remarkably more satisfying. 3. Required Tools and Knowledge: In this class, we will focus on the specifics that make up Ghost themes. Of course, Ghost theme development includes the three languages of the front end web, including HTML, CSS, and JavaScript. But this is not a class focused on any one of those topics. It is expected, before taking this class, that you have a solid understanding of HTML and CSS. In terms of required tools, as usual, you'll need a web browser, Google Chrome is recommended; and a code editor, which in this class, I will use Visual Studio Code. In the next video, I'll show you how to download and install Ghost locally so that you can start working on developing Ghost themes. But once our theme is built, we will need to upload it to an online host. The specifics of all this will be discussed throughout the class. 4. How to Install Ghost: As mentioned in the previous video, we're going to want to install Ghost locally. Locally, meaning that we're going to be running it on our own computer. There's two parts to this, the first part is installing the command line interface, so Ghost CLI, and the second part is we're going to install a Ghost instance on our local machine. We'll be able to run a little server here and operate with Ghost as we would on a live site, except it'll all just be local and we have easy access to the code for which to develop our Ghost theme. As mentioned here, it is not suitable for production use. This is something we use to develop the themes and then when we're ready to publish, we'll then push that code to our live site. But for now, what we're going to do is start with installing Ghost CLI and for that we need to ensure that we have a supportive version of Node and npm installed. This first requirement here under prerequisites, that's going to cover 99.9 percent of people watching this. I would be very surprised if you had an operating system other than those three, so you should be good for that. Node.js is a runtime environment that is very common in modern web development. If you've been doing other web development, you should have that one installed. Otherwise, the link is there,, and you can follow the installation instructions there. Otherwise, if you have Node installed, then make sure you have npm installed and then you're ready to follow along. If you're not sure if you have those two installed, let's open up a terminal window right now. I'll just clear all these warnings. I use one called iTerm, so it might look a little bit different to your terminal application. You'll use terminal on a Mac or command line prompt on a Windows by default. But here in my terminal application, what I can do to check whether I have Node and npm installed is just run the command to check the version. I can check my Node version and you can see it's 16.9.1 and then I can go npm-v and that'll tell me my npm version. I'll just zoom in here so you guys can see it more clearly. As you can see when we run these commands, we have version numbers come up and so that means that we have Node and npm installed. The next thing we're going to do is we're going to install the Ghost CLI with this command right here now that we know we have Node and npm installed. I'm going to copy and paste that command into our terminal and this will install the latest version of the Ghost CLI. After about 20 seconds that should be installed and now we can start to find a directory to install our Ghost website. What I'm going to do is clear that, I'm going to then navigate into a directory I have on my computer, code/skillshare. This is where I keep all my Skillshare projects and in here I'm going to make a new directory using the command mkdir, and I'm going to call this one Ghost site. Then after I've created that, I'm going to navigate into that empty directory and now I'm inside Ghost site. Let's now run the command to install Ghost, ghost install local, and let's see if it works. We get a error pretty early, I'm not sure if you're going to have the same error, but this is a pretty common error, so it'd be important for a lot of you to see how to work through this. It's the error that says the version of Node.js you using is not supported. As you can see here, these are the list of supported Node versions and if you remember before, we saw that we were running 16.9.1. Obviously, this is going to be different depending on what version you have, but as you can see, there is a discrepancy there and that's why we've got the error. Unfortunately for developers, this is not that uncommon of an error. There are a lot of different Node packages out there that only run on specific versions of Node. You would think that they'd all run on the latest version of Node, but I guess Node changes so quickly that you need a solution in order to switch between different versions of Node and for that, there's a package called nvm. I'll just show you that if you don't have it already, it should come up as the first result when you type in nvm into Google, and here you can see Node Version Manager. If we scroll down, we can see the command to install it. I already have it installed, but this is the command that you would run if you didn't have it installed already. We can go down here, as you can see on my particular computer, it says nvm is already installed and it's just going to try and update. For you, it might be doing a fresh install, just make sure you've got Node Version Manager installed and that way we can select a specific version of Node in order to run Ghost. A little frustrating, but unfortunately, this comes with the territory of being a developer. Sometimes you need to choose and switch between your Node versions. Now you would think that you'll be able to change to the Node version by just running nvm use node. This is the command that you use to change your Node version and I think it was, I even forgot what it said. Let me just run ghost install local again. We have those version numbers again, here we go, 14.15.0. You would think that you'd be able to use the command nvm use node, which is the command for changing a version. I'm going to go nvm use node 14.15.0 and now you can see we've now switched to that version. You would think that that would then work for running ghost install local. But unfortunately, for me, it says that it's not compatible with the current Node version. It's weird I get this different error which doesn't have the supported numbers, but at the same time, I'm still not able to install a Ghost instance. I can't explain why this is the case, but this version is still not compatible with Ghost. What I would do is look up Ghost supported node versions and check on the latest forums. This right here is an earlier screen recording of a forum post that I found called downloading error with Ghost CLI, I found an answer from Kevin on the staff that the minimum supported version is 14.16.1. If you install that using nvm and then run nvm use node and set it to that version, then it should work for this particular version of Ghost CLI. I know that's not the cleanest answer, but unfortunately, something that Ghost has not made clear on their website or inside the error message. Now let's run ghost install local. As you can see, our installation is going ahead now. I'm just going to skip ahead here so that we don't have to wait for the whole thing to go. As you can see, we're almost there, we're starting up Ghost now and here you can see Ghost was installed successfully, to complete setup of your publication visit this address which is going to allow you to set up your Ghost admin. Let's open that up. I'm going to hold down Command and then click on that link in order for me to access it and now you can see I can run through the Ghost installation. I'm going to click "Create account." I'm going to call this Skillshare, my name is Christopher Dodd, and I'll put in some details here that I've previously saved and then here I will skip this to invite staff users. Now, I'm inside a fresh Ghost install and here you can see the Ghost admin, which we'll talk about in just a second. If we wanted to view the site, we can click this link here and that'll take us to our Ghost site which has been prepopulated with some example blogposts and it's using the theme at Casper. If you want to check the theme you're currently using, you click on this to go into settings. Note that this interface is subject to change, but currently this is how it looks and going into Settings, we can then go into Theme and then you can see Casper is installed by default. In a moment, we're going to go out this theme and we're going to replace it with our own theme. But for now, we are working with this existing theme and we are pulling blog posts from within the admin here. As you can see, we've got some blog posts that come standard with Ghost. If we want to get rid of them, we just delete them all, and we've also got some pages that have been provided by Ghost. I think we've got one tag here and under Staff, we have me, and the Ghost user. The Ghost user is the one that all of these blog posts and pages have been attributed to and if we're looking for where this navigation comes from, we can go into Settings again, hit Navigation, and you can see this is where our navigation is coming from. This is what you get out-of-the-box from Ghost, but of course, in this class we're going to learn how to customize this, build our own theme. But we're all going to be using the same Ghost data. We're going to be using tags, authors, pages, and posts. But now that we have Ghost installed, I'm going to wait till the next video to show you more about Ghost and we're going to cover some of the basics of how this all works. 5. Ghost Basics: Welcome back, everyone. In this lesson, we're going to be covering some of the basics of Ghost sites and Ghost theme development. As you can see, I'm running a Ghost instance here and the address for the admin is as follows. If I look into my browser here, you can see the address to just check out the site as it is, is the same minus the slash ghost. Slash ghost is what we use to access the admin. What I'm going to do is put in slash ghost, and that'll take us back to the admin. I want to draw your attention to two other documents here that are going to guide us throughout the class. Here is the official documentation. I've moved from installation of Ghost to the themes overview. Inside the developer documentation, this is of course subject to change, but at the time of recording, it's under here in this themes menu, you've got all the details regarding themes. I just clicked on overview to get to this page. I encourage you to read the documentation. But what I've done on top of that is created my own guide to Ghost Theme Development. This will be published by the time you're watching this class, and the URL will be ghost-theme-development-guide. But it should be the latest post on my blog for the foreseeable future. Should be pretty easy to find by going to or, followed by the specific slug here. In this guide, what I do is I go through the basics which we're covering now, and I break up the whole article into four categories; templates, routes, context, and helpers. As we get further into in the later videos, once we start to build templates that match up with routes, if I scroll down here, you can see I've created a little diagram here. I think for things like structure, having a diagram really helps to conceptualize what's going on, and in this diagram what we have is on the left we have the different routes. If you're on the homepage route, you're going to be seeing index.hbs unless there's a home.hbs template. This is what this all means. Don't worry, we'll get into this later in the video. But the whole basis of Ghost Theme Development is routes and templates. Accessing these different routes, the template you're going to get is dependent on which templates you actually have in your theme and the name as well, because the naming convention is very important. But at a fundamental level, you've got these two templates here that form the backbone of all Ghost themes, which are index and post. Because if we go up here to the basics here, a blog at its most basic form is a collection of blog posts. Let's go back into our admin here and let's explain it in the context of the Ghost admin. The most prominent menu item here is your posts. You've got this little drop-down here. You can look at posts in terms of drafts scheduled or published. Now, this makes sense because Ghost is a publishing platform. It's a blogging platform. It's pure. It does this form of websites really well, which is why I recommend those of you who are going for specifically a blog to use ghost. It doesn't do absolutely everything well, but what it does is it focuses on the core functionality of a blog, which is a collection of posts. If we go into here, you can see here are our list of posts. As we saw in the last video, this has already been populated. But if we go into here and click "New Post," let's just call this Example Post, and in here we can insert all things like an image, markdown HTML, or we can just start typing. This all saves super-fast. The editor is super-quick. We can also add a feature image to our blog post as well. Then over here what we can do is we can update the post URL, change the published date, add tags, add an excerpt, change the author, and change the template, if other tablets had been set in the theme, which currently they haven't. That's a lot of detail but essentially with the exception of pages, all the other data types that we'll see in that little menu are related to organizing our posts. In a system like let's say WordPress or Shopify, in WordPress you have something called a category, and then you have tags, in Shopify you have something called a collection, and then you have tags. In Ghost, there's no concept of a category. What you have instead is something called a primary tag. For this post, the first tag that I put in this box is the primary tag. What I'm going to do is just create a new tag now called Example Tag and then I will tag it with getting started as well. It's going to have two tags, but this first tag is known as the primary tag. If you wanted to create mutually exclusive categories, this is how you would do it. You would put the category or the tag that you want to categorize the post by primarily as the first tag. Down here, the author is automatically set because I'm currently logged in as Christopher Dodd, but I could easily change this to Ghost and then have this article attributed to another user. I'm going to keep this attributed to myself. That's basically it for post settings. Let's have a look at what that looks like. This is just going to be a draft. I wouldn't do this on a normal site, but because we're working in development, I can just publish this and it'll publish instantly. Let's head back to posts and let's look at the other content types. As I mentioned before, tags are the way you organize your content in Ghost. If I click into tags here, you can see we've got the different tags and the number of posts attributed with each. I want to draw your attention to tags a little bit more in this video because they're a little bit different to what tags are in a lot of other systems. As you can see, inside a tag we have a lot of customization options. We can add a tag image, we can add a color, we can give it a more user-friendly name. We can even give it a description and customize its metadata. Now, this is very different to a lot of other systems, which a tag is just a string of characters. That's it. It doesn't have all this other data attached to it. But the point I'm making here is that we don't have categories in Ghost. Tags are basically your categories as well. Very flexible system tags work well to give you a lot of flexibility in terms of how to organize your website content and with the use of the primary tag which we just discussed, that gives us all of the benefits of that flexibility without the trade-off of having these mutually exclusive categories. If you want to categorize your posts by mutually exclusive groups, you can use the primary tag. Then in order to customize the staff or the authors, we can go down here to the stuff menu here, and in here we can add data to the user. If I go into my name here, I can change my Slug. I can give myself a cover image and a profile picture. I can give myself a location website, and all of this data is available for us to use inside of Ghost Theme Development. Of course, you've got integrations here and you've got members if you're running a paid membership site. In this class, we're not really going to be talking about Ghost as a way of making a membership site, we're just going to be talking about Ghost in terms of blogging, but this is also an option here. I've heard that Ghost is very powerful for that as well. The final little thing here I want to draw your attention to is pages. Pages are pretty much exactly like posts, it's just they're not organized in our index of collections here. They just stand alone. We have to link them somewhere. There's not a loop with all of our pages. They're just post that sit outside posts and don't belong in the core collection of your Ghost website. As you can see some of the examples here are stuff like that your About page, your contact page, your privacy page. These aren't really blogposts. They're going to look and feel the same perhaps, but they're blog posts in the sense that we're not going to give them tags and the date that they were published might not necessarily be so important. This is pretty much the same in any blogging platform you've got your posts and your pages. Basically, the only difference is that posts form a collection and have dates on them. Pages you can show the published date as well in Ghost Theme Development, but that's not so common. The difference is with posts, we want to be able to loop through them, and when new posts come available, we can publish them to RSS feeds, stuff like that. If you've had any experience with blogging before, that should be pretty clear to you. The final thing I just want to reiterate here is that your navigation sits in your settings here, so you can go into settings then click navigation, and this is where you set your navigational data. We saw, if I go back to our site by clicking this link, you can see our navigation up here. If I was to remove a few items here, let's just remove everything except about and hit Save. I think I actually have to click Trash here. Let's retry set over here. You can see our navigation shrinks and our new blog post that we published is there as well. Then what I'm going to do as we don't want a post with no image, I'm just going to add a featured image here. I can just find one randomly from Unsplash. Let's just insert one there. Click "Update." Go back to our theme, oops. Let me go back to Home, and here you can see we've got our example post under Example Tag and here under Getting Started, all our other posts. Now, if you're wondering where this Getting Started comes from, that is the primary tag. Very important what you put as your first tag. Of course, in your Ghost theme as you're developing it, you can choose to either feature the primary tag or treat it like a regular tag. It's up to you. But in terms of creating these categories here, you would use the primary tag. I just want to make that clear because that's something that is different in Ghost compared to other blogging platforms. Now that we've had a look inside the Ghost admin and you should be a little bit familiar with it, you'll get more accustomed to it as time goes on, let's get out our theme and start to rebuild it so that we can learn how it all works. The theme that we're using, if I go into Settings and I go into Theme here, is Casper. If I had another theme ready to go, I could upload it and switch to that theme. But for the purposes of us, as we're developing a brand new theme, and we want to see all our changes happening as soon as they happen, I'm going to keep things easy for us and we're just going to get out our Casper theme here. When that's ready to go, we can package that, and then upload that to our live site once we create it. For that what I'm going to do is open up our project in my code editor. As mentioned previously, the code editor that I'm using for this class is Visual Studio Code. I'm going to open up a new Window in that program. You can see all these different warnings here. I'm going to close that down. Let me expand this and then what I'm going to do is click "Open" here. From here, go into my Code folder, go into my Skillshare folder and open up Ghost site. Closing down that welcome menu here, you can see that we've got three folders: Content, Current, and Versions. Let's go into Content and go into Themes, and here is our theme code for Casper. We've got lots of different code within various files. In this class, rather than change things that already exist, I want to start from the bottom up so you guys have a fundamental understanding. What I'm going to do is literally start deleting some of these files. I'm going to delete all the templates except for the compulsory ones. If we go back to the blog post or the documentation, there are only two templates that are required in the Ghost theme, and those are index.hbs and post.hbs. As mentioned before, that makes sense because a blog at its most basic form is simply a collection of posts. If you don't have a place where people can view all your posts and then you don't have a template for displaying a post, then you basically can't have a website. There's not too many required files, you just need to make sure that you have an index.hbs and a post.hbs. Let's gut out all of our templates apart from those, so we can learn how to rebuild. I'll get rid of all of these templates here. I'm going to get rid of everything in the assets folder as well. We're going to be putting in our own assets, and then inside our partials here, we'll be getting rid of everything in there as well. What I'm going to do is take this down to a very basic level. Let's just remove all that and I'll just have a page 1 that says Index, and then in post, I will break this down and have a template that just says Post. Because we've made changes to our hbs files, we've removed some hbs files, it's a good idea to restart our Ghost instance. I'm going to head back over to the terminal here, and I'm going to run the command Ghost restart, which as you might suspect, we'll stop our Ghost instance and then start it again. Locally, this shouldn't take too long to do. There you go. It's restarted. Let's go back to our Ghost website now and see how it looks. There you can see on the root directory, I'm looking at the index template. Obviously, there's nothing there except for h1 but that's what we expect because we just gutted that out. Then if I go to a specific post, so what was one of the posts we had? If I go back into my Ghost admin here, and I go over to example posts, so clicking on View Post, you'll see that we just have post. No dynamic data, but you can clearly see that we have gutted out the index template which was serving on the homepage and we've gutted out the post template. That's what I want you to see is that we're now able to hit those two templates with our two core routes. Routes, of course, are one of the four categories I talk about in this guide. We talk about templates and we talk about routes. We go into a lot of detail about routes because routes are something that's very important in Ghost theme development. Actually, the file that you change your routes is not actually in your theme. Before we get started changing our routes and updating our templates, I just want to draw your attention to that file. It's the routes.yaml file and the place to look for it is inside your settings directory, inside your content directory. I'm just going to click this, and have a look at the routes.yaml file. This is the default configuration for routes as we talk about on the blog post here, this is the default. We'll get into more detail about how this works and the syntax behind YAML in the next video. But essentially what I want to bring your attention to, is we've got our routes defined here, and we have a single collection route here, that is loading our index, and is being served on the root route. This is what determines that we're running our index of blog posts on the root route. Then this permalink here tells us that we're running the slug of our blog post right after the first slash. Of course, it might make sense that the homepage is an index of posts and if we go into here and type example posts that it'll show a post. But this is all customizable within Ghost as well. We can update these routes, which we will do in a future video. In fact, I believe it's the next video. Hopefully, that's not too much information. I'm going to leave it there and we're going to go deeper into routes and templates in the next video. But what I wanted to demonstrate in this video was how the admin works. It's quite simple once you get your head around it. We're just running a collection of posts. We've got some pages on the side, and we categorize our posts by tags and authors. We can add data to tags and we can add data to authors, and we can display that in our Ghost theme as well. Finally, we can customize our routes using the routes.yaml file, which we'll learn about in the next video. I'll see you on the next one. 6. Theme Structure & The Default Template: In this lesson, we're going to go deeper into theme structure and talk about the various templates that make up your Ghost theme. We're going to go deep into this diagram here, and if we have some time, we are going to go more into the advanced routing. But there's a lot of detail there. So let's just start right here with theme structure. Now to explain this diagram, what I have on the left is the routes. So wherever you are accessing the homepage or a blog page, or a page or post, you will end up by default, if we go to, right here, the index.hbs file or the post.hbs file. I want you to ignore all these gray templates for the moment here and understand that if all you have in your Ghost theme is an index.hbs and a post.hbs, and of course, an optional default hbs, which we'll cover in just a second. It doesn't matter which route you go to, you will always end up on even one of these templates. Now, these templates in between the route name and these default templates are all optional templates. And these optional templates allow you to customize what shows up on more specific routes. So for instance, if we didn't want our index page on the homepage and one of the specific page that goes on the route route, we can't set that using home.hbs. This is probably the most simple example out of the whole diagram. If we go to the homepage and we don't have a home.hbs, then we will default to index. If we do that, we have to update our routes as well. I'll show you that in just a second. But moving further down, if we go into the next example, the next route, if we look at an index route, basically like looking at the blog overall or a filtered view of the overall collection. Let's just say, for instance, we are looking at all of those blog posts filtered by a particular author. If there's a template called author-of that offer.hbs, then that template will get loaded ahead of these templates. By default, if we go to an index page wherever we're filtering by author or tag, it'll go to index.hbs. But if, for instance, we are filtering by a certain author and we have an author.hbs file, then the code from that author.hbs file will be served. But if say, for instance, we're looking at the user Christopher, which is one of my users in my Ghost site at the moment, that is the slug for my user. If I have a template called author-christopher.hbs, that will get loaded ahead of the author.hbs file and ahead of the index.hbs file. So hopefully this diagram starting to make a little more sense here, but I will go into showing you how this actually works inside the Ghost theme in just a second. So following that same logic, here you can see if we filter by a certain tag, by default we will get the index.hbs template unless we have our tag.hbs template. And if we're filtering by a certain tag and we've set a custom template for that specific tag, then it'll serve that specific template. Heading down to these routes for individual posts and pages, if we're accessing a page and there's no page.hbs, no custom template being set on the page, and no custom templates specifically for that page, then we will load post, but only if none of these three exist. There's a lot of options here for page, we can have a page.hbs template, we can have a custom template but that can apply to both posts and pages, and I'll show you how you do that in just a second. Or we can have a page template that relates to a very specific page and that will get loaded ahead of all of these. So that's a step-by-step understanding of what these rows and columns mean. Let's actually go and put this into practice inside our Ghost theme. So first of all, what I want to do is set a default.hbs file. And what this does is hold some wrapper code that goes around whatever template is served in here. That's why I've got it sitting at the top. This is to signify that default.hbs will wrap around whatever template is being served. And this is handy for things like headers and footers, which will be the same on every single page. Heading back to our code, what I'm going to do is create a default.hbs file. In our default.hbs file, we're going to load in some boilerplate HTML content here. I'm going to put it in the doctype tag. Start the HTML tag, going to set Lang, and this is our first little bit of handlebars code, will go deeper into handlebars and how to use all the helpers in a later video. But for now, hopefully, this doesn't require too much explanation. I'm just going to throw that in, and then I'm going to start up my head tag, we're going to need a title for our document, and I'm going to set a dynamic value for this as well. Whatever the meta title of the page is, we'll put in there, put the charsets in there, utf-8, set the viewport Meta field here. Again, this is all boilerplate contents so these are the stuff you would do on any website. So I'm not going too in-depth on it at the moment. Then I'm going to put in is a special tag for inserting some of the head code that comes directly from the Ghost admin, which is just Ghost head. Underneath here, what I'm going to do is create a header and then we'll start to see that header appear on every page. So first of all, I got to open up a body tag and then create a header with the class of header. Get rid of those. Then I'm going to bring in some structure and CSS here that I've already created. I'm going to bring in some HTML or CSS classes that I have already determined. This is not a CSS class, so I will be copying and pasting some CSS over, just to make it look nice. It's always better when your website looks nice. Then I'm going to add in the header logo here, which if there is an image set, so if site logo, putting an else there, if there is a site logo, then let's load that one in there. For those of you wondering the at symbol indicates that that is a global object. But again, we'll learn more about handlebars in a later video. If there's no site logo included on the website, what we're going to do is output the site title. Then underneath that, we're going to create a header navigation, is to put in a really handy helper from Ghost which is just the navigation tag that's going to output our navigation with some default HTML structure. And then after the header, what I'm going to do is bring in the main content through this main tag here and use this special tag here with three curly braces on either side and put the keyword body in there. Now what this is going to do for every template that includes the default.hbs is going to load the content of that template inside this body tag here. There's one more step we need to do in order for this to load on both post and index and that is to put in a special tag at the top of these templates that looks like this. It looks a little bit like a partial tag which will see you in a sec but it's got that exclamation mark. So it's a special tag that tells Ghost to load in our default template around this template. As I mentioned before, I'm just going to copy and paste the CSS because I don't want to spend too long in this class creating CSS when we're not here to learn that. So what I'm going to do is I've got a folder of CSS here, I'm going to go into my assets and paste that in. And as you can see, I'm using Bootstrap, which is a CSS framework, you can find it at I'm using version 5.1.1 and then I've got a custom CSS file ahead of that as well. So we're not going to talk about CSS in this class but having that CSS there will at least make our project look a lot nicer. What we're going to need to do now in order to view these changes is because we've added a new template, we need to restart our Ghost instance. So I'm going to go do that right now. Run Ghost, restart again, and then once that's done, we can go back to our browser here, load up example post, and we should expect to see the default code wrapping. I think I've made a typo here, I think that needs to be a space there. Apologies for that, let's refresh Ghost again. And then heading back to that post, you can see we've now got our navigation and our site title showing up. And that's going to be the same if we go on to our index template. There's one other step here I forgot in order to bring in that CSS. And that is to go into our default.hbs and add in a link tag to bring in that CSS into our project. And we're going to go link rel style href equals. And here what we're going to do is use a helper. We're going to use the asset helper, which is going to pull in the CSS from both Bootstrap. So give it a file name here. This helper will look inside the asset folder for this particular file. So if your CSS is nested in a CSS folder, you just need to include that there, but otherwise, it scopes to the asset folder. I'm just going to copy and paste that to save some time and then change this to main.css. Refresh over here, and you can see our header is more nicely formatted now. So if I go to Home, we get our index template. If I go to example post, we still get the header, we still get all of that code but in the main content, we've got a different title coming through. So if I inspect this in our Chrome DevTools, you'll see that we're loading post, which is the content inside the post template, inside the main tag. If I go home, you can see same thing again, we're running index inside main and that is consistent with what we have right here. So everything above main, we've got the header, we've got our HTML head up here and that's going to load on every page because we're using default.hbs. It feels like we've spent a fair amount of time on this default.hbs file. So what I might do is I might call it there for this video, and in the next lesson, we'll start to build in more of these templates that we see in our diagram here. We'll create a home.hbs file and some of these other templates as well. 7. Optional Templates and Basic Routing: In the last video, we added a default.hbs file to our Ghost theme and then we included that on our post in index template. In this lesson, we're going to look at the additional optional templates that we can use to override the content in index.hbs and post.hbs for certain routes. The most obvious one is the homepage, and that's the one we're going to do first. It actually does require us to make a change to our routes file, but a very minor change. There's actually a part of the Ghost documentation that shows you how to do this as part of other examples within routing. You can go into Themes, Routing, and we've got our base configuration here. If we scroll down, we've got under the heading using a custom homepage, we can change our configuration to this. We can move our index collection off of our root route and move it onto blog. We can nest our blog posts slugs within blog and therefore we can set our home.hbs file to run on the root route. Let's go and do that right now. Let's go into our routes first of all and let's change this to blog, slug, and then blog. Make sure you have the slash on both sides and then here we're going to set a specific route for the root route and we're going to load the home template. Now, we're going to need to actually make that home template, so inside I'm going to write home.hbs and just like I've done with these other templates, we're not going to spend a lot of time coding them up in this lesson, we'll get to that in a later video. But I just want to show you how the routes work and the different templates that gets served on different routes. I'm just going to do the same thing I did before and put the h1 in there and I'll also get the default tag in there as well, so it loads the header. Now, that we've created the new template, we need to restart our Ghost instance, so I'm going to go over here and again run Ghost restart. This is something we'll need to do whenever we create or delete a file in our Ghost theme. Now, if we head back to our website and we go to the root directory, hit Enter you'll see that we're now on the home template. If I go to blog down and you can see that we're running the index template and if I go blog example post, you'll see we're running the post template. Now, just like we see in the diagram, if we don't have a home template we're going to be running index.hbs on the homepage route, but if we do that will override the index.hbs template. Moving down to the next route, we're going to be loading the index template by default on the blog route. Again, like we saw, we moved it in our routes. YAML file to be blog rather than the root route, but here whether we filter by a certain taxonomy or not we're going to get the same route. As we can see in our routes file, if I go back to it here, we've got these taxonomies here and this allows us to filter out index via tags and authors. Again, so remember we created that example tag before. If I go in here and I do tag, example tag, you'll see that we get the same template, we get the index template. If I go in here and use a different taxonomy and I go, Author Christopher which to make clear is my slug here in my user so here you can see Author Christopher, that is the URL you would go to, to filter the posts by author. We'll see that when we go through the index loop when we talk about contexts and help us later in the class. But the point I'm making here is because we haven't got any author or tag template set up, we are always going to default to the index template. It's always going to serve the index template if we have none of these setup. Let's set up some of these templates now and see this happening in real time. What I'm going to do is go over back to our Code Editor and inside our Casper theme folder and I'm going to create a tag.hbs and create a author.hbs file. In our tag.hbs I'm going to copy the same format as all these other templates and I'm going to just put tag here so we know we're in the tag template and the author template here. Restart our Ghost instance as we have added new files and then if I go back to our website here and we're on the Author Christopher route here, If I hit Enter on that you'll see that the word here changes to author, which means we're now running the author.hbs template now instead of the index.hbs template. This, of course, does not override it for the blog route. We still have the standard index template for that one, but if we're filtering by author we're going to see the author template and if we're filtering by a certain tag such as example tag we're going to be loading the tag template. That should be pretty clear by now. We can also create a template for a very specific author or a very specific tag. I'm going to do that right now. We've got a tag in here called getting started. Just want to show you this so I can show you that there is a difference. Right now, both of the tags are going to be loading that tag template. But what if we want to run different code based on a specific tag or a specific author? Well, what we can do in that case is create a custom author template for that specific one. I think I've clicked new file in the wrong one there. Got to do it inside our Casper theme folder and what I'm going do is make a template with author dash followed by the slug of the author, so for me it's going to be Christopher, hbs and here I'm going to copy that code, go over here and I'll just say Christopher author template. Then I'm going to do the same for tag, I'm going to go New File, tag dash followed by the slug of the tag, which is example tag, followed by dot hbs and do the same thing as before here , example tag template. Now, again, because we've created new templates, I'm going to restart and then head over here to getting started because we haven't overrided that template, it's still going to use the tag template. But if we go over here to example tag, you'll see we're now running a different template to the standard tag template. If I go over here to Author Christopher, you'll see that we're running the Christopher author template whereas if we go to the Ghost author, which is running the standard author template. Here what you're seeing is the theme structure in action. I know there's not much going on here because there's no content. We'll get to that in a later video. But this should start to reinforce what is happening in this diagram here. We have our core files at index.hbs and our post.hbs and then we have all of these additional templates that we can use to customize certain routes. I'm going to move further down the diagram now and look at the additional templates that sit on top of the post.hbs. As we see here for page, if we don't set any specific templates for page, it's just going to default to post, which is going to be the same template as a post. But if we set a page.hbs file, that will override what shows on posts. What I'm going to do is I'm going to run through this now. I'm going to go into our theme again, let's close down some of these files, and I'm going to add in a page.hbs. Again, what I'm going do is replace that with 'This is a page' and then let's restart out Ghost instance. Heading back over to our website now, if we're on a specific post, which remember we changed the route to blog and then followed by the post slug. We're going to be on a post, but if we go to a page, it's going to say, 'This is a page.' The next template that will override this page template and the post template is a custom template. Heading back to the diagram, we create a custom template by writing custom dash followed by an arbitrary name which we decide. The way we set that on any particular post or page is in the admin. I'll show you that right now. Let's go back to our theme here. Let's create a custom. What I'm going to do, you'll see later in the class, is have an alternative layout for pages and posts that has a image header. I'm going to call this one custom-header.hbs. For now, as we're not getting into the code, we're just getting into the concepts of routes and templates, I'm going to call this Custom Template Set in Ghost Admin. Again, we created a new file, so we're going to need to restart our server again. Again, this shouldn't take too long if we're running it locally, which we are, and then I'm going to go into our admin here and let's just say this one before example post. I'm going to click on that. I'm going to head over to post settings over here. Scroll down, and I think I need to refresh over here. Let's go again, scroll down. You can see that we now have an extra field here called template, and we've got this drop-down menu. We can either choose the default template or this one, the name of which is going to be equal to what we put after the custom dash in the template name. We called it custom-header. I'm going to click on this template here for header, "Update," and then when we view this post, you'll see that it says, Custom Template, Set in Ghost Admin. This is going to be different to the other posts like example-post, that's still using the post template, and the good thing about this template is we can set this on a page as well. All we have to do is go into our page. Let's say it's contribute, head into our page settings, and we can set this to an alternative template as well. We're almost through the diagram here. We just have page-slug, post-slug, and just like we did up here for author-slag and tag-slag, we can use a custom template for a very specific page or a custom template for a very specific post. Just for completeness sake, let's run through that as well, even though it's the exact same concept as the other ones. I'm going to go in here and create, let's do it for the about page. The about page-slug is about, so we just do page-about, and then this is the template specifically for the about-page. Then let's do it for post as well. Let's do example post. We'll do a specific template for a post-example-post. This is the template specifically for example-post. Again, restarting our Ghost server because we created some new files. We won't need to do this as much in the later videos, by the way, because we are just creating all of our files now. But then once we start working on building out some code in here, the files will have already been created, and therefore, we won't have to restart our server every single time just so you know. If you're getting sick of restarting. Back to our Ghost website now, if we head to the about page, you can see we're now running the template specifically for the about page. If I go to the blog example post, you can see we're running the template specifically for example post. To just differentiate that from everything else, if we go to blog welcome, you're going to see that we're running the custom template, and if we go to, let's say, another blog which doesn't have a custom template on it or has a post specific template on it. Let's just go to this one, design. You can see it uses the standard post template. It'll just default to post.hbs if it's not using one of the other optional templates that sit on top of post.hbs. That covers the entire diagram here. There are some other routes and templates, but these are the core routes and templates. If you want to learn more about the other routes, you can head to the documentation. It's not too complicated. If we head to structure, you'll see that we have some other ones here as well, such as private.hbs and error.hbs. Not to mention, we've got the amp.hbs as well. These are all additional templates that aren't essential to understand in terms of the structure. Whereas our guide over here, these ones are the ones that are important to understand because they sit on top of your core templates, which are index.hbs and post.hbs. I think this was another long one and we haven't gotten to routes yet, so we're going to get into routes in the next video. But this should drill in what this diagram means and how we can start to customize different routes on our Ghost theme using different templates. 8. Custom Routing: In this video, we're going to go a little deeper into routing. In the last video, we looked at our routes.yaml file, which is in our settings folder here, and we made one change. We moved our index template or our blog, you could say, off of our route route and replaced it with our home template. But there's a lot more we can do within routes. We won't go into absolutely every scenario of routes in this video, as I don't think it's particularly practical. But to get us started, let's look at the documentation. If you're ever stuck on what to do with routes, you can look at the documentation here or if you prefer an explanation by me, you can check out my article where I go deeper into setting up and updating routes. We go into basic routes, basic routes with dynamic data, basic routes with custom formatting, collections, collection filtering, the list goes on. I've organized my guide in a way that I feel makes more sense from a bottom-up understanding. Then I think these guys are done in the documentation, but different strokes for different folks, whichever helps you understand it better. But in this video, what we're going to do is I'm going to show you some examples of some of the stuff they talk about here. That is going to help you to set up pretty much any kind of route you would like in your Ghost site. If we scroll down, we can see the base configuration, which is what we had before, which is a single index gets served on the route route and our individual routes get served on that route route as well via their particular slug. Then you've got the taxonomies as well. If you want to filter by a certain tag, you can use this structure here or this structure. We saw all of that in the last video. But if we scroll down here, we can use some more dynamic routes and something called custom routes. Here we go. Basic routing. What we can do is we can literally set up our own route and then set a template that will show up when we go to that route. Let's do that right now. Let me switch over to my code editor. Let me just create something called special page. Let's just go here, and I'll call it special page. This filename is not following any of our conventions from before, so it's not going to automatically load in certain situations. We're just using this to populate a route. Again, I won't even bother with the default tag this time, I'll just call this special page. In fact, we might want to create a template where we don't have that default wrapper that goes around. Maybe this is that special page. Then in my routes file, what I'm going to do is just add that to our routes up here with a slash on each side. I'm just going to create a route, special page, and then afterwards I'm going to put the template name without the.hbs. I'm going to hit Save. Because we've made changes to our routes file, restart our Ghost server. Now if I go back to my website here, let's move it over to there, and let's go to this special page routes. You can see we're on this special page template, which does not have any of our boilerplate code. Not the most practical example, but just goes to show that we can create our own custom templates and load them on custom routes. An extension of this is custom channels, which I'll get to in just a second because they're a little bit more complicated. What I want to do instead is show you how you can break up your core post collection into different ''collections'' here because I think the wording here from Ghost can be a little bit confusing. There's no actually multiple collections from the perspective of your admin. What I mean by that is if we go into the admin by putting ghost after our site name, you can see that there's only one collection of posts here. There aren't multiple collections. But what we can do is simulate different collections via our routing and we can split those up via tags. Let's have a look in the documentation, what I mean by that, if we scroll down to collections, we've got the default collection which we had before, we've got using a custom homepage which we set up in the last video, and then we can also create two different ''collections'' for something like perhaps we had a blog and a podcast hosted on this same website, then we could split them up using this syntax here. Let's go ahead and do that right now just for demonstration purposes. Let's go into our posts here and let's say we're starting a podcast. I'm going to create one new episode, Podcast Episode 1, and then in here, I'm going to give it the primary tag of podcasts. Of course, for this to work, we're going to have to go through all of these other published posts and change the primary tag to blog. I'm going to move that to the front to make it the primary tag. Hit Update on that. I'm just going to quickly speed through here and do that on every single post. I won't bore you by letting you watch me do it in real-time. Unless I've missed one, we should have all our blog posts with Blog as the primary tag. I think we can see here in the summary that is true. This Podcast Episode 1 is podcast. Let's just say we don't want to show the podcast in the same feed as the blogs, and we don't want to show the blogs in the same field as the podcasts. Makes sense, right? What we can do is create or simulate these different collections. If I go into my routes.yaml file, what I can do here is add in a filter so that it'll only show posts that have been tagged with blog. If we go back to the documentation here, we've got the example right here. This is all we need to add. As the example shows, if we had a custom template just for the blog and a custom template just for the podcast, that's where we would put it in here as well, but we can use the same index template for both. That's not a problem. What I'm going to do is copy the structure for our podcast and then replace all the instances of blog with podcasts. Now we're simulating two different collections on our website and we're doing so via the primary tag. Let's restart our Ghost instance so we can see these changes on our website. Heading back to our website here, special page is still working. That's great. If we go to Blog now, we will see, and this is something we have to actually have a post loop for. I'm going to skip ahead a little bit, just so you can see this actually working. I was going to wait till we write some code, but I'm just going to have to write a little bit of code for you guys to see what's going on here. I'm just going to create for each loop so I can show you what posts are being returned on this template. I'm just going to write title and let's format that as a list. All this code is doing is looping through all of the post titles that are being served on that particular route. If I go back here, refresh. You'll see on the blog template, we can see all of the titles of the posts that have the primary tag of blog. But if we go to podcasts now, you'll see we have just the title of the podcast episode. As you can see here, we are still using the same template, the word index is showing on both, but what we're doing is simulating two different collections via our routes.yaml file. Just to be clear, if we go into our admin, these will all live in the same area. There's just one collection here, unlike other publishing platforms. That's something important to note. That's why I like to use the word simulate different collections because on the back-end, there is just one collection and we're just simulating different ones based on the primary tag. Taxonomies, I think are pretty easy to understand and there's not many options to change these. We can get rid of taxonomies altogether or we can change these words here. But other than that, there's not much you can change with taxonomies. The third type of route that I will go over in this video before we move on are channels. Channels, as it says in the documentation, if we scroll down, past taxonomies, is a custom stream of paginated content matching a specific filter. The best way to think of it is as a set of permanent search results. We can take a filter down part of our collection and create a route for viewing that filtered slice of content. Here on the documentation, you can see it's just the same thing as a custom route, except we're going to put controller channel underneath, and then we're going to set the filter. Let's go and do that right now. Remember earlier we created our own tag and our own author, let's create a custom channel. I'm just going to call it custom channel. But you can make this route whatever you want, obviously. Then I'm going to write controller channel. That's going to tell Ghost that this is a channel and not a normal custom route. Then what I'm going to do is add in the filter. It's going to be tag, needs to be example tag, and then we add in the plus here to add in another condition with the primary author, which is just like primary tag of Christopher. We can also customize this if we want. We could add in another template. We could do a secondary index template if we didn't want it to default to the normal index template here. But I think that should be fine. Then if I refresh over here because we need to refresh every time we change the routes.yaml file, and we go to that routes in our website, custom channel. Now you can see we just have example post because that is the only post that has been tagged with example tag and is by the primary author of Christopher. Actually, there's only one post that is by the author of Christopher and has been tagged example tag. But you get the point. It is filtering all of our posts and creating a specific route for that filtered set of content. This is really important because if you have a large publication and you're putting a lot of content in here, you're going to want to display it in different ways. The ways you organize your content, obviously via tags predominantly, you can also filter via author as well, but tags form the basis of your organization. Through routes, what we can do is use those tags, use those authors, to create custom channels and custom collections. There's a few more examples I could go through that I talk about here, but I think it'd be best just to read these. A lot of these aren't going to apply to your specific circumstance, but the core principles here, custom routes, custom channels, which is essentially a custom route with controller channel and filter, and then splitting up your collection into different collections, which I like to call simulating, and using a particular tag or other defining quality to split up the collection into these different collections. I feel like that's the major stuff you need to know in terms of routing in Ghost. As I mentioned before, I think routing and the custom template structure in Ghost is something that's quite unique to this platform and it gives you a lot of flexibility in terms of SEO or how you want to arrange your content. It's a really intelligent system that once you get your head around, it's very powerful and you can start to customize in an infinite number of ways. Again, if the documentation on the official website does not particularly make sense to you, you can refer to my guide, which explains it in a way that makes more sense to me. But in terms of learning, some people like to learn through documentation, some people like to learn visually, some people like to learn through guides like this, so whatever helps you to understand the concepts. As always, if you don't understand anything, you can always ask a question below. But between these two resources, you should be able to understand custom routing. We're just going to leave it there and move on to actually writing some code now. I'll see you in the next video. 9. Coding within the Post Context: In the previous three lessons, we set up our ghost theme with a bunch of custom templates, as can be seen here. And we updated our routes.yaml file to create our own custom routes. In this video, we're going to actually start to code out our templates because obviously we just put in the bare minimum here just to illustrate the point of routes and templates. Obviously, we don't want this content to be the only content that shows on the about page. It's time to actually start bringing in some of the dynamic content from our ghost admin and displaying it inside our theme. Otherwise, what's the point? What I'm going to do is head over to the documentation and I've got the page on context already open. Context is the next section in my guide as well. Remember the four sections of templates, routes, contexts, and helpers. Context is basically like scope in ghost theme development. What we need to know is what data we have available to us in which templates. As it states in the official ghost documentation, there are six different contexts, and I go into each of them in more detail on my guide here. If you want to see the full list of attributes on each context, you can go to the documentation. But let's get stuck in and start to code up our templates. Over time we will grow a deeper understanding of what these contexts actually mean. If I head over to my code editor here, I'm going to close down all of my open windows here, or open tabs rather. I'm going to go into the index.hbs template. Now before, as we saw it in order for me to demonstrate channels, I had to put in our first Foreach loop. But I didn't go into much detail about how this works. Obviously, on templates like index or any other custom index templates, we're going to run through a loop of posts. The first tag we're going to see is the tag for Foreach. Now what this does is it runs through every post that will be displayed on the particular route that's viewing this template. Then we'll have access within this block to all the attributes on the particular context of what we're looping through. In ghost themes, the only time we're really using a Foreach loop is to loop through the different posts in our overall collection. Then inside each iteration of the loop, we have access to all of the attributes of the post context. Alright, so let's go back to the theory here and look at the contexts. We can go down to the post context here. If we scroll down, we can see the list of object attributes. As you can see, I used Tidal in this particular loop, but we have access to the URL, the featured image. We have access to all of this data within each iteration of the post loop. Now, of course, we have access to this information on a post template as well for an individual post, I'll show you how to open up that context very shortly. But for now, understand that inside this Foreach loop for each iteration of this loop. So the content inside this block, we have access to the post contexts. That'll give us the ability to pull off data from every single post contained within our ghost website. What I'm going to do is I'm going to build this out to something better than just a list. Up here, let's type in blog. Here you can see our list of blog posts, but none of them are linked up. So this is pretty much useless. This is not very useful to us right now. One thing I want to do before we continue, I just thought this, is get rid of this podcast episode and remove all of the blog tags as well because we're not going to be running a blog and a podcast on this particular website. That was just for demonstration purposes. So bear with me while I do this, I will fast-forward and I will meet you after I remove the blog tags from all of our posts. That's that done. The main reason why I wanted to clean that up is because we're going to be outputting the list of tags on each individual post. I didn't want to have blog on them. Now that I've refreshed the page, you can see we've lost all of our blog posts here. I just need to go back into my routes.yaml file and set it back to what it was before. I'm going to take away the filter and take away this podcast collection effects. I'll take away some of these other routes here because we don't need to use them. That was just a proof of concept. I'll get rid of special page here. Delete that one. That's all we need for our particular ghost theme. I'm going to, of course, restart our ghost instance because we have made changes to the routes file and removed a handlebars file. If I refresh over here you can see the full list of all the posts we have on our site. Let's start to build those out. If I close down the routes.yaml file. What I'm going to do is start to use some of the attributes we have on the outpost object and build out a nice postcard that we can then use to click on to get into the single post listing page. Here is the reference guys. If you are ever lost of what information you can get off a particular post, it's all listed here on the ghost documentation. But for now, I'm going to start writing out these posts. Instead of a list here, what I'm going to do is, first of all, a little bit of formatting. I'm going to put this all in a page width container so that it's not extending the full width of the page. Unless we're on a smaller screen size, of course. What I'm going to put here, is all posts. Then I'm going to add a little bit of a description. Here you will find all posts. Alright, then I'm going to create a container for these posts. These class names relate to classes I've already set up in a CSS file. If you're wondering where my decision-making on this is coming from, that's why. But for you is going to be different depending on what styles and structure you want for your own ghost theme. But for me having an inside at div with the class of posts, I'm able to format them in line. Then what I'm going to do inside each iteration of this loop is open up a post-card and then give it the additional class of card, which is a bootstrap class. We'll give it some nice formatting. Then I'm going to open up an if tag. This allows us to first check if the attribute is actually set. If there's a featured image, we're going to execute the code inside this block right here. And if not, I'm going to put in, L statement, we're going to execute the code inside this block. I still want to placeholder the image to show if there isn't a featured image. I'm just going to put in image SRC. Let's just say this is our placeholder image. This website allows us to put in a placeholder image of whatever dimensions we want. So I'm just going to put in this dimension because it matches the photos that we already have, in our ghost site. Then for our, if true statement, I'm going to put in, usually I'd like to optimize these images based on certain screen sizes. But for now, I'm just going to give it a single SRC and I'm going to use the help at image URL. We haven't gone into help us yet, but there are little keywords that help us do something within ghost theme development. Then I'm going to take the featured image. I'm going to ask for the medium size. Also, I'm going to add in another conditional here for the alt if there is an alt. I'm going to go open up another if here. If a featured image alt has been set for this particular post, then what I want to see is the alt tag. Inside the alt tag, I want to put in the value of the featured image alt. I'm also going to give it a class for formatting. I'm going to give it postcard image, which is a class I defined, and then the bootstrap class of card image top. If we want to, we can save that and have a look at what we've got so far. We can go back to here and you can see nothing's coming through at the moment. We've got our postcards and looks like we might have made a mistake here on our SRC. My mistake was saying featured image. That name would make more sense to me, but in Ghost, it's just a feature image. I'll save that, head back over, and now you can see that the images are showing up for each post. Let's continue to build our card here. After the featured image, what I'm going to do is open up a div with the class of card body. What I'm going to do is just follow the structure of the card component as is present in Bootstrap Version 4 I believe, no 5.1.1. If you have the same Bootstrap version, you'll get the same result. I'm going to do this pretty quickly here because this is a HTML and CSS class. I just want to demonstrate to you how we actually bring in some of this dynamic data from Ghost. We've got a h5 with a card class of card title. I'm going to nest a link in there for our URL and then, of course, put a title in there. Then underneath that one, I'm going to add a p tag with the class of card text and put in the excerpt of the post there. Now if we save and refresh our page in our browser, you can see we've got these cards here with the image, the title, and an excerpt from the post. If I click on the title here, it'll take me to that post's template, which we haven't actually created the code for just yet. We're going to build on top of this in future videos. But for now, let's actually code up our post template because that's important that we get right. We've got this alternative template running at the moment. As you can see in the title which we set up before, it says custom template setting goes to admin. If we look at the example post, it says this template is specifically for example post. I'm going to navigate to the third one, which as you can see is just using the standard post template. Let's start to code up that one as well. If I go into the post.hbs file, remembering guys, that at the basic level, all our website needs is an index.hps and a post.hbs. If you know how to code both of these, theoretically you can do the whole website. What I'm going to do is do like we did similar with the index, open up a page container and then here is where instead of creating a loop, which will give us access to the context of a new post inside every iteration of the loop, what we're doing is we're accessing a single post here. What we need to do in order to access the post context, even though we're on a single post, is open up a tag that looks like this, hash post. It's a very simple one. Then we close with slash post. What this does is allows us to access the post context just like we had here for each individual post. Now we can access all the same attributes within a single post. I'll go back here and what I'll do is add some boilerplate code. I'm going to have a post header. Sorry, I'll go back and give that a class of post header, bring back that h1. But now it's going to say, actually I'm going to give that one a class of post title. Inside, we're going to put the title of the actual post. Instead of putting a static title, a hard coded title, we're obviously going to let the title of the post itself come through. Then underneath, because it is a blog post, we're going to put in the date that it was published. I'm going to put that in a span and we use the helper here of date to format our published at date. We've got the attribute of published at and using the date helper with this attribute here of format, we can then set our format for the date. I'm just going to set it to quadruple M, double D, and quadruple Y. We're going to move on past that header section and then create a section with the class of post content. Then in here, what I'm going to do is insert the contents of the post and that is done for the content attribute. I'm going to hit save on that, refresh the page over here and voila, we've got the title of the post. We've got the date that the post was published and we've got all the content that is automatically populated inside our tag here, post content. You can, of course, verify that using your DevTools. Of course I recommend Google Chrome, but whatever you prefer to use. Here you can see all the code we set up in the template, but with the dynamic values coming through inside that markup. If I go back to blog and then I click on the next post, you'll see it's the same template but with different content. There's a little bit of clean up that needs to happen here with the different links and stuff. But you understand the concept, we're putting through the content into this template. We're putting through the title and the published date, and so now we have somewhat of a functioning website. To finish off this video because it feels like it's already quite long. What I'm going to do is put in some code for the About page. Now, the funny thing about the About page is that the page context is virtually identical to the post context. That's why if we go back to our diagram here, that we can load a page on the post template because we're using the same context regardless of whether it's a page or a post. Let's see that in action right now. I'll scroll down to context, back to what we had before, which was the page context. The page context, as it's written here, is virtually identical to the post context and is accessed using the same block expression. What I'm going to do is just go ahead and copy the code from post.hbs over to page.hbs. What I'm going to do because the big difference between a post and a page is mainly the date. I'm just going to remove the date from that particular template, hit save on that. Now, if we go back to our About page, you'll see that we're not getting the result we're after because we're actually using a specific template for About page. But let's go to a different page, I believe there was one on privacy. Sorry, I misspelled that there. If we go to the Privacy page, here you can see we've got the same format. We're inside our container, we've got our title and we've got our content for the page. So really, the only difference between pages and posts is that we can't loop through a list of pages, but we can loop through the list of posts. We have access to the post contexts within here. We have access to the post context within an individual post, and we have access to it when working with pages as well. The post context is actually identical to the page context. You could argue that there is no page context, it's just the post context used for pages as well. I'm going to wrap it up there for this video because we've got our core building block sorted. We've got our blog page where we can see all our different posts and we can now click in and view an individual post. Where we haven't covered yet is all of these additional optional templates. How do we work within the author context or work within the tag context? What should we do about these specific custom templates that result to certain authors, certain tags, certain pages, and certain posts? All of that we'll cover in the next video. 10. Coding within Author and Tag Contexts: In the last video, we started coding out our index.hbs, post.hbs, and page.hbs templates and we worked with the post context. The post contexts applies to all three of these templates whether we're in a loop or we're in a single post or page. In this video, we're going to talk about the other contexts, author and tag, and if we have time, we're going to build out our theme using these additional templates that we created for specific posts, pages and authors. What I'm going to do is I'm going to head back to my website here and I'm going to navigate to one of our author routes. If we go to the Christopher author route, you can see that we've got a specific template. I want to go to the regular author template, which I believe if we type in Ghost, we should be able to get to. There you go. I'm going to start working on the author.hbs template next and the context I'm going to be working on is the author context. Let's go into the documentation. Click over to Contexts, scroll down and you can see the list here. We've got index, which actually doesn't seem to have a page on this documentation. We have page which is essentially the same as post. Post, obviously. Then we've got author and tag. We've got error as well, but we're not going to be learning about error in this class. This is a very simple one. We don't even have to enter the contexts using the tag we saw in the previous video. We can just output these tags inside of any error template that we're working on. That's just a minor detail that we're not going to go deep into in this class. But what we are going to cover is the author and tag context. If I click on the author context, as it says here, "Authors in Ghost each get their own page, which outputs a list of posts that were published by that author." Essentially on the author template, we have access to this author context, but we also have access to the post contexts via a 4h loop. If I scroll down here, here are some of the author object attributes. What I'm going to do is let's create a listing page for our author. Just to get started, let's copy across the code here on our index.hbs. I'm going to cancel out these two and let's open up author.hbs. Let's paste that code in here. Instead of all posts, I'm going to say, "All posts by", and, here you will find all posts by. Let's save that. Let's refresh over here. You can see here's all the posts by the author Ghost. We haven't brought in those dynamic values yet but you can see in the loop, it's excluding all the posts that were written by authors other than Ghost, and that is essentially just one post. All the posts before that post are now showing up on this page. What we have to do is fill in these dynamic values. I'm going to show you two ways to do this. Without opening up a block, we can just scope into the author contexts by running author and then the attribute, I believe it is name, and then we can just copy that down here. Hit "Save" on that, refresh the page and you see all posts by Ghost. Here you will find all posts by Ghost. If there was another author that used the same template, you would say that author's name instead of Ghost but hopefully, you get the point. This is a dynamic value. What I'm going to do is I'm going to make this a little bit more realistic. I'm going to also, instead of having to put author. in front of the name each time, what I'm going to do is open up the author context. I do that exact same way that I opened up the post contexts in the previous video by putting a tag here, starting with hash and then putting author. Then I'm going to close it here. What this should do is give us the same result. I'll indent this code, refresh and you see we've got the same result. What I'm going to do is I'm going to reject this page and bring in the author photo as well. What I'm going to do is the container up here is going to be a header, and then I'm going to have two columns. I'm going to have a column for the user and give a card for the author. Then I'm going to have this content moved over to the side in a second column. What I'm going to do is instead of having all posts by name, what I'm going to do, drag this out of our little author block expression here. Create a div with the class of page header. Put that in there. Still going to have all posts by the author and put that there again. Then what I'm going to do is create a container with the class of page with sidebar. Then in here, after I open up a div, the first column, I'm going to bring out some content from author. Put that in there. Then the second column, I'm going to put the rest of this code here. Each div is representing a column in this case. I can just indent that properly there, delete some of this wide space, and here inside our little sidebar, I can put in an author card. Again, I'm going to use the card class and that's going to give me access to styling within Bootstrap. Open up a card. The first thing I'm going to want to do is create a image with the class of card img top and the SRC for that is going to be coming from the author. I'm going to use the image URL Helper and then afterwards access the attribute on the author context of profile image. Then for the ALT is just going to say, "picture of name", which is going to be the author name. Then just like before, I'll set up the card body with a h5 with the class of card title, inside, I'll put the name of the author again. If the author has a bio, I want to show it. I'm going to output a HTML element with the class of card text inside here. I just need to put "bio". That HTML will only show if there is a value set in bio. Finally, I'm going to put in an author meta area here and I'm going to put in some code that might look a little funky here. I'm basically using the plural helper here to show a number with the right pluralization. It's going to take the pagination title of what we're currently looking at. If it's empty, it's going to say, no posts. Then defining for singular, we want the number followed by post and for plural, it's going to say the number of posts with posts. As you can see this plural helper here, it takes a certain number and if it's zero, it'll output no posts. If it's singular, it's going to say one post and if it's plural is going to say that number posts, so this helper helps us output plurals. We are going to hit "Save" on that, and let's refresh. It looks like we've missed something here, let me go back. Just going to move this over a little bit so we can see more of the screen over here. Here we are, I can see we've misspelled singular. If I save, refresh over here you'll see that we've got our card here. Formatting is not on-point just give this a max width of 18, REM refresh over here. Getting a funky results, haven't got the right div set up. I think it's this one right here, and now we're getting the result that we would expect. As you can see here, what we've got is in the first column, we've got the user. It's Ghost with its little image here, this is its bio, which is set by Ghosts themselves and this is how many posts that this user has written. Then of course, you've got the list of posts that are generated by the for-each loop and this will be limited to the posts by the user of Ghost. Let's go now and do that for the tag template, very similar thing. I'm going to copy across author and instead of author name, we're going to change it to tag name. Instead of all posts by, we're going to say all posts with the tag of. Here you'll find all posts with the tag of making sure that change author of the tag. Now in here, instead of going into the author context, we're going to go into the tag context. Again, just changing the keyword author to tag, and now substituting some of these values. Instead of profile image, which is what we have available on the author context, we have feature image instead and then instead of pitcher of name, we'll say pitcher for tag name. What I'm going to do as well is check to make sure that we have a featured image set. Let me do that, it's just like we did before. If feature image and then this and then end to that if block, like such. All right. Cool instead of bio, we're going to do description and then of course we're going to get rid of this author meta. Going to hit "Save" on that. Let's go to a tag that isn't example tag because remember we've got a special template setup for that, so the only other option is getting started. Here you can see all posts with the tag of getting started. Here you will find all posts with the tag of getting started. Pretty self-explanatory, right? Here you can see we've got our name of our tag there. If we wanted to add an image, right now it's detected that there's no image set because we put our if statement there. But if we wanted to, we could go into our admin, go into our tags, getting started. I'll just upload one from Unsplash. Let's just search for getting started, see what comes up. That looks all right. Let's hit "Save" on that. Let's go back here. Now you can see we have an image populating in that card image top area. That's essentially how you work with the tag and the author context. If you're on the author template or the tag template, or a specific tag template like we can see here. Or a specific author template like we can see here. Then you have access to the author or tag context. Now before we move on and talk about custom queries and partials, I just want to build out the rest of this theme. I think we haven't spent too much time in this lesson, so we've got time to just quickly fill out the other additional templates that are inside our theme at present. I notice we haven't done anything with the homepage. Let's just say welcome to and then we'll put site title. Using that global site object, putting in title, just going to click over to here. There you go. It says welcome to Skillshare. You can see there's a little bit of an issue there. I think we've got a bit of an error in our default hbs. I accidentally put the header dash logo in AH ref. Apologies if you noticed that from the start and was screaming out. But if I refresh over here, you'll now see that to click anywhere else and then click there, it'll take us back to the homepage. Then also on that homepage, which will add a custom query to in the next video. But for now, let's just put that all in a page width container. Save, and now it's in the page width container. All right, so what are the other templates that we haven't customized yet. If I go to the about page, for example, we've got this page/about.hbs. I think in this case, I want the page-about.hbs to look exactly the same. I might just delete that one altogether, refresh my ghost instance. Now if I head back and go to the about page, we've just got a normal page template running for the about page. Also, I want to add a navigation for blog now. Let's do blog and then add in that route there. Hit "Save" on that head back here. Now if we click on "Blog", you can see we've got all our posts. We don't have a way to navigate to specific authors and we don't have a way to navigate to specific tags. What I might actually do is I might pause it there and then we will do that in the next video. We're going to add the tags and the author to these posts. Then we're going to customize those special templates that we had. For instance, our custom header template, which we have here, which you haven't customized yet, and I'll example post template as well. We've got these other ones working fine. But those other two ones we need to customize as well. This video is getting long, so I might cut it there, guys, and we will do that in the next video. 11. Custom Queries and Partials: Time got the better of me in the last video, but that's all right. We're just going to take one extra video to cover custom queries in partials and then we'll use the video after that to finish off our ghost theme, fill out those extra templates, and then we will have a simple yet completed ghost theme. Where you want to take it after that in terms of extra styling and imagery and custom templates is up to you, but by the time we finish up, you should have a pretty solid understanding of how ghost themes work. What I want to do is jump ahead to custom queries and partials right now, and then we will get to customizing those specific templates in the next video. First things first is you would have noticed we have some copy and pasted code for owl post-card. The postcard is always going to be exactly the same no matter whether we're on the author template, the tag template, or the index template. That means it's a perfect candidate for something called a partial. Partials as you can see, we've got our folder set up here for our different partials. What we use partials for is sectioning off little bits of codes that we can then reuse in our project. Instead of writing this code and copying and pasting it out into multiple templates, what I'm going to do is put that into its own partial. I'm going to call this post-card.hbs. I'm going to take our card HTML and handlebars code there, cut that out, and put it in here. Let's fix all the indenting, save that. The way we call a partial, now that we have moved it into there is by using this greater than sign. We put the greater than sign in, open up a string, and in here we put the name of the partial postcard. Now if we go to the index which is our blog page, says partial postcard cannot be found. I believe that's because we haven't refreshed our server. Remember we added a new file, so we need to refresh our ghost instance. Good little reminder there. Refresh. Now you can see we've got the exact same look on that index template. What I'll do now is I will replace the duplicated code with just this little code to the partial. That's going to make our codebase a lot cleaner. The other thing we can do, which I'll show you in the next video, is create custom partials for navigation and for pagination, which if we use them in ghost without those partials, it'll output default code that we don't have to even set ourselves. They're like special system partials. I'm talking about, for instance, when we go to default.hbs and we have this navigation tag here. This is almost like a special version of a partial, but without having to set it in our partials folder. If we did want to customize our navigation, we can set a partial in there for this navigation and also for pagination, which we'll cover in the next video. Stay tuned for that. Essentially though, that is the basis of partials, taking out code that you are copying and pasting into multiple places, putting it into the one location and just calling it where you need it. The second concept I want to talk about in this video is custom queries. Custom queries allow us to draw data from Argos site and put it on any template. I think a practical application of this is for a homepage. Sometimes when you're on a blog homepage, you want to see the most latest post. Using a custom query, we can actually do this. I'm going to go into the documentation for custom queries, which I believe we can find in either context or helpers think maybe in help us here. Here we go. Inside functional helpers, we can do a custom query with this, get help. I'm going to click on that. We can read about how we can get certain information from our back-end and output it wherever we want. Inside out goes theme. Here we go. Simple examples right here we can get our posts and that allows us to create a post loop. Here are not coded that I'm going to navigate over to our home.hps template. What I'm going to do is let's build this out a little bit. I'm going to add a div with a class of home header. Then after the H1, I might do a site description. Take the description, attribute off the site global object, and put that in there. Then after this header, I'm going to open up the get helper. I'm going to request the posts and I'm going to make sure it includes tags and authors. We'll be using that data in the next video. I'm going to close that get block there. Then I'm going to put in H2 and say latest posts. Then here we can create a for-each loop and have access to the posts even though we're not on an index template. I can go for each posts, close the block there. I'm also going to put that inside a div with the class of posts in line. Again, all these classes, it just for formatting and structure. It relates to the CSS I have in my main file. Don't get confused by that. That's just me giving some formatting to this. In this loop, I can output the partial that we created post-card. There's two other things I want to do here. I want to limit this to only three results. The latest three, and make sure that it's sorted by most recently published. Published at descending. Save that. Head back over to our homepage. You can see, welcome to Skillshare thoughts, stories, and ideas, which is the default description of a ghost website. We can obviously go into the settings of our website and change that. We go general publication details, title, and description. Let's just say instead of Skillshare, it's ghost site and say this is an example ghost site, save those settings, go back, and you'll see that updates the homepage. It updates the logo, it updates everything. This is an example of ghost site. But most importantly, what we have here is a list of the latest three posts on the website, so that users can see a preview of the posts on the website without having to navigate to the blog page. This one's a bit of a short one or shorter one compared to the other ones. But in the next video, we're going to cover what we fail to finish off in the last video, which was building out these custom templates. The ones specifically for the example post specifically for custom templates set in the ghost admin. Add the ability to navigate to different authors and tags from the postcards themselves. In the next video, we'll cover that. Then we will wrap up our project and get started on taking our side live. 12. Completing the Theme: In this final lesson, before we start to look at hosting and making our site live, I'm going to add in some links to author and tags here for each post and then also we need to update these custom templates. After we do that, we should have a pretty well-functioning ghost theme. Obviously, there's so much more we could do here, but this will complete a basic project for us. The first thing I want to do is so that we can navigate to the individual authors and the individual tag page, what I want to do is add the tag and author to our postcards. I'm going to go over to blog here so we can see all of the postcards. I'm going to go into my code editor, once go over to postcard and I'm going to start adding some code under the excerpt. The first one I'm going to do is a link to the author. I'm going to format it as a Bootstrap button. I'm going to give this the class of btn btn-primary. That's the href fields, class. Then I'm going to set the href, which is going to be the primary author URL. It'll just show the primary author, which is a similar concept to the primary tag, just in case there are multiple authors there. Can close that and then I'm going to say by primary Now before we leave that there, there's one consideration to make and that is that we don't want to have a link to the primary author when we are currently viewing that postcard on the author page. If we're in the author context, we don't actually want to see a link to the author because that would just be a circular link. What we can do is we check whether we are in the author context by using this special helper is. But in this case we want to check that we're not in that author context. We can put in this little character here to look for the opposite. This code right here is going to execute if we don't have access to the author context. I'm going to close the is there and then now for the tags. I'm going to go card-tags and then here all we need to do is just put the keyword tags and just writing tags like that, as you'll see in the documentation, will output a comma separated list of tags, and each tag will be a link to that tag. If I refresh over here, now you can see we've got a button to go view the author and we've got our different tags that have been set on that post. If I click on the buy ghost" button here, you'll see that we've got our template that we set up before. If I click on "Getting started", and you can see that we've got our template for getting started that we saw before. But remember we set up a different template for Christopher Dodd. My author route has a different template and we have a different template for example tag. I wanted to put those in there first so we can easily navigate to those. Heading back to the blog here, we need to customize those templates. We also need to customize the example post. We've set a template specifically for example post and we need to change this custom template that we set in the ghost admin as well. We've got four templates that we need to fix up here. Let's go into the code and let's fix up the, what should we do first? Let's do the Christopher author template. Let's go into author-Christopher.hbs. Because I know we're going to be running through an index, I'm going to copy and paste some code from our index here and put that in here. Instead of having a heading up here, I've created a class in our CSS for author heading. I'm going to go above this page-width to create a full width header. I'm going to enter the author context, then close it so we don't forget. Inside here, I'm going to create a div with the class of author header and inside that I'm going to have an h1. I'm going to put a small in here as well. Inside the small, I'm going to say posts by and then after the small output, the name of the author, which given this template only applies to the author template for Christopher, we don't actually need dynamic values. We could hard-code these in. But I think it's a bit cleaner to have the dynamic values in case we want to change this or copy and paste this code across. The other thing I want to do is have the image of the author, so we've got background color. This is something that we will need to be dynamic because we could always go in and change the author cover image. I'm going to use the image URL helper again and then afterwards put in cover image, which is an attribute on the author object. Hitting "Save" on that, heading back over to here. You can now see we've got this header. It says posts by and that has the author name, and then it has the list of posts. You can't see it right now, but it's supposed to be a background image there. I'll go into my Settings here, go into Staff, go into Christopher Dodd, and let's change the cover. Let's upload an image. We've got a folder here with some examples. Let's just say that that is my cover image. Now if I go over here, it's not showing up and that's because I mistakenly put background color in here, not background image, refreshing over here and you can see now I have this banner here. If we go on to our Ghost author page, you can see we've got the standard template which is having the author card in this left column here. But if I go specifically to Christopher Dodd, I've got this layout instead. That allows us to customize certain layouts. Maybe this author is particularly special and needs a custom template. Well, in that case, we can customize it by using our knowledge of the theme structure that we established earlier in this class. Let's do the same thing for our tag now. Tag-example-tag, I'm going to close down these open files again. In tag-example-tag, let's just copy the same format we had in author-Christopher. I'm going to move this over. Instead of author here we want to put tag because we're entering the tag contexts now and instead of cover image, we need to use the appropriate image for a tag which is feature image which we saw before. Then we're not going to have posts by, we're just going to have the name of the tag. This is a bit of a misnomer here because we're copying across a header that we set up for author. Actually we can go in to our CSS file and just fix it right now, author header, if I wanted to be bit cleaner here. Let's just say image header. I'll fix that up for you guys right now. Let's just use the class image header instead. As we are using this on more than just the author template. Hitting "Save" and moving over to here. Here is a link to the different tag templates. We can go to the Standard Tag template, which is running for everything other than example tag. But if we wanted to see that special one, we can click on "Example-tag" and you can see we've got our header now. The formatting hasn't come across, which is likely because of ghosts intense caching. I'm just going to open up DevTools, allowing me to empty cache and hard reload. After it empties its cache and hard reloads, you should start to see those CSS changes taking effect. There we go. We've now got our nice little header here with the name of the tag and the tag image in the background. Now we've just got our templates specifically for the example post, and then we've got our custom header template that we set in the ghost admin. I'm going to start with this one because it's going to be the same format. I'm going to go to custom header. Again, I'm going to copy from here. I'll get rid of this right here because we're not looping through a list of posts. I will change this to post so that we can have access to the post context. I'll move this to the ends because we're going to need access to the post contexts throughout. I'm going to keep this image header and instead of cover image, feature image is the attribute on the post context. Again, not post by. We're just going to have the title of the blog post, and then we're going to copy across from the post template here, this bit of code. Let's save that, head back over. Here, you can see we've got the title in two places, so I'm just going to remove the title here. Save and here, you can see we've got our alternate template, which puts the featured image behind the title and then has the title overlaid on it. It still has the date, it still has the content, but then we also have this. Now let's do the final template. Let's go into blog, go into example post. For the example post, what we're going to do is head back into the code again. Let's just say that the template, for example, post will be the same as post, but we'll just have a list of related articles down the bottom. We'll only do that for this specific template. What we're going to do, I'll close all this down again, and let's go to post example post and we will copy the content over from the standard post template. I'll do that. Head over to here, paste that in, and now we'll get the exact same look as before. But this time what I want to do is I want to add, and this is another custom query which we learned about in the previous video, what I'm going to do is I'm going to write a get post underneath here. What I'll do before that is I will create an article with a class of, this will be a dynamic value, the post_class, and then I'll wrap our article up in that. That way we will differentiate it from what we're about to do down below, which is to open up a custom query. Let's go for the posts. Let's include the tags and authors because we want to have those attributes there so we can access them and have those links that we created at the start of this lesson, then I'm going to filter by the primary tag. If the primary tag is equal to the primary tag of this post, then I want to show those similar posts again. Let's end that. Get featured-posts is the class. Then I'm just going to add in the title here, similar articles. Then create a container for these posts and let's set up the loop. We're going to have it just like we did on the homepage. I'm going to grab this loop right here and show the latest three that we have that share the primary tag of the post we're looking at. I'm going to hit save on that, refresh over here and you can see it brings through the original example post. We just need to remove the example post. I'm going to add another filter here that says ID is not We save that, we'll get no similar articles, so what I might do is do the opposite and put this on the standard post template and this other one can be a clean version. Just wrapping this around with an article tag, class, post_class, and then underneath here, I'll post that in. Grab this code, put it inside the article, fix up a little bit of nesting here. Then if I hit "Save" on that, you'll see, for example, posts, we just have the post with no similar posts, but if I go to my blog and I go to any of our welcome articles, this one of course is on a different template, so I have to add that exact same code to our custom header as well. I think we need to get rid of the whitespace here, it doesn't seem to like that. There we go. We got our author back now and our tags here. We've got similar articles on here. If we go over to our custom template over here, we haven't got the same result. I think that's because we are still in the post context. I'm just going to move that up here as we don't need it down here. Save, refresh over here, and now you can see we've got our similar articles coming through. They're coming through on the custom header template, they're coming through on the standard post template, but they're not coming through on the example post. Of course, that's just one example of how we might have different blog posts showing different templates. I want to add in a few more things here. I know this video is going to be particularly long, but I want to show you some things that are going to make this site even more complete. One of the things I want to do, here, I just noticed that we've got that same issue again. Let me just go back to the homepage here. Make sure there's no space between tags and authors. There we go, that fixed it. What I'm going to do is something I've noticed is when we click into a post, there's no navigation that head back to the blog. Of course, we can click up here in the header, but I think some breadcrumbs would be nice here. So I'm just going to add some breadcrumbs as well. I'm going to use the standard format within bootstrap. I'm going to go into, let's just say post first. I'm actually going to use this on all the posts templates, so I might just go straight into making a partial. I call this breadcrumbs.hbs. Then I'm going to start writing out some standard bootstrap code. I'll fast forward through this because the structure is just standard bootstrap components. Then I will check in with you at the end to explain. Very simple. The only dynamic value in there is the title of the current post. Otherwise, we're just going to have a slash and a link back to the blog. Now we can include that over here in our post. I'm going to put it just above the post header here. The syntax for including a partial, as we saw before, open up that greater than sign and then put in the name of your partial in double-quotes. Let's just check that that worked. There you can see we've got a nice little formatted breadcrumbs there and let's just add that to all of our custom post templates. Here we go, example post. Then for our custom header, remember we can use this custom header on pages as well. I want to put in an extra check here to make sure when not on a page. I'm going to do if not is page, then we will put the partial for the breadcrumbs in. We don't want the breadcrumbs to show if we're on a page. Of course, this is a personal preference. You can still have the breadcrumbs, but it just wouldn't make sense with the link back to the blog. If I went into my about page, for instance, and I changed this to our custom header template, I wouldn't see those breadcrumbs. Whereas if I go into the post here where I know we're running that custom template, I can see that the breadcrumbs show up. One more tiny thing and then we'll wrap up is I believe that we're going to need some pagination here. As we saw before with the navigation tag, if we just add in a tag with the word pagination down here, it'll work, but it won't look as good as we might hope. What I'm going to do is add in some pagination and then I'm going to customize it by creating a separate partial. First of all, in order to actually enact pagination, because right now there is only one page, I'm going to limit the posts to six posts per page. The way we do that is in our package.json file, scroll down to towards the end, there should be this config object, if not, simply create it. Here you can see the posts per page. I'm going to set that to six. I believe we will need to restart our Ghost server after changing that so going to run, Ghost restart. Now if I head over to our blog page, you can see we're only seeing the six latest posts. We're going to need some pagination in order to navigate to the other posts. I will go into our index.hbs file and after this loop, I will add in the simple tag, pagination. Refreshing over here, let's have a look at how that looks. Here you can see we're at Page 1 of 2, and we can look for older posts, and here it automatically outputs newer posts. I'm just going to make that look a little nicer by using Bootstrap classes. For that, what we need to do is set up a partial in order to customize pagination. I'm going to do pagination.hbs and for this one, to save time, I'm going to copy and paste some code in, and then I'll explain it. This is just the standard Bootstrap structure for pagination. What we're doing here in terms of the handlebars code is that if there is a previous item, we want to have a link to it right here. The text to that link is going to be previous and we use the page URL helper to put the link to the previous page in there. Then we're going to have the current page as a dot point and then if there is a next page, we're going to do the opposite of what we did up here and have a link to the next page. Of course, we just created a new file, so we're going to need to refresh our server. Then heading over to our index page here, you can see we now have a nicer Bootstrap formatted pagination. There you go. Works perfectly and it shows you what page you are on currently. As I said at the start of this lesson, there is so much more customization and features we can give our Ghost site, but this essentially covers all the main points, all the fundamentals for your Ghost theme development needs. To recap, we have built out the most important templates like the post template. We have built out the index template. We have created a home template which lists the three latest posts. We have a standard page template which shows our about page, and then we have templates for the authors using the author context. We have an alternative template for the author, specifically by the slug of Christopher. We have a template for filtering by a certain tag and then, again, we have an alternative template for filtering by a specific tag. Then finally, we've got two alternative post templates. We've got this one with the custom header and then we've got this one, which is essentially the same as a normal post template, but without the similar articles. I want to note here that I did use quite a bit of syntax that wasn't necessarily explained. I wanted to jump in here and make this as practical as possible and show you how we build out our Ghost theme. But if you're ever confused about some of the helpers that I used here or the syntax, the best place to look is the documentation. Something that we didn't go deep into in this class was the list of different helpers but as you can see here, we have so much here in terms of checking the context of the current route. We saw that a little bit in this class, obviously testing very simple conditionals with if statements using get to create custom queries, but you've also got other things like the site objects, which we did use a bit, navigation. We've got all sorts of different data we can access and these utility helpers here, which we saw. Stuff like adding in code from Ghost automatically in the head, putting in dynamic CSS classes, we can even render the estimated reading time for a post. These are all options within Ghost, and if you want to look them up, they're here in helpers. I also talk about it in my article so if you're interested in reading more, you can learn about the different contexts and how Ghost themes are coded using handlebars here. Handlebars comes with its own syntax, but has also been extended in Ghost themes. The theory behind all of that, I encourage you to read in this guide, it will be published by the time you're watching this video, but I don't want to get too bogged down on making this course highly theoretical. The feedback on some of these courses that I've done before is people want to see this theory put into practice. I haven't focused too much on the syntax of coding Ghost themes, but I think what's very important to note is understanding if I scroll up this diagram right here, and routing, because that forms the structure of your Ghost theme. If you don't understand these fundamentals then you can get yourself really caught out and wondering what you're supposed to do to create different templates for different routes. This is very powerful and sits at the core of Ghost. The other stuff like what specific syntax to use to check for things, you can easily look up and we've seen a few examples with coding our own little Ghost theme here. I encourage you, as we'll get into in the class project, to create your own version of this but in the next lesson, we're going to learn how to actually publish this live to a website on the Internet so that we can actually showcase our Ghost theme to the world and hopefully use it in a production environment. I'll see you in the next video. 13. How to Publish your Theme Live: In this lesson, we're going to talk about putting out Ghost theme in a production environment, i.e. taking our Ghost website live and publishing it on the Internet. I've got a few tabs opened been that are going to help us with this aim. I've got the article here from Ghost on how to install Ghost. Here you can see we did one of the options here, which is local install. But if you scroll down for Cloud hosting, there's actually three options which we'll talk about in just a second. Then also what we're going to do is look at the GitHub Ghost integration and that allows us to do simple continuous integration of the Ghost theme, deploying directly to our Ghost website. Basically, what we have to do when we make changes to our Ghost theme code locally is push that code to our repository and GitHub Actions will build it for us and automatically update our live Ghost website. This is a very powerful integration in terms of updating your site over time. Once you go live, you might want to make modifications here and there. You'll once again do that locally and then if you just push those changes to GitHub with this integration, then it will go directly onto your Ghost site. Now, getting back to the first article here, how to install Ghost. The first line, as it says, the fastest way to get started to set up a site on Ghost is on Ghost Pro. Scrolling down here to the Cloud options again, DigitalOcean is what I use and I use DigitalOcean because it's cheaper than Ghost Pro. But of course, it's a little bit harder to set up and it's not something that we will be going into in this class. They used to be a service that Ghost provided cord Ghost Valet and they would take care of all the installation for you for a one-time fee. That doesn't appear to be available anymore. So the second website that I set up on DigitalOcean, I had to go through the steps myself and it was a bit of a tricky process for someone that's not too familiar with server configurations. For this class, what I'm going to recommend and what I'm going to walk through with you guys is to use Ghost Pro, which is going to be the simplest way to get your website up and it's essentially using Ghost themselves to manage the hosting for you. As it says here, official managed hosting, I'm going to click on that option and as you can see, there is a cost to this. Unfortunately, any website hosting, there is going to be some cost. If we scroll down here, we're going to need at a minimum, the creator plan because we are using custom themes. If we take away the annual discount is going to be $31 a month. With annual discount, it's going to be minimum $25 a month. But the good news is, and for our purposes with learning how to do this, we can test it out by starting a 14-day trial. I'm going to click on that one and you guys can follow along as well as long as you haven't done the trial before him and let's create an account. I'm going to call this chrisskillshare and then let's use a, I'll just use the suggested password and copy that down somewhere so I don't lose that. It's going to ask for your card details because after the 14-day trial it's going to automatically charge your card, if you don't want this to happen, obviously, make sure you cancel before the 14 days. I find a lot of people don't like this, but if you just set up a reminder on your phone or computer to make sure you cancel before the 14 days ends, then you'll never get charged and I've never been charged for anything that I didn't want to get charged for, simply because I always remember to put in a reminder. For those of you who have particularly worried about getting your card charged, make sure you put that reminder in. I've got my card details saved here. Just going to put that in, details confirmed, and then I'm going to hit continue. Here you can choose what kind of personality you are. I'm going to hit Developer because that's what I am. Now hit continue and then now we can give a name to our site. I'm just going to call it Ghost Site. Then I'll click. Let's do this. Something's wrong with Stripe, it seems. Let's try again. It looks like it worked the second time. It's now spinning up right now, completing our install and finalizing our configuration. Then just like we had on our local computer, we've got a fresh install. If I go into posts here, you can see we've got all the same default posts that we had before, the same default pages, and of course, the Ghost staff member and our user up here which we set when we first logged in. This is the great thing about Ghost Pro. To get to this point using DigitalOcean or Linode takes a few extra steps. Whereas using Ghost Pro allows us to get a Ghost website up and running super fast. As you can see here, we can access our site, It's pretty cool that ghost-site was actually available as a website name. But by the time you're watching this class, this site should probably be down anyway. We've got the same starting point as we had before when we installed locally. Now what we're going to do is we're going to package up our theme and upload it to this live Ghost site. Remember we go into settings here to look at themes and inside theme here we have Casper. Now one of the things I didn't do when we got into the Casper theme originally was I didn't change the details in the package.json file, so I'm going to switch over to the code editor now. Let's close down everything except the package.json file. If I scroll up, you can see that our theme is still called Casper. I'm going to start to rename this, let's say custom-ghost-theme, and let's change some of these other details as well. Custom Ghosts theme by Chris Dodd. Don't need to put in a demo there. Let's just say it's Version 1.0. Get rid of these screenshots. Instead of author, we're going to put our actual name, email, get rid of the URL there. We can probably leave all the rest of these ones here. Repository, let's just get rid of that. These links are specifically for the Casper theme, so I'm going to get rid of those as well. Just removing everything that is specific to Casper and especially changing something like the name. Now what we're going to do is I'm going to open up a Finder Window. One second while I rearrange my Window here and then we're going to go to our Ghost site here. Once we're inside our Ghost site folder, we can click on current content themes to get to our themes folder. Here you can see our Casper theme folder here, but of course, we have heavily modified the code inside our Casper folder here, so what I'm going to do is, first of all, I'm going to compress it, and then I'm going to rename the theme, custom theme. Now we've got our theme packaged up into a zip folder. We can now upload it to our live Ghost site. I'm going to head over to our Ghost site, the production version. Make sure you're on the production version, not the local version, as we don't really need to upload a theme to our local version. We already have it right here on our computer. What I'm going to do is click this green button to upload a theme. Grab this zip version of the theme and then it's going to ask me if I want to activate it and I'm going to click yes to activate. Now I can click this button over here and now we can see our Ghost site now loading in our theme. One thing to note here though is that the navigation is different, as you can see up here, and so what I'll do is replicate our navigation here on our live site to what we had built on our local site. Click into Settings here, go into navigation, and let's remove these links. I believe we had home, about, and blog. Blog here, hit save. Now if we go back to our site, you can see, let's refresh one more time. Now you can see that we have the same or similar navigation to what we had on our local install. But there is one more thing we need to change here and that is upload our updated routes file. If we go into Settings here and then we scroll down to labs, not sure why it's in labs, because it is a pretty important thing that we would need to do. But here under Beta features, we're going to go down to routes and we're going to upload our own routes.yaml file. Here you can see inside content settings is our routes file, we'll upload that. That's now uploaded. So what that means is if we head to the homepage of our website, we will see the homepage. But now our index will be moved off to the blog route, just like we had locally and now our site should be exactly the same as what we had locally. To refresh what we've done in this video, we've gotten our package.json file ready right here. Updated all the details so it's not specific to Casper anymore. We created a zip of the theme folder. We renamed that folder to our custom theme name, in this case, custom theme and then we uploaded our theme to the Ghost admin. If I go in here, go into themes, we can see we've got our customer Ghost theme there. Then we updated our navigation, of course. But then finally, really importantly, we went into labs and updated our routes file as well. Remember that routes is highly related to the theme, but it actually isn't in the theme folder, so that needs to be uploaded separately. That's what is required if you're running a Ghost Pro, I do recommend Ghost Pro if you can afford it for the simple reason that it's so easy to install, I mean, it's created by Ghost. So they make it super easy to get up and running with Ghost site. The same process on another host, like we saw before, like DigitalOcean or Linode takes a bit longer and so I'm not going to go through that in this class. What I use personally is DigitalOcean and allows me to pay as little as $5 a month to run my website rather than the minimum which I think we saw was, if I go to the Ghost Pro pricing page here, 25, so I pay five times less for setting it up on DigitalOcean. But the difference is I had to spend like an afternoon or morning going through trying to figure out how to actually set it up on DigitalOcean. It's a little bit more work to set it up on DigitalOcean or Linode. If you can afford it and you want to do it, by all means, use Ghost Pro. But for me, I was willing to go through the trouble of figuring out how to get it to work on DigitalOcean in order to pay less over the long term. That's your options there. I'm going to leave it there for this lesson and in the next lesson, we'll talk about the GitHub integration. In the next lesson, what we'll talk about is creating a GitHub workflow where we can just create edits to our local theme, push them to GitHub, and then it'll automatically update our Ghost site without us having to go in here and manually upload a zip file each time. If you want to learn how to do that, I will catch you in the next bonus video. 14. Bonus: Setting up the Github Integration: As promised in this bonus lesson, we're going to look at how we can use the GitHub integration to publish changes to our Ghost themes via a single Git, commit, and push. Git is something I recommend everyone to use. A lot of you guys, I'm sure are already using it, but it's a good way to track changes to your project. GitHub is the premier website that allows you to host remote repositories. In order to do this, you will need a GitHub account. I'm not going to go through how you set one up, but you will need a GitHub account. If you want to pause the video right now and go do that, I'll wait for you on the other side. Otherwise, if you do have a GitHub account, let's get started setting up this GitHub integration. We just need to follow this guide here on the website. It's under integrations/GitHub, and let's go through and follow this document. The first step is going to be to create the custom integration in our live Ghost site. How this works is we're going to edit the code on our local computer, we're going to make a Git commit and push that to GitHub. Then once it reaches GitHub, is going to then affect our live site. In order for it to affect our live site, we're going to have to set up a custom integration so that we have those permissions in order to affect our live site. As it says here in the Ghost Admin, we're going to navigate to integrations and create a new custom integration called GitHub Actions. I'll try and run these side-by-side. Might be a little bit tight, but it would be good to have these side-by-side. It looks like it's going to be very tight. Here we go. We're going to go into more here, click ''Integrations'', and then we want to scroll down to Add custom integration. We're going to call the integration, as it says in the document, Github Actions. Create that one. Scroll up, and then next we're going to copy and paste the integration details into our GitHub repositories environment variables. We actually need to create a GitHub repository for this to work. What we're going to do is I'm going to expand this window again, go into my GitHub account, login, and I've got two-factor authentication, so I'm going to need to authorize with my Authenticator app. Now we're going to create a new repository by using this plus symbol up here, and let's call this ghost-custom-theme, and then Custom theme created in my Skillshare class Ghost Theme Development. I'll leave this for public for you guys so you can actually look at the code. I know some of you guys request that every now and then, so I will publish this code to this repository. If you want to go to this address, this is going to be a public address after this, so you can go and do that. We're going to go and I'm not going to follow these instructions word for word, I don't like to create a main branch, I like to use the master branch. I went through this first part here, but what I will do is I'll go into, let's open up our terminal again, maybe create a new tab here, and then navigate to our folder where we have the theme code. Cd code/skillshare/ghost-site, and then from within here, I believe its contents, and then themes, and then inside themes, let's just have a look, casper. Let's cd into casper here. Then we're going to initialize our Git repository. But before that, I'm just going to make this a little bigger, and then run git init to create a new Git repository, git add everything to it, and then make our initial commit. Now we can add that address right here, whatever it is for you, add that, and then we can do git push -u origin master. There we go. That should be now pushed to our GitHub repository, and if we refresh, we should now have that theme up here. It's copy the same read from Casper, so I might do some clean up after this. But other than that, we have our Ghost custom theme up. We're going to do that next step that it said in the documentation, so dragging this across, it said to then go into our Settings and then go into Secrets. We're a bit tired here, but it's inside this menu. I go into Settings, look for secrets, and then if I scroll down, I can create a new repository secret, and let's follow exactly what it says here. I'm going to create one secret called GHOST_ADMIN_API_URL, and I'm going to copy across the details from our integration. I'm going to take the API URL, click ''Add secret'', and then we're going to add one more for the ADMIN_API_KEY, so copying this exactly, and then taking our ADMIN_API_KEY key here and putting it in that value field. That should be enough to give GitHub enough permissions to actually affect our live Ghost site. Now let's get to the next step, which is to install the Ghost theme deploy action. We're going to go into our theme here. Make sure we're in the theme folder, and we're going to create that new file, which was.github/workflows/main.yml. Let's see if we can do this from within VS code. There we go. It has made the path for us and everything, so then we can just copy all of this, paste it into our file here. That's all that we need to do there except we don't have a main branch, so I'm going to get rid of that, and that should be it. What I'm going to do is open up our terminal again, and I'm going to add the change, and then commit that, add in code for GitHub Actions, and then make sure to git push. That'll upload that specific file to our Git repository. I'll make this bigger again. Head back into our code here, and now you can see we've got a.github folder with our workflows in it, and we have this code for the GitHub Action. That should be enough for us to now push changes here and that to integrate on our Ghost site. I'm going to head over here to Themes, and as you can see here, we have already pushed that theme to our admin. As you can see here in the brackets, it's a little bit confusing because there's a few of the same name. But now we have this custom-ghost-theme. Let's make a pretty obvious change now and see how it affects our live side when we use the GitHub integration. Let's just say instead of this header styling, let's change that to, let's say we're going to do a background color of white, just invert the colors basically, so we can do. That's not going to work for the navigation, we're going to need to make sure the nav is that color as well, and so that should sort it. Let's replicate that in our CSS file. It was header and header A. This is just for demonstration purposes, you can do whatever change you like as you go about your project. This is just to demonstrate that the change will come across when we publish to our GitHub repository. I'm going to make this white switches, fff. Now that we've made a change, you can see here, we've made a change there. We're going to go into our terminal again, Git add to stage all those changes, which is just one, and then commit updates header. Then we're going to git push, and our GitHub Action should then work. We can actually look at the status of our GitHub Action. I close this head over to here, refresh, click on ''Actions'', and you can see here we have a workflow running right now. Says it started 15 seconds ago and now it's done, so it should have a green tick next to it, and then if we go into our Ghost site and refresh, I'm pretty sure it's this second one here. Now you can see that we have our updated header Style now live on our site. That's how we can use GitHub to publish changes to our website. Once we have this active, any changes we push to this theme will be live on our website. It might be a good idea to have a few branches, one for staging and one for live. Otherwise, if I keep this theme activated and I make changes locally that I then pushed to GitHub, then this will be changed in our Ghost website as well, and that allows us to continually make changes over time. That's it for this finace bonus lesson. In the next video, we're going to wrap up the class with our conclusion, and I'm going to tell you about your class project. I'll see you in the next video. 15. Conclusion & Class Project: This concludes this class on how to customize your ghost publication. For your class project, I encourage you to create your own ghost theme using the knowledge you gained in this class. If you're stuck at any point, you can refer to the official documentation or my ultimate guide to ghost development posted on my website. Of course, if that still doesn't help, you can leave a comment in the discussion box below, and I'll do my best to point you in the right direction. As always, thanks for watching and I hope to see you again on some of my other classes.