安装
git clone https://github.com/CaiJimmy/hugo-theme-stack/ themes/hugo-theme-stack
官方文档: Modify theme | Stack
美化
下文custom.scss
指的是~hugo/themes/hugo-theme-stack/assets/scss/custom.scss
主题整体细节调整
在custom.scss
中写入以下内容:
//----------------------------------------------------// 页面基本配色:root { // 全局顶部边距 --main-top-padding: 30px; // 全局卡片圆角 --card-border-radius: 25px; // 标签云卡片圆角 --tag-border-radius: 8px; // 卡片间距 --section-separation: 40px; // 全局字体大小 --article-font-size: 1.8rem; // 行内代码背景色 --code-background-color: #f8f8f8; // 行内代码前景色 --code-text-color: #e96900; // 暗色模式下样式 &[data-scheme="dark"] { // 行内代码背景色 --code-background-color: #ff6d1b17; // 行内代码前景色 --code-text-color: #e96900; // 暗黑模式下背景色 --body-background: #000; // 暗黑模式下卡片背景色 --card-background: hsl(225 13% 8% / 1); }}//------------------------------------------------------// 修复引用块内容窄页面显示问题a { word-break: break-all;}
code { word-break: break-all;}
//---------------------------------------------------// 文章封面高度.article-list article .article-image img { width: 100%; height: 200px !important; object-fit: cover;
@include respond(md) { height: 250px !important; }
@include respond(xl) { height: 285px !important; }}
//--------------------------------------------------// 文章内容图片圆角阴影.article-page .main-article .article-content { img { max-width: 96% !important; height: auto !important; border-radius: 8px; }}
//------------------------------------------------// 文章内容引用块样式.article-content { blockquote { border-left: 6px solid #358b9a1f !important; background: #3a97431f; }}// ---------------------------------------// 代码块样式修改.highlight { max-width: 102% !important; background-color: var(--pre-background-color); padding: var(--card-padding); position: relative; border-radius: 20px; margin-left: -7px !important; margin-right: -12px; box-shadow: var(--shadow-l1) !important;
&:hover { .copyCodeButton { opacity: 1; } }
// keep Codeblocks LTR [dir="rtl"] & { direction: ltr; }
pre { margin: initial; padding: 0; margin: 0; width: auto; }}
// light模式下的代码块样式调整[data-scheme="light"] .article-content .highlight { background-color: #fff9f3;}
[data-scheme="light"] .chroma { color: #ff6f00; background-color: #fff9f3cc;}
//-------------------------------------------// 设置选中字体的区域背景颜色//修改选中颜色::selection { color: #fff; background: #34495e;}
a { text-decoration: none; color: var(--accent-color);
&:hover { color: var(--accent-color-darker); }
&.link { color: #4288b9ad; font-weight: 600; padding: 0 2px; text-decoration: none; cursor: pointer;
&:hover { text-decoration: underline; } }}
//-------------------------------------------------//文章封面高度更改.article-list article .article-image img { width: 100%; height: 150px; object-fit: cover;
@include respond(md) { height: 200px; }
@include respond(xl) { height: 305px; }}
//---------------------------------------------------// 全局页面布局间距调整.main-container { min-height: 100vh; align-items: flex-start; padding: 0 15px; gap: var(--section-separation); padding-top: var(--main-top-padding);
@include respond(md) { padding: 0 37px; }}
//--------------------------------------------------//页面三栏宽度调整.container { margin-left: auto; margin-right: auto;
.left-sidebar { order: -3; max-width: var(--left-sidebar-max-width); }
.right-sidebar { order: -1; max-width: var(--right-sidebar-max-width);
/// Display right sidebar when min-width: lg @include respond(lg) { display: flex; } }
&.extended { @include respond(md) { max-width: 1024px; --left-sidebar-max-width: 25%; --right-sidebar-max-width: 22% !important; }
@include respond(lg) { max-width: 1280px; --left-sidebar-max-width: 20%; --right-sidebar-max-width: 30%; }
@include respond(xl) { max-width: 1453px; //1536px; --left-sidebar-max-width: 15%; --right-sidebar-max-width: 25%; } }
&.compact { @include respond(md) { --left-sidebar-max-width: 25%; max-width: 768px; }
@include respond(lg) { max-width: 1024px; --left-sidebar-max-width: 20%; }
@include respond(xl) { max-width: 1280px; } }}
//-------------------------------------------------------//全局页面小图片样式微调.article-list--compact article .article-image img { width: var(--image-size); height: var(--image-size); object-fit: cover; border-radius: 17%;}
//----------------------------------------------------//固定代码块的高度.article-content { .highlight { padding: var(--card-padding); pre { width: auto; max-height: 20em; } }}
//--------------------------------------------------// 修改首页搜索框样式.search-form.widget input { font-size: 1.5rem; padding: 44px 25px 19px;}
菜单栏调整为圆角
在custom.scss
中写入以下内容:
//----------------------------------------------------// 下拉菜单改圆角样式.menu { padding-left: 0; list-style: none; flex-direction: column; overflow-x: hidden; overflow-y: scroll; flex-grow: 1; font-size: 1.6rem; background-color: var(--card-background);
box-shadow: var(--shadow-l2); //改个阴影 display: none; margin: 0; //改为0 border-radius: 10px; //加个圆角 padding: 30px 30px;
@include respond(xl) { padding: 15px 0; }
&, .menu-bottom-section { gap: 30px;
@include respond(xl) { gap: 25px; } }
&.show { display: flex; }
@include respond(md) { align-items: flex-end; display: flex; background-color: transparent; padding: 0; box-shadow: none; margin: 0; }
li { position: relative; vertical-align: middle; padding: 0;
@include respond(md) { width: 100%; }
svg { stroke-width: 1.33;
width: 20px; height: 20px; }
a { height: 100%; display: inline-flex; align-items: center; color: var(--body-text-color); gap: var(--menu-icon-separation); }
span { flex: 1; }
&.current { a { color: var(--accent-color); font-weight: bold; } } }}
滚动条美化
在custom.scss
中写入以下内容:
//------------------------------------------------//将滚动条修改为圆角样式//菜单滚动条美化.menu::-webkit-scrollbar { display: none;}
// 全局滚动条美化html { ::-webkit-scrollbar { width: 20px; }
::-webkit-scrollbar-track { background-color: transparent; }
::-webkit-scrollbar-thumb { background-color: #d6dee1; border-radius: 20px; border: 6px solid transparent; background-clip: content-box; }
::-webkit-scrollbar-thumb:hover { background-color: #a8bbbf; }}
归档页实现双栏
在custom.scss
中写入以下内容:
//--------------------------------------------------//归档页面双栏/* 归档页面两栏 */@media (min-width: 1024px) { .article-list--compact { display: grid; grid-template-columns: 1fr 1fr; background: none; box-shadow: none; gap: 1rem;
article { background: var(--card-background); border: none; box-shadow: var(--shadow-l2); margin-bottom: 8px; border-radius: 16px; } }}
文章页面左上角引入返回按钮
在custom.scss
中,给返回按钮添加以下样式,不然返回按钮会显示异常:
//--------------------------------------------------//引入左上角返回按钮.back-home { background: var(--card-background); border-radius: var(--tag-border-radius); color: var(--card-text-color-tertiary); margin-right: 0.1rem; margin-top: 24px; display: inline-flex; align-items: center; font-size: 1.4rem; text-transform: uppercase; padding: 10px 20px 10px 15px; transition: box-shadow 0.3s ease; box-shadow: var(--shadow-l3);
&:hover { box-shadow: var(--shadow-l2); }
svg { margin-right: 5px; width: 20px; height: 20px; }
span { font-weight: 500; white-space: nowrap; }}
.main-container .right-sidebar { order: 2; max-width: var(--right-sidebar-max-width);
/// Display right sidebar when min-width: lg @include respond(lg) { display: flex; }}
main.main { order: 1; min-width: 0; max-width: 100%; flex-grow: 1; display: flex; flex-direction: column; gap: var(--section-separation);
@include respond(md) { padding-top: var(--main-top-padding); }}
修改~\hugo\themes\hugo-theme-stack\layouts\_default\single.html
写入添加以下内容:
注意对照原主题,不要把重复的部分也写进去
.......已省略,请自己对照...... {{ partialCached "footer/footer" . }}
{{ partialCached "article/components/photoswipe" . }}{{ end }}
{{ define "left-sidebar" }}
{{ if (.Scratch.Get "TOCEnabled") }} <div id="article-toolbar" style="position: sticky;top: 5px;z-index: 1000;"> <a href="{{ .Site.BaseURL | relLangURL }}" class="back-home"> {{ (resources.Get "icons/back.svg").Content | safeHTML }} <span>{{ T "article.back" }}</span> </a> </div> {{ else }} {{ partial "sidebar/left.html" . }} {{ end }}{{ end }}
{{ define "right-sidebar" }} {{ if .Scratch.Get "hasWidget" }}{{ partial "sidebar/right.html" (dict "Context" . "Scope" "page") }}{{ end}}{{ end }}
代码块引入 MacOS 窗口样式
在主题目录下的assets
文件夹中的img
文件夹中,创建一个名为code-header.svg
的文件,在文件中写入以下内容:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" x="0px" y="0px" width="450px" height="130px"> <ellipse cx="65" cy="65" rx="50" ry="52" stroke="rgb(220,60,54)" stroke-width="2" fill="rgb(237,108,96)"/> <ellipse cx="225" cy="65" rx="50" ry="52" stroke="rgb(218,151,33)" stroke-width="2" fill="rgb(247,193,81)"/> <ellipse cx="385" cy="65" rx="50" ry="52" stroke="rgb(27,161,37)" stroke-width="2" fill="rgb(100,200,86)"/></svg>
在custom.scss
文件中添加以下内容:
//----------------------------------------------------------//为代码块顶部添加macos样式.article-content { .highlight:before { content: ""; display: block; background: url(../img/code-header.svg); height: 32px; width: 100%; background-size: 57px; background-repeat: no-repeat; margin-bottom: 5px; background-position: -1px 2px; }}
热力图
新建 hugo/themes/hugo-theme-stack/layouts/shortcodes/heatmap.html
<div id="heatmap" style=" max-width: 1900px; height: 180px; padding: 2px; text-align: center; "></div><script src="https://cdn.jsdelivr.net/npm/echarts@5.3.0/dist/echarts.min.js"></script><script type="text/javascript"> var chartDom = document.getElementById('heatmap'); var myChart = echarts.init(chartDom); window.onresize = function() { myChart.resize(); }; var option;
// echart heatmap data seems to only support two elements tuple // it doesn't render when each item has 3 value // it also only pass first 2 elements when reading event param // so here we build a map to store additional metadata like link and title // map format {date: [{wordcount, link, title}]} // for more information see https://blog.douchi.space/hugo-blog-heatmap var dataMap = new Map(); {{ range ((where .Site.RegularPages "Type" "post")) }} var key = {{ .Date.Format "2006-01-02" }}; var value = dataMap.get(key); var wordCount = {{ .WordCount }}; var link = {{ .RelPermalink}}; var title = {{ .Title }};
// multiple posts in same day if (value == null) { dataMap.set(key, [{wordCount, link, title}]); } else { value.push({wordCount, link, title}); } {{- end -}}
var data = []; // sum up the word count for (const [key, value] of dataMap.entries()) { var sum = 0; for (const v of value) { sum += v.wordCount; } data.push([key, (sum / 1000).toFixed(1)]); }
var startDate = new Date(); var year_Mill = startDate.setFullYear((startDate.getFullYear() - 1)); var startDate = +new Date(year_Mill); var endDate = +new Date();
var dayTime = 3600 * 24 * 1000; startDate = echarts.format.formatTime('yyyy-MM-dd', startDate); endDate = echarts.format.formatTime('yyyy-MM-dd', endDate);
// change date range according to months we want to render function heatmap_width(months){ var startDate = new Date(); var mill = startDate.setMonth((startDate.getMonth() - months)); var endDate = +new Date(); startDate = +new Date(mill);
endDate = echarts.format.formatTime('yyyy-MM-dd', endDate); startDate = echarts.format.formatTime('yyyy-MM-dd', startDate);
var showmonth = []; showmonth.push([ startDate, endDate ]); return showmonth };
function getRangeArr() { const windowWidth = window.innerWidth; if (windowWidth >= 600) { return heatmap_width(12); } else if (windowWidth >= 400) { return heatmap_width(9); } else { return heatmap_width(6); } }
option = { title: { top: 0, left: 'center', text: '热力图' }, tooltip: { hideDelay: 1000, enterable: true, formatter: function (p) { const date = p.data[0]; const posts = dataMap.get(date); var content = `${date}`; for (const [i, post] of posts.entries()) { content += "<br>"; var link = post.link; var title = post.title; var wordCount = (post.wordCount / 1000).toFixed(1); content += `<a href="${link}" target="_blank">${title} | ${wordCount} k</a>` } return content; } }, visualMap: { min: 0, max: 10, type: 'piecewise', orient: 'horizontal', left: 'center', top: 30,
inRange: { // [floor color, ceiling color] color: ['#7aa8744c', '#7AA874' ] }, splitNumber: 4, text: ['千字', ''], showLabel: true, itemGap: 20, }, calendar: { top: 80, left: 20, right: 4, cellSize: ['auto', 13], range: getRangeArr(), itemStyle: { color: '#F1F1F1', borderWidth: 1.5, borderColor: '#fff', }, yearLabel: { show: false }, // the splitline between months. set to transparent for now. splitLine: { lineStyle: { color: 'rgba(0, 0, 0, 0.0)', // shadowColor: 'rgba(0, 0, 0, 0.5)', // shadowBlur: 5, // width: 0.5, // type: 'dashed', } } }, series: { type: 'heatmap', coordinateSystem: 'calendar', data: data, } }; myChart.setOption(option); myChart.on('click', function(params) { if (params.componentType === 'series') { // open the first post on the day const post = dataMap.get(params.data[0])[0]; const link = window.location.origin + post.link; window.open(link, '_blank').focus(); }});</script>
文章里输入(将\
删除):
{\{< heatmap >}\}
汉语和英语之间自动添加空格
在主题目录中的 layouts/partials/footer/footer.html
中写入以下内容
<script> (function(u, c) { var d = document, t = 'script', o = d.createElement(t), s = d.getElementsByTagName(t)[0]; o.src = u; if (c) { o.addEventListener('load', function(e) { c(e); }); } s.parentNode.insertBefore(o, s); })('//cdn.bootcss.com/pangu/4.0.7/pangu.min.js', function() { pangu.spacingPage(); });</script>