Hugo Stack主题代码块美化

为了让代码块更美观加上整理这篇文又熬夜了两天

本文基于Hugo版本0.150.0,Stack版本3.31.0,部分代码使用AI完成

预览图

12

代码字体修改

使用了Fira Code字体(链接

<style>
    @font-face {
        font-family: 'mkty';
        src: url({{ (resources.Get "font/mkty.ttf").Permalink }}) format('truetype');
    }

    @font-face {
        font-family: 'Fira Code';
        src: 
            url('/font/fira-code/FiraCode-Light.woff2') format('woff2'),
            url('/font/fira-code/FiraCode-Light.woff') format('woff');
    }

    :root {
        --base-font-family: 'mkty';
        --code-font-family: 'Fira Code', 'mkty';
    }
</style>

其中mkty是我的主字体,我希望代码块里面的中文字体保持这个字体,于是:root{--code-font-family: 'Fira Code', 'mkty';}
Fira Code这个字体包下载完了有很多字体文件,网页用.woff.woff2就够了,根据你喜欢的字重挑选相应的文件放到项目根目录\assets\font\下面即可

同时修改了样式:

// 代码块字体样式
code,
pre,
.highlight {
    font-weight: 300 !important;
    // font-size: 1.6rem;
}
//行内代码
.article-content code {
    border: none;
    border-radius: 4px ;
    font-size: 0.85em ;
}

代码块折叠展开

参考莱特雷的博客
我用了stack主题目录里自带的assets\icons\top.svg,复制了一张改了方向作为图标
主要在原代码基础上添加了折叠功能并修改了样式,java部分deepseek写的,目前电脑和平板上运行没发现问题,不保证不出问题

<style>
    .highlight {
        /* 你可以根据需要调整这个高度 */
        max-height: 400px;
        overflow: hidden;
    }

    .code-show {
        max-height: none !important;
    }

    .code-more-box {
        width: 100%;
        padding-top: 78px;
        background-image: -webkit-gradient(linear, left top, left bottom, from(rgba(255, 255, 255, 0)), to(var(--card-background)));
        position: absolute;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 1;
    }

    .code-more-btn, .code-less-btn {
        display: block;
        margin: auto;
        width: 100%;
        height: 78px;
        background: none;
    }

    .code-less-btn {
        position: absolute;
        bottom: 0px;
        left: 0px;
    }

    .code-more-img, .code-less-img{
        display: block;
        margin: auto;
        padding-right: 1rem;
        width: 60px;
        position: absolute;
        right: 2.5rem;
        bottom: 30px;
    }

    .code-less-img {
        bottom: 25px !important;
    }
</style>

<!-- deepseek:代码折叠展开 -->
<script>
  function initCodeMoreBox() {
    // 新增代码开始
    // 检测是否为移动端
    const isMobile = /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);

    if (isMobile) {
        // 移动端直接移除所有高度限制
        document.querySelectorAll('.highlight').forEach(block => {
            block.style.maxHeight = 'none';
            block.style.overflow = 'auto';
        });
        return;
    }
    // 新增代码结束

    let codeBlocks = document.querySelectorAll(".highlight");
    if (!codeBlocks || codeBlocks.length === 0) {
        return;
    }
    
    codeBlocks.forEach(codeBlock => {
        // 校验是否overflow
        if (codeBlock.scrollHeight <= codeBlock.clientHeight) {
            return;
        }
      
        // 保存初始位置
        const originalPosition = codeBlock.getBoundingClientRect().top + window.pageYOffset;
      
        // 创建 code-more-box
        let codeMoreBox = document.createElement('div');
        codeMoreBox.classList.add('code-more-box');
        
        // 创建 code-more-btn
        let codeMoreBtn = document.createElement('span');
        codeMoreBtn.classList.add('code-more-btn');
      
        // 创建 code-more-img
        let moreImg = document.createElement('img');
        moreImg.classList.add('code-more-img');
        moreImg.src = {{ (resources.Get "icons/bottom.svg").Permalink }};
        
        // 组装更多按钮
        codeMoreBtn.appendChild(moreImg);
        codeMoreBox.appendChild(codeMoreBtn);
        codeBlock.appendChild(codeMoreBox);
      
        // 点击更多按钮事件
        codeMoreBox.addEventListener('click', function() {
            // 隐藏更多按钮
            codeMoreBox.style.display = 'none';
            
            // 展开代码块
            codeBlock.classList.add('code-show');
            
            // 创建折叠按钮
            let codeLessBox = document.createElement('div');
            codeLessBox.classList.add('code-less-box');
            
            let codeLessBtn = document.createElement('span');
            codeLessBtn.classList.add('code-less-btn');
            
            let lessImg = document.createElement('img');
            lessImg.classList.add('code-less-img');
            lessImg.src = {{ (resources.Get "icons/top-pre.svg").Permalink }};
            
            // 组装折叠按钮
            codeLessBtn.appendChild(lessImg);
            codeLessBox.appendChild(codeLessBtn);
            codeBlock.appendChild(codeLessBox);
            
            // 点击折叠按钮事件
            codeLessBox.addEventListener('click', function() {
                // 移除折叠按钮
                codeLessBox.remove();
                
                // 隐藏展开状态
                codeBlock.classList.remove('code-show');
                
                // 显示更多按钮
                codeMoreBox.style.display = 'block';
                
                // 只在初始位置不在视口内时才滚动
                scrollToInitialPositionIfNeeded(originalPosition, codeBlock);
                
                // 触发resize事件
                window.dispatchEvent(new Event('resize'));
            });
            
            // 触发resize事件
            window.dispatchEvent(new Event('resize'));
        });
    });
}

    function scrollToInitialPositionIfNeeded(originalPosition, codeBlock) {
        // 获取当前视口信息
        const viewportHeight = window.innerHeight;
        const currentScroll = window.pageYOffset;
        
        // 计算初始位置是否在视口内
        const isOriginalPositionInViewport = 
        originalPosition >= currentScroll && 
        originalPosition <= currentScroll + viewportHeight;
        
        // 计算代码块当前是否在视口内
        const codeBlockRect = codeBlock.getBoundingClientRect();
        const isCodeBlockInViewport = 
        codeBlockRect.top >= 0 && 
        codeBlockRect.top <= viewportHeight;
        
        // 只有当初始位置不在视口内,且代码块也不在视口内时才滚动
        if (!isOriginalPositionInViewport && !isCodeBlockInViewport) {
            const header = document.querySelector('header');
            let headerOffset = 0;
      
        if (header) {
            const headerHeight = window.getComputedStyle(header).getPropertyValue('height');
            headerOffset = parseInt(headerHeight.replace('px', '')) || 0;
        }
      
        // 计算最终滚动位置(考虑header高度和一点边距)
        const targetPosition = originalPosition - headerOffset - 20;
      
        window.scrollTo({
            top: targetPosition,
            behavior: 'smooth'
        });
      
        console.log('触发滚动:初始位置不在视口内');
        } else {
        console.log('不触发滚动:初始位置或代码块在视口内');
        }
    }
  
    // 初始化
    document.addEventListener('DOMContentLoaded', function() {
        initCodeMoreBox();
    });
