主题安装
有两种方法来安装Themes。第一种是在这个管理界面中选最下面的『Upload A Theme』来上传你下载过的Theme .zip包,第二种是直接将.zip包解压到Ghost安装目录的content/themes/
目录下。
Custom Theme
这是官方的模板项目。
https://github.com/TryGhost/Starter
几个需要注意的地方。
主要文件:
default.hbs
- The main template fileindex.hbs
- Used for the home pagepost.hbs
- Used for individual postspage.hbs
- Used for individual pagestag.hbs
- Used for tag archivesauthor.hbs
- Used for author archives
有一个整洁的技巧是,你也可以创建自定义的一次性模板,只需在模板文件中添加页面的slug即可。比如说,你可以在模板文件中添加一个页面的Slug。
page-about.hbs
- Custom template for the/about/
pagetag-news.hbs
- Custom template for/tag/news/
archiveauthor-ali.hbs
- Custom template for/author/ali/
archive
Styles使用Gulp/PostCSS进行处理. 所以你需要安装Node](https://nodejs.org/), Yarn 和 Gulp 。
# Install
yarn
# Run build & watch for changes
$ yarn dev
现在你可以直接编辑/assets/css/文件,这样修改就会被自动编译到 /assets/built/
目录下。
The zip
Gulp task packages the theme files into dist/<theme-name>.zip
, which you can then upload to your site.
yarn zip
官方使用gulp来优化JavaScript和CSS,所以,需要先安装gulp相关的依赖,官方的介绍文档如下。
https://ghost.org/tutorials/how-to-use-gulp-in-a-ghost-theme/
Gulp是一个精简js、css的工具,官方地址如下。
JS的生态真是太恶劣了,版本管理是我见过的最复杂,最难解决冲突的语言了。官方的Casper Demo clone下来之后,要处理很多东西。
npm install // 安装所有npm依赖
npm install @tryghost/release-utils --save // 安装release-utils
所有npm安装好了之后,执行yarn dev,就可以在修改css文件后,自动生成built目录下的优化css了。
Handlebars中文指南
https://handlebarsjs.com/zh/guide
Ghost主题调试
通常情况下,会创建一个本地调试环境,测试开发主题完成之后,再打包上传到正式环境。
npm install ghost-cli@latest -g
ghost install local
这样就在本地搭建了测试环境。
自己做的主题,直接放到ghost-local\content\themes目录下即可,这时候,直接修改hbs文件,在浏览器中刷新,就可以实时预览效果了。
不过这里有个问题,那就是CSS是没办法hotreload的,必须要执行ghost restart才可以生效。大部分时间,都是现在Chrome Devtools里面修改DOM,然后再去修改CSS。
调试完成后,通过打包成zip,就可以在后台上传主题包了。
使用SVG Icon
Casper使用内嵌式SVG图标,通过Handlebars partials引用。你可以在/partials/icons中找到所有图标。
要使用一个图标,只需包含相关文件的名称,例如,要在 /partials/icons/rss.hbs中包含SVG图标,可以使用。
{{> "icons/rss"}}
快速修改文章
Ghost后台没有提供搜索文章的入口,所以要修改文章时,就只能通过一些条件来进行筛选,但是通过下面的方法,可以直接修改文章。
Post: http://www.your-domain.com/post-name
Edit: http://www.your-domain.com/post-name/edit
添加代码高亮
highlight.js可以自动检测语言,且在使用时比较简单,所以这里使用highlight.js来创建Ghost博客中的代码高亮问题。官网地址如下所示。
不过Ghost官方是通过Prism https://ghost.org/tutorials/code-syntax-highlighting/ 来实现的。我试下来Prism必须要在MD中指定语言类型才能实现高亮。
使用 highlight.js
很简单。只需要添加三个组件。
- 代码的样式文件
- 分析代码确定样式的JS脚本文件
- 用来调用脚本文件的脚本
在default.hbs中,修改header。这里使用的是官方推荐的CDN,你也可以使用BootCDN的链接。还可以在highlight.js Demo直接预览效果。
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/styles/androidstudio.min.css">
在script中,增加JS代码。
<script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/10.2.0/highlight.min.js"></script>
可以引入不同的css文件,展示不同的代码风格。
在script中,增加调用脚本。
<script>hljs.initHighlightingOnLoad();</script>
添加目录
每篇文章,都应该有一个目录用来展示文章的大纲,点击可以跳转对应的章节,原始的Casper主题是没有这个功能的,所以我们需要为其拓展这个功能,官方给出的指引中,实际上也包含了这个例子,地址如下所示。
https://ghost.org/tutorials/adding-a-table-of-contents/
首先,要引入Tocbot的库,用于生成Markdown的目录。
CSS文件如下,添加到default.hbs的header中。
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.0/tocbot.css" />
JS文件如下。
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.0/tocbot.min.js"></script>
在default.hbs的body完结标签之前,加入调用的JS代码。
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.0/tocbot.min.js"></script>
<script>
tocbot.init({
tocSelector: '.toc',
contentSelector: '.post-content',
hasInnerContainers: true
});
</script>
由于目录会添加在每篇文章里面,所以还需要修改post.hbs文件,在post-content之前,增加aside代码,添加toc,代码如下所示。
<section class="post-full-content">
<aside class="toc-container">
<div class="toc"></div>
</aside>
<div class="post-content">
{{content}}
</div>
</section>
最后,修改CSS文件,添加TOC相关的样式,比如右侧悬浮、高亮等等,代码如下所示。
/* 13. TCO
/* ---------------------------------------------------------- */
/* Offset headings from fixed header */
.post-content h2::before,
.post-content h3::before {
display: block;
content: " ";
height: 84px;
margin-top: -84px;
visibility: hidden;
}
/* Adjust content wrapper */
.post-content {
display: block;
}
/* Adjustments to wide and full width cards */
.kg-gallery-card,
.kg-width-wide,
.kg-width-full {
display: flex;
flex-direction: column;
align-items: center;
}
.kg-gallery-card > *,
.kg-width-wide > *,
.kg-width-full > *,
figure.kg-width-full img {
margin-left: -50vw;
margin-right: -50vw;
}
.post-full-content pre {
max-width: 0;
}
.post-content h2,
.post-content h3 {
outline: none;
}
body {
overflow: visible;
}
.post-full-content {
display: grid;
grid-template-columns: 1fr 0.2fr;
padding: 0 0 6vw;
margin: 0;
}
.toc-container {
order: 1;
}
.toc-container .toc {
position: sticky;
top: 70px;
min-width: 260px;
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
Ubuntu, Cantarell, Open Sans, Helvetica Neue, sans-serif;
font-size: 1.6rem;
line-height: 1.6em;
padding: 0 0.8em;
}
.is-active-link::before {
background-color: #3eb0ef;
}
ol.toc-list {
margin: 0;
}
添加阅读进度
在post.hbs最顶部,添加一个自定义的progress。官方文档。
<progress data-reading-progress max="4"></progress>
添加JS代码。
const readingProgress = (contentArea, progressBar) => {
// Grab content area and progress bar
const content = document.querySelector(contentArea);
const progress = document.querySelector(progressBar);
// Minutes remaining label template
const label = value => `${value} minute${value !== 1 ? "s" : ""} remaining`;
// Set the progress bar label to maximum time remaining if data attribute is present
if (progress.hasAttribute('data-reading-progress')) {
console.log('test');
progress.dataset.readingProgress = label(progress.max);
}
const frameListening = () => {
// Get the content area position,
// the vertical centre of the browser window,
// the minutes remaining without decimal places
const contentBox = content.getBoundingClientRect();
const midPoint = window.innerHeight / 2;
const minsRemaining = Math.round(progress.max - progress.value);
// Update the label if data attribute is present
if (progress.hasAttribute('data-reading-progress')) {
progress.dataset.readingProgress = label(minsRemaining);
}
// Default the progress bar to 0 before content is in view
if (contentBox.top > midPoint) {
progress.value = 0;
}
// Default the progress bar to maximum when the content is past view
if (contentBox.top < midPoint) {
progress.value = progress.max;
}
// Start updating the progress value when content as reached the vertical centre
// Stop updating when the content is past the vertical centre
if (contentBox.top <= midPoint && contentBox.bottom >= midPoint) {
// Calculate the progress bar value
progress.value =
(progress.max * Math.abs(contentBox.top - midPoint)) /
contentBox.height;
}
// Continue to request animation frames
window.requestAnimationFrame(frameListening);
};
// Begin requesting animation frames
window.requestAnimationFrame(frameListening);
};
// Init, main content selector and progress bar selector
readingProgress(".post-full-content", "progress");
添加CSS样式。
/* 14. Reading Progress
/* ---------------------------------------------------------- */
progress {
-webkit-appearance: none;
-moz-appearance: none;
appearance: none;
z-index: 1000;
position: fixed;
top: 64px;
left: 0;
right: 0;
width: 100%;
height: 2px;
}
progress[value]::-webkit-progress-bar {
background-color: rgba(0, 0, 0, 0.5);
}
progress[value]::-webkit-progress-value {
background-color: #3eb0ef;
}
progress::before {
content: attr(data-reading-progress);
position: absolute;
background: none;
font-size: .6em;
margin: .6em .8em;
color: white;
right: 0;
background: rgba(0, 0, 0, 0.7);
padding: .3em .6em;
border-radius: .8em;
}
.content {
max-width: 600px;
margin: 0 auto;
}
这里总结了Ghost主题开发的一些知识点,在了解这些基本知识和开发方式之后,再去制作主题或者修改主题,就比较方便了。