Ghost:添加目录栏
对于复杂的技术博文没有侧边目录栏基本看不下去,然而ghost并不自带目录栏功能,所以我们只能通过改主题文件来实现。
不过好在ghost的主题文件并不复杂,这里完全可以依照官方教程把代码粘贴进去就能用了,强烈建议先看官方教程。这篇博客主要讲解一些细节
官方教程:https://ghost.org/tutorials/adding-table-of-contents
我的博客主题修改自source,仓库:https://github.com/dedfaf/DF-Blog-Theme
教程讲解
官方教程使用了ToCBot来实现目录栏
可以用ghost install local
安装本地测试环境,在其上修改后再push到生产环境
一些简单的样式调整
这里讲解一些官方教程操作完后的一些细节调整,也可以直接参考文末我的代码
默认展开所有目录
默认情况下,之后浏览到的目录只会展开当前标题的子标题,需要在脚本中加入
<script>
tocbot.init({
// ...
collapseDepth: 6,
// ...
});
</script>
去除序号前缀
脚本中加入
<script>
tocbot.init({
// ...
orderedList: false, // Ensures the list is not ordered
// ...
});
</script>
去除链接下划线
CSS:
.toc-list .toc-list-item a {
text-decoration: none; /* remove underline */
}
调整目录的位置
默认的目录位置是紧贴正文的,可以修改css中目录栏的上级.gh-sidebar
的偏移量来调整目录的位置。我的目录原本在左侧紧贴正文,我将其向左偏移了px,仅供参考
@media (min-width: 1300px) {
.gh-sidebar {
position: absolute;
top: 0;
bottom: 0;
margin-top: 4vmin;
grid-column: wide-start / main-start; /* Place the TOC to the left of the content */
margin-left: -192px; /* add offset */
}
.gh-toc {
position: sticky; /* On larger screens, TOC will stay in the same spot on the page */
top: 4vmin;
}
}
个人最终生成的代码
请重点关注两个{{!-- ToC --}}注释后的部分,其余的部分和casper的代码是一样的
<!-- default.hbs -->
<!DOCTYPE html>
<html lang="{{@site.locale}}">
<head>
{{!-- Basic meta - advanced meta is output with {{ghost_head}} below --}}
<title>{{meta_title}}</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{{!-- Preload main styles and scripts for better performance --}}
<link rel="preload" as="style" href="{{asset "built/screen.css"}}">
<link rel="preload" as="script" href="{{asset "built/source.js"}}">
{{!-- Fonts are preloaded and defined in the default template to avoid layout shift --}}
{{> "typography/fonts"}}
{{!-- Theme assets - use the {{asset}} helper to reference styles & scripts, this will take care of caching and cache-busting automatically --}}
<link rel="stylesheet" type="text/css" href="{{asset "built/screen.css"}}">
{{!-- Custom background color --}}
<style>
:root {
--background-color: {{@custom.site_background_color}}
}
</style>
<script>
/* The script for calculating the color contrast has been taken from
https://gomakethings.com/dynamically-changing-the-text-color-based-on-background-color-contrast-with-vanilla-js/ */
var accentColor = getComputedStyle(document.documentElement).getPropertyValue('--background-color');
accentColor = accentColor.trim().slice(1);
if (accentColor.length === 3) {
accentColor = accentColor[0] + accentColor[0] + accentColor[1] + accentColor[1] + accentColor[2] + accentColor[2];
}
var r = parseInt(accentColor.substr(0, 2), 16);
var g = parseInt(accentColor.substr(2, 2), 16);
var b = parseInt(accentColor.substr(4, 2), 16);
var yiq = ((r * 299) + (g * 587) + (b * 114)) / 1000;
var textColor = (yiq >= 128) ? 'dark' : 'light';
document.documentElement.className = `has-${textColor}-text`;
</script>
{{!-- TOC styles --}}
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.css">
<style>
.gh-content {
position: relative;
}
.gh-toc > .toc-list {
position: relative;
}
.toc-list {
overflow: hidden;
list-style: none;
}
@media (min-width: 1300px) {
.gh-sidebar {
position: absolute;
top: 0;
bottom: 0;
margin-top: 4vmin;
grid-column: wide-start / main-start; /* Place the TOC to the left of the content */
margin-left: -192px; /* add offset */
}
.gh-toc {
position: sticky; /* On larger screens, TOC will stay in the same spot on the page */
top: 4vmin;
}
}
.toc-list .toc-list-item a {
text-decoration: none; /* remove underline */
}
.gh-toc .is-active-link::before {
background-color: var(--ghost-accent-color); /* Defines TOC accent color based on Accent color set in Ghost Admin */
}
</style>
{{!-- This tag outputs all your advanced SEO meta, structured data, and other important settings, it should always be the last tag before the closing head tag --}}
{{ghost_head}}
</head>
<body class="{{body_class}} has-{{#match @custom.title_font "Elegant serif"}}serif{{else match @custom.title_font "Consistent mono"}}mono{{else}}sans{{/match}}-title has-{{#match @custom.body_font "Elegant serif"}}serif{{else}}sans{{/match}}-body">
<div class="gh-viewport">
{{> "components/navigation" navigationLayout=@custom.navigation_layout}}
{{{body}}}
{{> "components/footer"}}
</div>
{{#is "post, page"}}
{{> "lightbox"}}
{{/is}}
{{!-- Scripts - handle responsive videos, infinite scroll, and navigation dropdowns --}}
<script src="{{asset "built/source.js"}}"></script>
{{!-- Tocbot script --}}
<script src="https://cdnjs.cloudflare.com/ajax/libs/tocbot/4.12.3/tocbot.min.js"></script>
{{! Initialize Tocbot after you load the script }}
<script>
tocbot.init({
// Where to render the table of contents.
tocSelector: '.gh-toc',
// Where to grab the headings to build the table of contents.
contentSelector: '.gh-content',
// Which headings to grab inside of the contentSelector element.
headingSelector: 'h1, h2, h3, h4',
// Ensure correct positioning
hasInnerContainers: true,
collapseDepth: 6,
orderedList: false,
});
</script>
{{!-- Ghost outputs required functional scripts with this tag, it should always be the last thing before the closing body tag --}}
{{ghost_foot}}
</body>
</html>
<!-- post.hbs -->
{{!< default}}
{{!-- The tag above means: insert everything in this file into the body of the default.hbs template --}}
{{#post}}
<main class="gh-main">
<article class="gh-article {{post_class}}">
<header class="gh-article-header gh-canvas">
{{#if primary_tag}}
<a class="gh-article-tag" href="{{primary_tag.url}}">{{primary_tag.name}}</a>
{{/if}}
<h1 class="gh-article-title is-title">{{title}}</h1>
{{#if custom_excerpt}}
<p class="gh-article-excerpt is-body">{{custom_excerpt}}</p>
{{/if}}
{{#if @custom.show_post_metadata}}
<div class="gh-article-meta">
<div class="gh-article-author-image instapaper_ignore">
{{#foreach authors}}
{{#if profile_image}}
<a href="{{url}}">
<img class="author-profile-image" src="{{img_url profile_image size="xs"}}" alt="{{name}}">
</a>
{{else}}
<a href="{{url}}">{{> "icons/avatar"}}</a>
{{/if}}
{{/foreach}}
</div>
<div class="gh-article-meta-wrapper">
<h4 class="gh-article-author-name">{{authors}}</h4>
<div class="gh-article-meta-content">
<time class="gh-article-meta-date" datetime="{{date format="YYYY-MM-DD"}}">{{date format="DD MMM YYYY"}}</time>
{{#if reading_time}}
<span class="gh-article-meta-length"><span class="bull">—</span> {{reading_time}}</span>
{{/if}}
</div>
</div>
</div>
{{/if}}
{{> "feature-image"}}
</header>
<section class="gh-content gh-canvas is-body{{#if @custom.enable_drop_caps_on_posts}} drop-cap{{/if}}">
<aside class="gh-sidebar"><div class="gh-toc"></div></aside> {{! The TOC will be inserted here }}
{{content}}
</section>
</article>
{{#if comments}}
<div class="gh-comments gh-canvas">
{{comments}}
</div>
{{/if}}
</main>
{{/post}}
{{#if @custom.show_related_articles}}
{{#get "posts" include="authors" filter="id:-{{post.id}}" limit="4" as |next|}}
{{#if next}}
<section class="gh-container is-grid gh-outer">
<div class="gh-container-inner gh-inner">
<h2 class="gh-container-title">Read more</h2>
<div class="gh-feed">
{{#foreach next}}
{{> "post-card" lazyLoad=true}}
{{/foreach}}
</div>
</div>
</section>
{{/if}}
{{/get}}
{{/if}}