</script>

用上述代码手机端会有不显示折叠展开功能相关按钮和样式的问题,用了比较极端的方法解决,即限制手机端禁用这个功能,改用限制代码块高度+滑动查看

/* 移动端禁用折叠,启用滚动并限制高度 */
@media (max-width: 768px) {
    .highlight {
        overflow: auto !important;
        pre {
            max-height: 17em !important;
            &::-webkit-scrollbar {
                display: flex;
            }
        }
    }

    .code-more-box,
    .code-less-box {
        display: none !important;
    }
}

此外如果自定义了鼠标光标样式,也要记得改,
我自定义鼠标光标样式的代码抄的莱特雷
由于添加的img属于.article-content这个类,要记得排除

// 【Stack主题鼠标样式写法】
// default光标图片
body,
html,
.article-content img:not(.code-less-img):not(.code-more-img),  // 这里
.waline-container,
.wl-header label {
    cursor: url(../mouse/default.png),
        auto !important;
}

// pointer光标图片
a:hover,
button:hover,
.copyCodeButton:hover,
#dark-mode-toggle,
.wl-actions label,
.wl-actions a,
.wl-emoji-popup .wl-tab-wrapper button,
.wl-gif-popup img,
.wl-sort li,
.code-more-img,  // 这里
.code-less-img { // 这里
    cursor: url(../mouse/pointer.png),
        auto !important;
}

代码块显示标题

参考loyayz:Hugo 代码块显示标题
用ai改了一下,少创建一个类,自己写了css

{{- $lang := .Type | default "text" }}
{{- $title := .Attributes.title | default "" -}}

{{- if $title }}
<div class="highlight-with-title" data-title="{{ $title }}">
    {{- end }}

    {{ highlight .Inner $lang .Options }}

    {{- if $title }}
</div>
{{- end }}
// 代码块标题样式
.highlight-with-title {
    position: relative;
    margin: 1rem 0;
    border-radius: 20px;
    overflow: hidden;
}
.highlight-with-title .highlight {
    padding-top: 4rem;
}
.highlight-with-title .copyCodeButton {
    top: calc(var(--card-padding) + 1.5rem);
}
.highlight-with-title::before {
    content: attr(data-title);
    position: absolute;
    left: 0;
    right: 0;
    color: var(--accent-color-text);
    opacity: 0.5;
    padding: 0 calc(2.5rem + 3em);
    background-color: var(--accent-color);
    font-size: 0.7em;
    font-family: 'Fira code', 'mkty';
    z-index: 1;
}
```语法名 {title="标题内容"}
# code here

其他样式更改

参照莱特雷的样式改了代码块圆角

// 代码块基础样式修改
.highlight {
    max-width: 100% !important;
    border-radius: 20px;
    margin: 0 !important;
    &:hover {
        box-shadow: var(--shadow-l1) !important;
    }
}

禁用了滚动条

.article-content {
    .highlight {
        pre {
            scrollbar-width: none;
            /* Firefox */
            &::-webkit-scrollbar {
                display: none;
                /* Chrome Safari */
            }
        }
    }
}

复制按钮圆角

// 代码块复制按钮
.article-content .copyCodeButton {
    border-radius: 4px;
}

一些代码块相关注意事项

行号不跟代码一起滚动的问题

做代码块折叠的话用不到,但如果希望限制代码块高度滚动查看代码的话,要想让行号跟着代码块一起滚动,记得改项目根目录\hugo.yaml(也可能是config.yaml 我记不清- -

markup:
    highlight:
        noClasses: false
        codeFences: true
        guessSyntax: true
        lineNoStart: 1
        lineNos: true
        lineNumbersInTable: false
        tabWidth: 4

代码缩进长度不一致的问题

写博客的时候如果发现代码块里面缩进长度不一致的问题,可能是用了制表符Tab缩进导致的,解决办法:

  1. 在VS Code里把缩进改成使用空格缩进,把默认tab长度改成和yaml文件里面写的tabWidth一致,我写的是4所以VS Code里我也改成4。这样改了之后按tab直接等于4个空格
VS Code里按 Ctrl+, 打开设置
搜索 "editor.insert spaces"
勾选 "Editor: Insert Spaces"
搜索 "editor.detect indentation"
勾选 "Editor: Detect Indentation"(自动检测)
或者设置 "Editor: Tab Size" 为 4
搜索 "editor.format on paste"
勾选 "Editor: Format On Paste"
  1. 有时候遇到从别的文件粘贴过来的代码,粘贴到.md文件里还保留了制表符,需要全选并按Ctrl + KCtrl + F,可以把里面的制表符格式化成4个空格
  2. 注意你的.md文件里面所有缩进都得是4个空格(或者是你设置的其他数字)才能保持缩进长度一致,如果还是发现有不一致的问题就手动改一下

参考资料

  1. 莱特雷:【Hugo】Stack主题自定义修改
  2. loyayz:Hugo 代码块显示标题
本博客已稳定运行
发表了12篇文章 · 总计71.90k字
使用 Hugo 构建
主题 StackJimmy 设计